diff options
author | Wei Chen <wechen@nvidia.com> | 2012-10-31 12:55:01 -0700 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-11-08 07:19:04 -0800 |
commit | f8b5d012af3f4da4150b4ac2de4fb7382a1cf6f9 (patch) | |
tree | 3dc15af74de51e710a57a57aab86c35ce4d1caea /drivers | |
parent | f266d25b766a90c10770857f2404cc71fd4c3d7a (diff) |
media: video: tegra: add imx091 nvc driver
add NVC framework driver for imx091
Bug 961418
Change-Id: I2a6c984eac956f62fefb36119d3868aadb800f26
Signed-off-by: Wei Chen <wechen@nvidia.com>
Reviewed-on: http://git-master/r/159376
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Frank Chen <frankc@nvidia.com>
Reviewed-by: Dan Willemsen <dwillemsen@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/tegra/imx091.c | 2716 |
1 files changed, 1853 insertions, 863 deletions
diff --git a/drivers/media/video/tegra/imx091.c b/drivers/media/video/tegra/imx091.c index 1aa353f0d2f1..304ade92723a 100644 --- a/drivers/media/video/tegra/imx091.c +++ b/drivers/media/video/tegra/imx091.c @@ -1,49 +1,183 @@ /* - * imx091.c - imx091 sensor driver - * - * * Copyright (c) 2012 NVIDIA Corporation. All rights reserved. - * - * Contributors: - * Krupal Divvela <kdivvela@nvidia.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +* imx091.c - imx091 sensor driver +* +* Copyright (c) 2012, NVIDIA, All Rights Reserved. +* +* This file is licensed under the terms of the GNU General Public License +* version 2. This program is licensed "as is" without any warranty of any +* kind, whether express or implied. +*/ -#include <linux/delay.h> #include <linux/fs.h> #include <linux/i2c.h> #include <linux/miscdevice.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/uaccess.h> -#include <linux/regulator/consumer.h> -#include <media/imx091.h> +#include <linux/atomic.h> #include <linux/gpio.h> +#include <linux/regulator/consumer.h> #include <linux/module.h> +#include <linux/list.h> +#include <media/imx091.h> + +#define IMX091_ID 0x0091 +#define IMX091_ID_ADDRESS 0x0000 +#define IMX091_STREAM_CONTROL_REG 0x0100 +#define IMX091_STREAM_ENABLE 0x01 +#define IMX091_STREAM_DISABLE 0x00 +#define IMX091_SENSOR_TYPE NVC_IMAGER_TYPE_RAW +#define IMX091_STARTUP_DELAY_MS 50 +#define IMX091_RES_CHG_WAIT_TIME_MS 100 +#define IMX091_SIZEOF_I2C_BUF 16 +#define IMX091_TABLE_WAIT_MS 0 +#define IMX091_TABLE_END 1 +#define IMX091_NUM_MODES ARRAY_SIZE(imx091_mode_table) +#define IMX091_MODE_UNKNOWN (IMX091_NUM_MODES + 1) +#define IMX091_LENS_MAX_APERTURE 0 /* / _INT2FLOAT_DIVISOR */ +#define IMX091_LENS_FNUMBER 0 /* / _INT2FLOAT_DIVISOR */ +#define IMX091_LENS_FOCAL_LENGTH 4760 /* / _INT2FLOAT_DIVISOR */ +#define IMX091_LENS_VIEW_ANGLE_H 60400 /* / _INT2FLOAT_DIVISOR */ +#define IMX091_LENS_VIEW_ANGLE_V 60400 /* / _INT2FLOAT_DIVISOR */ +#define IMX091_WAIT_MS 3 +#define IMX091_I2C_TABLE_MAX_ENTRIES 400 + +static u16 imx091_ids[] = { + 0x0091, +}; + +static struct nvc_gpio_init imx091_gpios[] = { + {IMX091_GPIO_RESET, GPIOF_OUT_INIT_LOW, "reset", false, true}, + {IMX091_GPIO_PWDN, GPIOF_OUT_INIT_LOW, "pwdn", false, true}, + {IMX091_GPIO_GP1, 0, "gp1", false, false}, +}; + +static struct nvc_regulator_init imx091_vregs[] = { + { IMX091_VREG_DVDD, "vdig", }, + { IMX091_VREG_AVDD, "vana", }, + { IMX091_VREG_IOVDD, "vif", }, +}; + +struct imx091_info { + atomic_t in_use; + struct i2c_client *i2c_client; + struct imx091_platform_data *pdata; + struct nvc_imager_cap *cap; + struct miscdevice miscdev; + struct list_head list; + struct nvc_gpio gpio[ARRAY_SIZE(imx091_gpios)]; + struct nvc_regulator vreg[ARRAY_SIZE(imx091_vregs)]; + int pwr_api; + int pwr_dev; + u8 s_mode; + struct imx091_info *s_info; + u32 mode_index; + bool mode_valid; + bool mode_enable; + bool reset_flag; + unsigned test_pattern; + struct nvc_imager_static_nvc sdata; + u8 i2c_buf[IMX091_SIZEOF_I2C_BUF]; + u8 bin_en; +}; struct imx091_reg { u16 addr; u16 val; }; -struct imx091_info { - struct miscdevice miscdev_info; - int mode; - struct imx091_power_rail power; - struct imx091_sensordata sensor_data; - struct i2c_client *i2c_client; - struct imx091_platform_data *pdata; - atomic_t in_use; +struct imx091_mode_data { + struct nvc_imager_mode sensor_mode; + struct nvc_imager_dynamic_nvc sensor_dnvc; + struct imx091_reg *p_mode_i2c; +}; + +static struct nvc_imager_cap imx091_dflt_cap = { + .identifier = "IMX091", + .sensor_nvc_interface = 3, + .pixel_types[0] = 0x100, + .orientation = 0, + .direction = 0, + .initial_clock_rate_khz = 6000, + .clock_profiles[0] = { + .external_clock_khz = 24000, + .clock_multiplier = 10416667, /* value / 1,000,000 */ + }, + .clock_profiles[1] = { + .external_clock_khz = 0, + .clock_multiplier = 0, + }, + .h_sync_edge = 0, + .v_sync_edge = 0, + .mclk_on_vgp0 = 0, + .csi_port = 0, + .data_lanes = 4, + .virtual_channel_id = 0, + .discontinuous_clk_mode = 1, + .cil_threshold_settle = 0x0, + .min_blank_time_width = 16, + .min_blank_time_height = 16, + .preferred_mode_index = 1, + .focuser_guid = NVC_FOCUS_GUID(0), + .torch_guid = NVC_TORCH_GUID(0), + .cap_end = NVC_IMAGER_CAPABILITIES_END, }; -#define IMX091_TABLE_WAIT_MS 0 -#define IMX091_TABLE_END 1 +static struct imx091_platform_data imx091_dflt_pdata = { + .cfg = 0, + .num = 0, + .sync = 0, + .dev_name = "camera", + .cap = &imx091_dflt_cap, +}; -#define IMX091_WAIT_MS 3 + /* NOTE: static vs dynamic + * If a member in the nvc_imager_static_nvc structure is not actually + * static data, then leave blank and add the parameter to the parameter + * read function that dynamically reads the data. The NVC user driver + * will call the parameter read for the data if the member data is 0. + * If the dynamic data becomes static during probe (a one time read + * such as device ID) then add the dynamic read to the _sdata_init + * function. + */ +static struct nvc_imager_static_nvc imx091_dflt_sdata = { + .api_version = NVC_IMAGER_API_STATIC_VER, + .sensor_type = IMX091_SENSOR_TYPE, + .bits_per_pixel = 10, + .sensor_id = IMX091_ID, + .sensor_id_minor = 0, + .focal_len = IMX091_LENS_FOCAL_LENGTH, + .max_aperture = IMX091_LENS_MAX_APERTURE, + .fnumber = IMX091_LENS_FNUMBER, + .view_angle_h = IMX091_LENS_VIEW_ANGLE_H, + .view_angle_v = IMX091_LENS_VIEW_ANGLE_V, + .res_chg_wait_time = IMX091_RES_CHG_WAIT_TIME_MS, +}; -static struct imx091_reg mode_4208x3120[] = { - /* Software reset */ +static LIST_HEAD(imx091_info_list); +static DEFINE_SPINLOCK(imx091_spinlock); + + +static struct imx091_reg tp_none_seq[] = { + {IMX091_TABLE_END, 0x0000} +}; + +static struct imx091_reg tp_cbars_seq[] = { + {IMX091_TABLE_END, 0x0000} +}; + +static struct imx091_reg tp_checker_seq[] = { + {IMX091_TABLE_END, 0x0000} +}; + +static struct imx091_reg *test_patterns[] = { + tp_none_seq, + tp_cbars_seq, + tp_checker_seq, +}; + +static struct imx091_reg imx091_4208x3120_i2c[] = { + /* Reset */ {0x0103, 0x01}, {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, @@ -145,125 +279,11 @@ static struct imx091_reg mode_4208x3120[] = { {0x0203, 0xB8}, {0x0205, 0x00}, - /* stream on */ - {0x0100, 0x01}, - - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, {IMX091_TABLE_END, 0x00} }; -static struct imx091_reg mode_2104x1560[] = { - /* Software reset */ - {0x0103, 0x01}, - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, - - /* global settings */ - {0x3087, 0x53}, - {0x309D, 0x94}, - {0x30A1, 0x08}, - {0x30C7, 0x00}, - {0x3115, 0x0E}, - {0x3118, 0x42}, - {0x311D, 0x34}, - {0x3121, 0x0D}, - {0x3212, 0xF2}, - {0x3213, 0x0F}, - {0x3215, 0x0F}, - {0x3217, 0x0B}, - {0x3219, 0x0B}, - {0x321B, 0x0D}, - {0x321D, 0x0D}, - - /* black level setting */ - {0x3032, 0x40}, - - /* PLL */ - {0x0305, 0x02}, - {0x0307, 0x2F}, - {0x30A4, 0x02}, - {0x303C, 0x4B}, - - /* Mode Settings */ - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x0340, 0x06}, - {0x0341, 0x58}, - {0x0342, 0x12}, - {0x0343, 0x0C}, - {0x0344, 0x00}, - {0x0345, 0x08}, - {0x0346, 0x00}, - {0x0347, 0x30}, - {0x0348, 0x10}, - {0x0349, 0x77}, - {0x034A, 0x0C}, - {0x034B, 0x5F}, - {0x034C, 0x08}, - {0x034D, 0x38}, - {0x034E, 0x06}, - {0x034F, 0x18}, - {0x0381, 0x01}, - {0x0383, 0x03}, - {0x0385, 0x01}, - {0x0387, 0x03}, - {0x3033, 0x00}, - {0x303D, 0x10}, - {0x303E, 0xD0}, - {0x3040, 0x08}, - {0x3041, 0x97}, - {0x3048, 0x01}, - {0x304C, 0x7F}, - {0x304D, 0x04}, - {0x3064, 0x12}, - {0x309B, 0x28}, - {0x309E, 0x00}, - {0x30D5, 0x09}, - {0x30D6, 0x01}, - {0x30D7, 0x01}, - {0x30D8, 0x64}, - {0x30D9, 0x89}, - {0x30DE, 0x02}, - {0x3102, 0x10}, - {0x3103, 0x44}, - {0x3104, 0x40}, - {0x3105, 0x00}, - {0x3106, 0x0D}, - {0x3107, 0x01}, - {0x310A, 0x0A}, - {0x315C, 0x99}, - {0x315D, 0x98}, - {0x316E, 0x9A}, - {0x316F, 0x99}, - {0x3301, 0x03}, - {0x3304, 0x05}, - {0x3305, 0x04}, - {0x3306, 0x12}, - {0x3307, 0x03}, - {0x3308, 0x0D}, - {0x3309, 0x05}, - {0x330A, 0x09}, - {0x330B, 0x04}, - {0x330C, 0x08}, - {0x330D, 0x05}, - {0x330E, 0x03}, - {0x3318, 0x73}, - {0x3322, 0x02}, - {0x3342, 0x0F}, - {0x3348, 0xE0}, - - {0x0202, 0x06}, - {0x0203, 0x00}, - {0x0205, 0x00}, - - /* stream on */ - {0x0100, 0x01}, - - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, - {IMX091_TABLE_END, 0x00} -}; - -static struct imx091_reg mode_1052x778[] = { - /* Software reset */ +static struct imx091_reg imx091_1948x1096_i2c[] = { + /* Reset */ {0x0103, 0x01}, {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, @@ -301,20 +321,20 @@ static struct imx091_reg mode_1052x778[] = { {0x0342, 0x09}, {0x0343, 0x06}, {0x0344, 0x00}, - {0x0345, 0x08}, - {0x0346, 0x00}, - {0x0347, 0x34}, - {0x0348, 0x10}, - {0x0349, 0x77}, - {0x034A, 0x0C}, - {0x034B, 0x5B}, - {0x034C, 0x04}, - {0x034D, 0x1C}, - {0x034E, 0x03}, - {0x034F, 0x0A}, + {0x0345, 0xA4}, + {0x0346, 0x02}, + {0x0347, 0x00}, + {0x0348, 0x0F}, + {0x0349, 0xDB}, + {0x034A, 0x0A}, + {0x034B, 0x8F}, + {0x034C, 0x07}, + {0x034D, 0x9C}, + {0x034E, 0x04}, + {0x034F, 0x48}, {0x0381, 0x01}, - {0x0383, 0x03}, - {0x0385, 0x05}, + {0x0383, 0x01}, + {0x0385, 0x01}, {0x0387, 0x03}, {0x3033, 0x84}, {0x303D, 0x10}, @@ -356,25 +376,20 @@ static struct imx091_reg mode_1052x778[] = { {0x330C, 0x08}, {0x330D, 0x05}, {0x330E, 0x03}, - {0x3318, 0x75}, + {0x3318, 0x67}, {0x3322, 0x02}, {0x3342, 0x0F}, {0x3348, 0xE0}, - {0x0202, 0x06}, + {0x0202, 0x00}, {0x0203, 0x00}, {0x0205, 0x00}, - /* stream on */ - {0x0100, 0x01}, - - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, {IMX091_TABLE_END, 0x00} }; - -static struct imx091_reg mode_524x390[] = { - /* Software reset */ +static struct imx091_reg imx091_1308x736_i2c[] = { + /* Reset */ {0x0103, 0x01}, {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, @@ -400,160 +415,50 @@ static struct imx091_reg mode_524x390[] = { /* PLL */ {0x0305, 0x02}, - {0x0307, 0x2F}, + {0x0307, 0x20}, {0x30A4, 0x02}, {0x303C, 0x4B}, /* Mode Settings */ {0x0112, 0x0A}, {0x0113, 0x0A}, - {0x0340, 0x01}, - {0x0341, 0x96}, + {0x0340, 0x04}, + {0x0341, 0x4E}, {0x0342, 0x12}, {0x0343, 0x0C}, {0x0344, 0x00}, - {0x0345, 0x10}, - {0x0346, 0x00}, - {0x0347, 0x30}, - {0x0348, 0x10}, - {0x0349, 0x6F}, - {0x034A, 0x0C}, - {0x034B, 0x5F}, - {0x034C, 0x02}, - {0x034D, 0x0C}, - {0x034E, 0x01}, - {0x034F, 0x86}, - {0x0381, 0x09}, - {0x0383, 0x07}, - {0x0385, 0x09}, - {0x0387, 0x07}, + {0x0345, 0x96}, + {0x0346, 0x01}, + {0x0347, 0xF8}, + {0x0348, 0x0F}, + {0x0349, 0xE9}, + {0x034A, 0x0A}, + {0x034B, 0x97}, + {0x034C, 0x05}, + {0x034D, 0x1C}, + {0x034E, 0x02}, + {0x034F, 0xE0}, + {0x0381, 0x03}, + {0x0383, 0x03}, + {0x0385, 0x03}, + {0x0387, 0x03}, {0x3033, 0x00}, {0x303D, 0x10}, {0x303E, 0xD0}, {0x3040, 0x08}, {0x3041, 0x97}, - {0x3048, 0x01}, + {0x3048, 0x22}, {0x304C, 0x7F}, {0x304D, 0x04}, {0x3064, 0x12}, - {0x309B, 0x28}, - {0x309E, 0x00}, - {0x30D5, 0x09}, - {0x30D6, 0x00}, - {0x30D7, 0x00}, - {0x30D8, 0x00}, - {0x30D9, 0x00}, - {0x30DE, 0x08}, - {0x3102, 0x10}, - {0x3103, 0x44}, - {0x3104, 0x40}, - {0x3105, 0x00}, - {0x3106, 0x0D}, - {0x3107, 0x01}, - {0x310A, 0x0A}, - {0x315C, 0x99}, - {0x315D, 0x98}, - {0x316E, 0x9A}, - {0x316F, 0x99}, - {0x3301, 0x03}, - {0x3304, 0x03}, - {0x3305, 0x02}, - {0x3306, 0x09}, - {0x3307, 0x06}, - {0x3308, 0x1E}, - {0x3309, 0x05}, - {0x330A, 0x05}, - {0x330B, 0x04}, - {0x330C, 0x07}, - {0x330D, 0x06}, - {0x330E, 0x01}, - {0x3318, 0x44}, - {0x3322, 0x0E}, - {0x3342, 0x00}, - {0x3348, 0xE0}, - - {0x0202, 0x00}, - {0x0203, 0x00}, - {0x0205, 0x00}, - - /* stream on */ - {0x0100, 0x01}, - - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, - {IMX091_TABLE_END, 0x00} -}; - -static struct imx091_reg mode_348x260[] = { - /* Software reset */ - {0x0103, 0x01}, - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, - - /* global settings */ - {0x3087, 0x53}, - {0x309D, 0x94}, - {0x30A1, 0x08}, - {0x30C7, 0x00}, - {0x3115, 0x0E}, - {0x3118, 0x42}, - {0x311D, 0x34}, - {0x3121, 0x0D}, - {0x3212, 0xF2}, - {0x3213, 0x0F}, - {0x3215, 0x0F}, - {0x3217, 0x0B}, - {0x3219, 0x0B}, - {0x321B, 0x0D}, - {0x321D, 0x0D}, - - /* black level setting */ - {0x3032, 0x40}, - - /* PLL */ - {0x0305, 0x02}, - {0x0307, 0x24}, - {0x30A4, 0x02}, - {0x303C, 0x4B}, - - /* Mode Settings */ - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x0340, 0x01}, - {0x0341, 0x36}, - {0x0342, 0x09}, - {0x0343, 0x06}, - {0x0344, 0x00}, - {0x0345, 0x18}, - {0x0346, 0x00}, - {0x0347, 0x30}, - {0x0348, 0x10}, - {0x0349, 0x67}, - {0x034A, 0x0C}, - {0x034B, 0x5F}, - {0x034C, 0x01}, - {0x034D, 0x5C}, - {0x034E, 0x01}, - {0x034F, 0x04}, - {0x0381, 0x05}, - {0x0383, 0x07}, - {0x0385, 0x0B}, - {0x0387, 0x0D}, - {0x3033, 0x84}, - {0x303D, 0x10}, - {0x303E, 0xD0}, - {0x3040, 0x08}, - {0x3041, 0x97}, - {0x3048, 0x01}, - {0x304C, 0x3F}, - {0x304D, 0x02}, - {0x3064, 0x12}, - {0x309B, 0x48}, + {0x309B, 0x60}, {0x309E, 0x04}, - {0x30D5, 0x0D}, + {0x30D5, 0x09}, {0x30D6, 0x00}, {0x30D7, 0x00}, {0x30D8, 0x00}, - {0x30D9, 0x00}, - {0x30DE, 0x06}, + {0x30D9, 0x89}, + {0x30DE, 0x03}, {0x3102, 0x09}, {0x3103, 0x23}, {0x3104, 0x24}, @@ -566,35 +471,31 @@ static struct imx091_reg mode_348x260[] = { {0x316E, 0x4B}, {0x316F, 0x4A}, {0x3301, 0x03}, - {0x3304, 0x02}, - {0x3305, 0x00}, - {0x3306, 0x06}, - {0x3307, 0x04}, - {0x3308, 0x10}, - {0x3309, 0x02}, - {0x330A, 0x03}, - {0x330B, 0x01}, - {0x330C, 0x05}, - {0x330D, 0x03}, - {0x330E, 0x01}, - {0x3318, 0x44}, - {0x3322, 0x05}, - {0x3342, 0x00}, + {0x3304, 0x05}, + {0x3305, 0x04}, + {0x3306, 0x12}, + {0x3307, 0x03}, + {0x3308, 0x0D}, + {0x3309, 0x05}, + {0x330A, 0x09}, + {0x330B, 0x04}, + {0x330C, 0x08}, + {0x330D, 0x05}, + {0x330E, 0x03}, + {0x3318, 0x6C}, + {0x3322, 0x02}, + {0x3342, 0x0F}, {0x3348, 0xE0}, {0x0202, 0x00}, {0x0203, 0x00}, {0x0205, 0x00}, - /* stream on */ - {0x0100, 0x01}, - - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, {IMX091_TABLE_END, 0x00} }; -static struct imx091_reg mode_1948x1096[] = { - /* Software reset */ +static struct imx091_reg imx091_1052X778_i2c[] = { + /* Reset */ {0x0103, 0x01}, {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, @@ -632,20 +533,20 @@ static struct imx091_reg mode_1948x1096[] = { {0x0342, 0x09}, {0x0343, 0x06}, {0x0344, 0x00}, - {0x0345, 0xA4}, - {0x0346, 0x02}, - {0x0347, 0x00}, - {0x0348, 0x0F}, - {0x0349, 0xDB}, - {0x034A, 0x0A}, - {0x034B, 0x8F}, - {0x034C, 0x07}, - {0x034D, 0x9C}, - {0x034E, 0x04}, - {0x034F, 0x48}, + {0x0345, 0x08}, + {0x0346, 0x00}, + {0x0347, 0x34}, + {0x0348, 0x10}, + {0x0349, 0x77}, + {0x034A, 0x0C}, + {0x034B, 0x5B}, + {0x034C, 0x04}, + {0x034D, 0x1C}, + {0x034E, 0x03}, + {0x034F, 0x0A}, {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, + {0x0383, 0x03}, + {0x0385, 0x05}, {0x0387, 0x03}, {0x3033, 0x84}, {0x303D, 0x10}, @@ -687,651 +588,1704 @@ static struct imx091_reg mode_1948x1096[] = { {0x330C, 0x08}, {0x330D, 0x05}, {0x330E, 0x03}, - {0x3318, 0x67}, + {0x3318, 0x75}, {0x3322, 0x02}, {0x3342, 0x0F}, {0x3348, 0xE0}, - {0x0202, 0x00}, + {0x0202, 0x06}, {0x0203, 0x00}, {0x0205, 0x00}, - /* stream on */ - {0x0100, 0x01}, - - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, {IMX091_TABLE_END, 0x00} }; -static struct imx091_reg mode_1308x736[] = { - /* Software reset */ - {0x0103, 0x01}, - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, +/* Each resolution requires the below data table setup and the corresponding + * I2C data table. + * If more NVC data is needed for the NVC driver, be sure and modify the + * nvc_imager_nvc structure in nvc_imager.h + * If more data sets are needed per resolution, they can be added to the + * table format below with the imx091_mode_data structure. New data sets + * should conform to an already defined NVC structure. If it's data for the + * NVC driver, then it should be added to the nvc_imager_nvc structure. + * Steps to add a resolution: + * 1. Add I2C data table + * 2. Add imx091_mode_data table + * 3. Add entry to the imx091_mode_table + */ +static struct imx091_mode_data imx091_4208x3120 = { + .sensor_mode = { + .res_x = 4096, + .res_y = 3072, + .active_start_x = 0, + .active_stary_y = 0, + .peak_frame_rate = 15000, /* / _INT2FLOAT_DIVISOR */ + .pixel_aspect_ratio = 1000, /* / _INT2FLOAT_DIVISOR */ + .pll_multiplier = 10000, /* / _INT2FLOAT_DIVISOR */ + .crop_mode = NVC_IMAGER_CROPMODE_NONE, + }, + .sensor_dnvc = { + .api_version = NVC_IMAGER_API_DYNAMIC_VER, + .region_start_x = 0, + .region_start_y = 0, + .x_scale = 1, + .y_scale = 1, + .bracket_caps = 1, + .flush_count = 2, + .init_intra_frame_skip = 0, + .ss_intra_frame_skip = 2, + .ss_frame_number = 3, + .coarse_time = 0x0C53, + .max_coarse_diff = 5, + .min_exposure_course = 2, + .max_exposure_course = 0xFFFC, + .diff_integration_time = 110, /* / _INT2FLOAT_DIVISOR */ + .line_length = 0x120C, + .frame_length = 0x0C58, + .min_frame_length = 0x0C58, + .max_frame_length = 0xFFFF, + .min_gain = 1, /* / _INT2FLOAT_DIVISOR */ + .max_gain = 16000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain = 1000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain_bin_en = 1000, /* / _INT2FLOAT_DIVISOR */ + .support_bin_control = 0, + .support_fast_mode = 0, + .pll_mult = 0x2A, + .pll_div = 0x2, + }, + .p_mode_i2c = imx091_4208x3120_i2c, +}; - /* global settings */ - {0x3087, 0x53}, - {0x309D, 0x94}, - {0x30A1, 0x08}, - {0x30C7, 0x00}, - {0x3115, 0x0E}, - {0x3118, 0x42}, - {0x311D, 0x34}, - {0x3121, 0x0D}, - {0x3212, 0xF2}, - {0x3213, 0x0F}, - {0x3215, 0x0F}, - {0x3217, 0x0B}, - {0x3219, 0x0B}, - {0x321B, 0x0D}, - {0x321D, 0x0D}, +static struct imx091_mode_data imx091_1948x1096 = { + .sensor_mode = { + .res_x = 1920, + .res_y = 1080, + .active_start_x = 0, + .active_stary_y = 0, + .peak_frame_rate = 30000, /* / _INT2FLOAT_DIVISOR */ + .pixel_aspect_ratio = 1000, /* / _INT2FLOAT_DIVISOR */ + .pll_multiplier = 7000, /* / _INT2FLOAT_DIVISOR */ + .crop_mode = NVC_IMAGER_CROPMODE_PARTIAL, + }, + .sensor_dnvc = { + .api_version = NVC_IMAGER_API_DYNAMIC_VER, + .region_start_x = 0, + .region_start_y = 0, + .x_scale = 1, + .y_scale = 1, + .bracket_caps = 1, + .flush_count = 2, + .init_intra_frame_skip = 0, + .ss_intra_frame_skip = 2, + .ss_frame_number = 3, + .coarse_time = 0x08A1, + .max_coarse_diff = 5, + .min_exposure_course = 2, + .max_exposure_course = 0xFFFC, + .diff_integration_time = 110, /* / _INT2FLOAT_DIVISOR */ + .line_length = 0x0906, + .frame_length = 0x08A6, + .min_frame_length = 0x08A6, + .max_frame_length = 0xFFFF, + .min_gain = 1, /* / _INT2FLOAT_DIVISOR */ + .max_gain = 16000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain = 1000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain_bin_en = 1000, /* / _INT2FLOAT_DIVISOR */ + .support_bin_control = 0, + .support_fast_mode = 0, + .pll_mult = 0x20, + .pll_div = 0x2, + }, + .p_mode_i2c = imx091_1948x1096_i2c, +}; - /* black level setting */ - {0x3032, 0x40}, +static struct imx091_mode_data imx091_1308x736 = { + .sensor_mode = { + .res_x = 1280, + .res_y = 720, + .active_start_x = 0, + .active_stary_y = 0, + .peak_frame_rate = 30000, /* / _INT2FLOAT_DIVISOR */ + .pixel_aspect_ratio = 1000, /* / _INT2FLOAT_DIVISOR */ + .pll_multiplier = 5000, /* / _INT2FLOAT_DIVISOR */ + .crop_mode = NVC_IMAGER_CROPMODE_PARTIAL, + }, + .sensor_dnvc = { + .api_version = NVC_IMAGER_API_DYNAMIC_VER, + .region_start_x = 0, + .region_start_y = 0, + .x_scale = 1, + .y_scale = 1, + .bracket_caps = 1, + .flush_count = 2, + .init_intra_frame_skip = 0, + .ss_intra_frame_skip = 2, + .ss_frame_number = 3, + .coarse_time = 0x0448, + .max_coarse_diff = 5, + .min_exposure_course = 2, + .max_exposure_course = 0xFFFC, + .diff_integration_time = 110, /* / _INT2FLOAT_DIVISOR */ + .line_length = 0x120C, + .frame_length = 0x044e, + .min_frame_length = 0x044e, + .max_frame_length = 0xFFFF, + .min_gain = 1, /* / _INT2FLOAT_DIVISOR */ + .max_gain = 16000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain = 1000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain_bin_en = 1000, /* / _INT2FLOAT_DIVISOR */ + .support_bin_control = 0, + .support_fast_mode = 0, + .pll_mult = 0x20, + .pll_div = 0x2, + }, + .p_mode_i2c = imx091_1308x736_i2c, +}; - /* PLL */ - {0x0305, 0x02}, - {0x0307, 0x20}, - {0x30A4, 0x02}, - {0x303C, 0x4B}, +static struct imx091_mode_data imx091_1052x778 = { + .sensor_mode = { + .res_x = 1024, + .res_y = 768, + .active_start_x = 0, + .active_stary_y = 0, + .peak_frame_rate = 30000, /* / _INT2FLOAT_DIVISOR */ + .pixel_aspect_ratio = 1000, /* / _INT2FLOAT_DIVISOR */ + .pll_multiplier = 5000, /* / _INT2FLOAT_DIVISOR */ + .crop_mode = NVC_IMAGER_CROPMODE_NONE, + }, + .sensor_dnvc = { + .api_version = NVC_IMAGER_API_DYNAMIC_VER, + .region_start_x = 0, + .region_start_y = 0, + .x_scale = 1, + .y_scale = 1, + .bracket_caps = 1, + .flush_count = 2, + .init_intra_frame_skip = 0, + .ss_intra_frame_skip = 2, + .ss_frame_number = 3, + .coarse_time = 0x08A1, + .max_coarse_diff = 5, + .min_exposure_course = 2, + .max_exposure_course = 0xFFFC, + .diff_integration_time = 110, /* / _INT2FLOAT_DIVISOR */ + .line_length = 0x0906, + .frame_length = 0x08A6, + .min_frame_length = 0x08A6, + .max_frame_length = 0xFFFF, + .min_gain = 1, /* / _INT2FLOAT_DIVISOR */ + .max_gain = 16000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain = 1000, /* / _INT2FLOAT_DIVISOR */ + .inherent_gain_bin_en = 1000, /* / _INT2FLOAT_DIVISOR */ + .support_bin_control = 0, + .support_fast_mode = 0, + .pll_mult = 0x20, + .pll_div = 0x2, + }, + .p_mode_i2c = imx091_1052X778_i2c, +}; - /* Mode Settings */ - {0x0112, 0x0A}, - {0x0113, 0x0A}, - {0x0340, 0x04}, - {0x0341, 0x4E}, - {0x0342, 0x12}, - {0x0343, 0x0C}, - {0x0344, 0x00}, - {0x0345, 0x96}, - {0x0346, 0x01}, - {0x0347, 0xF8}, - {0x0348, 0x0F}, - {0x0349, 0xE9}, - {0x034A, 0x0A}, - {0x034B, 0x97}, - {0x034C, 0x05}, - {0x034D, 0x1C}, - {0x034E, 0x02}, - {0x034F, 0xE0}, - {0x0381, 0x03}, - {0x0383, 0x03}, - {0x0385, 0x03}, - {0x0387, 0x03}, - {0x3033, 0x00}, - {0x303D, 0x10}, - {0x303E, 0xD0}, - {0x3040, 0x08}, - {0x3041, 0x97}, - {0x3048, 0x22}, - {0x304C, 0x7F}, - {0x304D, 0x04}, - {0x3064, 0x12}, - {0x309B, 0x60}, - {0x309E, 0x04}, - {0x30D5, 0x09}, - {0x30D6, 0x00}, - {0x30D7, 0x00}, - {0x30D8, 0x00}, - {0x30D9, 0x89}, - {0x30DE, 0x03}, - {0x3102, 0x09}, - {0x3103, 0x23}, - {0x3104, 0x24}, - {0x3105, 0x00}, - {0x3106, 0x8B}, - {0x3107, 0x00}, - {0x310A, 0x0A}, - {0x315C, 0x4A}, - {0x315D, 0x49}, - {0x316E, 0x4B}, - {0x316F, 0x4A}, - {0x3301, 0x03}, - {0x3304, 0x05}, - {0x3305, 0x04}, - {0x3306, 0x12}, - {0x3307, 0x03}, - {0x3308, 0x0D}, - {0x3309, 0x05}, - {0x330A, 0x09}, - {0x330B, 0x04}, - {0x330C, 0x08}, - {0x330D, 0x05}, - {0x330E, 0x03}, - {0x3318, 0x6C}, - {0x3322, 0x02}, - {0x3342, 0x0F}, - {0x3348, 0xE0}, - {0x0202, 0x00}, - {0x0203, 0x00}, - {0x0205, 0x00}, +static struct imx091_mode_data *imx091_mode_table[] = { + &imx091_4208x3120, + &imx091_1948x1096, + &imx091_1308x736, + &imx091_1052x778, +}; - /* stream on */ - {0x0100, 0x01}, - {IMX091_TABLE_WAIT_MS, IMX091_WAIT_MS}, - {IMX091_TABLE_END, 0x00} -}; +static int imx091_i2c_rd8(struct imx091_info *info, u16 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[3]; -enum { - IMX091_MODE_4208X3120, - IMX091_MODE_2104X1560, - IMX091_MODE_1052X778, - IMX091_MODE_524X390, - IMX091_MODE_348X260, - IMX091_MODE_1948X1096, - IMX091_MODE_1308X736, -}; + buf[0] = (reg >> 8); + buf[1] = (reg & 0x00FF); + msg[0].addr = info->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = &buf[0]; + msg[1].addr = info->i2c_client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &buf[2]; + *val = 0; + if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) + return -EIO; -static struct imx091_reg *mode_table[] = { - [IMX091_MODE_4208X3120] = mode_4208x3120, - [IMX091_MODE_2104X1560] = mode_2104x1560, - [IMX091_MODE_1052X778] = mode_1052x778, - [IMX091_MODE_524X390] = mode_524x390, - [IMX091_MODE_348X260] = mode_348x260, - [IMX091_MODE_1948X1096] = mode_1948x1096, - [IMX091_MODE_1308X736] = mode_1308x736, -}; + *val = buf[2]; + return 0; +} -static inline void -msleep_range(unsigned int delay_base) +static int imx091_i2c_rd16(struct imx091_info *info, u16 reg, u16 *val) { - usleep_range(delay_base*1000, delay_base*1000+500); + struct i2c_msg msg[2]; + u8 buf[4]; + + buf[0] = (reg >> 8); + buf[1] = (reg & 0x00FF); + msg[0].addr = info->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = &buf[0]; + msg[1].addr = info->i2c_client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = &buf[2]; + *val = 0; + if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2) + return -EIO; + + *val = (((u16)buf[2] << 8) | (u16)buf[3]); + return 0; } -static inline void -imx091_get_frame_length_regs(struct imx091_reg *regs, u32 frame_length) +static int imx091_i2c_wr8(struct imx091_info *info, u16 reg, u8 val) { - regs->addr = 0x0340; - regs->val = (frame_length >> 8) & 0xff; - (regs + 1)->addr = 0x0341; - (regs + 1)->val = (frame_length) & 0xff; + struct i2c_msg msg; + u8 buf[3]; + + buf[0] = (reg >> 8); + buf[1] = (reg & 0x00FF); + buf[2] = val; + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + + return 0; } -static inline void -imx091_get_coarse_time_regs(struct imx091_reg *regs, u32 coarse_time) +static int imx091_i2c_wr16(struct imx091_info *info, u16 reg, u16 val) { - regs->addr = 0x202; - regs->val = (coarse_time >> 8) & 0xff; - (regs + 1)->addr = 0x203; - (regs + 1)->val = (coarse_time) & 0xff; + struct i2c_msg msg; + u8 buf[4]; + + buf[0] = (reg >> 8); + buf[1] = (reg & 0x00FF); + buf[2] = (val & 0x00FF); + buf[3] = (val >> 8); + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 4; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + + return 0; } -static inline void -imx091_get_gain_reg(struct imx091_reg *regs, u16 gain) +static int imx091_i2c_rd_table(struct imx091_info *info, + struct imx091_reg table[]) { - regs->addr = 0x205; - regs->val = gain; + struct imx091_reg *p_table = table; + u8 val; + int err = 0; + + while (p_table->addr != IMX091_TABLE_END) { + err = imx091_i2c_rd8(info, p_table->addr, &val); + if (err) + return err; + + p_table->val = (u16)val; + p_table++; + } + + return err; } -static int -imx091_read_reg(struct i2c_client *client, u16 addr, u8 *val) +static int imx091_i2c_wr_blk(struct imx091_info *info, u8 *buf, int len) { - int err; - struct i2c_msg msg[2]; - unsigned char data[3]; + struct i2c_msg msg; - if (!client->adapter) - return -ENODEV; + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = buf; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 2; - msg[0].buf = data; + return 0; +} - /* high byte goes out first */ - data[0] = (u8) (addr >> 8); - data[1] = (u8) (addr & 0xff); +static int imx091_i2c_wr_table(struct imx091_info *info, + struct imx091_reg table[]) +{ + int err; + const struct imx091_reg *next; + const struct imx091_reg *n_next; + u8 *b_ptr = info->i2c_buf; + u16 buf_count = 0; - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 1; - msg[1].buf = data + 2; + for (next = table; next->addr != IMX091_TABLE_END; next++) { + if (next->addr == IMX091_TABLE_WAIT_MS) { + msleep(next->val); + continue; + } - err = i2c_transfer(client->adapter, msg, 2); + if (!buf_count) { + b_ptr = info->i2c_buf; + *b_ptr++ = next->addr >> 8; + *b_ptr++ = next->addr & 0xFF; + buf_count = 2; + } + *b_ptr++ = next->val; + buf_count++; + n_next = next + 1; + if (n_next->addr == next->addr + 1 && + n_next->addr != IMX091_TABLE_WAIT_MS && + buf_count < IMX091_SIZEOF_I2C_BUF && + n_next->addr != IMX091_TABLE_END) + continue; - if (err != 2) - return -EINVAL; + err = imx091_i2c_wr_blk(info, info->i2c_buf, buf_count); + if (err) + return err; + + buf_count = 0; + } - *val = data[2]; return 0; } -static int -imx091_write_reg(struct i2c_client *client, u16 addr, u8 val) + +static inline void imx091_frame_length_reg(struct imx091_reg *regs, + u32 frame_length) { - int err; - struct i2c_msg msg; - unsigned char data[3]; + regs->addr = 0x0340; + regs->val = (frame_length >> 8) & 0xFF; + (regs + 1)->addr = 0x0341; + (regs + 1)->val = (frame_length) & 0xFF; +} - if (!client->adapter) - return -ENODEV; +static inline void imx091_coarse_time_reg(struct imx091_reg *regs, + u32 coarse_time) +{ + regs->addr = 0x0202; + regs->val = (coarse_time >> 8) & 0xFF; + (regs + 1)->addr = 0x0203; + (regs + 1)->val = (coarse_time) & 0xFF; +} - data[0] = (u8) (addr >> 8); - data[1] = (u8) (addr & 0xff); - data[2] = (u8) (val & 0xff); +static inline void imx091_gain_reg(struct imx091_reg *regs, u32 gain) +{ + regs->addr = 0x0205; + regs->val = gain & 0xFF; +} - msg.addr = client->addr; - msg.flags = 0; - msg.len = 3; - msg.buf = data; +static int imx091_bin_wr(struct imx091_info *info, u8 enable) +{ + int err = 0; - err = i2c_transfer(client->adapter, &msg, 1); - if (err == 1) + if (enable == info->bin_en) return 0; - pr_err("%s:i2c write failed, %x = %x\n", - __func__, addr, val); + if (!info->mode_valid || !imx091_mode_table[info->mode_index]-> + sensor_dnvc.support_bin_control) + return -EINVAL; + if (!err) + info->bin_en = enable; + dev_dbg(&info->i2c_client->dev, "%s bin_en=%x err=%d\n", + __func__, info->bin_en, err); return err; } -static int -imx091_write_table(struct i2c_client *client, - const struct imx091_reg table[], - const struct imx091_reg override_list[], - int num_override_regs) +static int imx091_exposure_wr(struct imx091_info *info, + struct nvc_imager_bayer *mode) { + struct imx091_reg reg_list[8]; int err; - const struct imx091_reg *next; - int i; - u16 val; - for (next = table; next->addr != IMX091_TABLE_END; next++) { - if (next->addr == IMX091_TABLE_WAIT_MS) { - msleep_range(next->val); - continue; - } + reg_list[0].addr = 0x0104; + reg_list[0].val = 0x01; + imx091_frame_length_reg(reg_list+1, mode->frame_length); + imx091_coarse_time_reg(reg_list + 3, mode->coarse_time); + imx091_gain_reg(reg_list + 5, mode->gain); + reg_list[6].addr = 0x0104; + reg_list[6].val = 0x00; + reg_list[7].addr = IMX091_TABLE_END; + err = imx091_i2c_wr_table(info, reg_list); + if (!err) + err = imx091_bin_wr(info, mode->bin_en); + return err; +} + +static int imx091_gain_wr(struct imx091_info *info, u32 gain) +{ + int err; - val = next->val; + gain &= 0xFF; + err = imx091_i2c_wr16(info, 0x0205, (u16)gain); + return err; +} - /* When an override list is passed in, replace the reg */ - /* value to write if the reg is in the list */ - if (override_list) { - for (i = 0; i < num_override_regs; i++) { - if (next->addr == override_list[i].addr) { - val = override_list[i].val; - break; - } - } - } +static int imx091_gain_rd(struct imx091_info *info, u32 *gain) +{ + int err; + + *gain = 0; + err = imx091_i2c_rd8(info, 0x0205, (u8 *)gain); + return err; +} + +static int imx091_group_hold_wr(struct imx091_info *info, + struct nvc_imager_ae *ae) +{ + int err; + bool groupHoldEnable; + struct imx091_reg reg_list[6]; + int count = 0; + + groupHoldEnable = ae->gain_enable | + ae->frame_length_enable | + ae->coarse_time_enable; - err = imx091_write_reg(client, next->addr, val); + if (groupHoldEnable) { + err = imx091_i2c_wr8(info, 0x104, 1); if (err) { - pr_err("%s:imx091_write_table:%d", __func__, err); + dev_err(&info->i2c_client->dev, + "Error: %s fail to enable grouphold\n", + __func__); return err; } } - return 0; + + if (ae->gain_enable) { + imx091_gain_reg(reg_list + count, ae->gain); + count += 1; + } + if (ae->coarse_time_enable) { + imx091_coarse_time_reg(reg_list + count, ae->coarse_time); + count += 2; + } + if (ae->frame_length_enable) { + imx091_frame_length_reg(reg_list + count, ae->frame_length); + count += 2; + } + reg_list[count].addr = IMX091_TABLE_END; + err = imx091_i2c_wr_table(info, reg_list); + if (err) { + dev_err(&info->i2c_client->dev, "Error: %s i2c wr_table fail\n", + __func__); + } + + if (groupHoldEnable) { + err = imx091_i2c_wr8(info, 0x104, 0); + if (err) { + dev_err(&info->i2c_client->dev, + "Error: %s fail to release grouphold\n", + __func__); + } + } + return err; } -static int -imx091_set_mode(struct imx091_info *info, struct imx091_mode *mode) +static int imx091_test_pattern_wr(struct imx091_info *info, unsigned pattern) { - int sensor_mode; - int err; - struct imx091_reg reg_list[5]; - - pr_info("%s: xres %u yres %u framelength %u coarsetime %u gain %u\n", - __func__, mode->xres, mode->yres, mode->frame_length, - mode->coarse_time, mode->gain); - - if (mode->xres == 4096 && mode->yres == 3072) - sensor_mode = IMX091_MODE_4208X3120; - else if (mode->xres == 2048 && mode->yres == 1536) - sensor_mode = IMX091_MODE_2104X1560; - else if (mode->xres == 1024 && mode->yres == 768) - sensor_mode = IMX091_MODE_1052X778; - else if (mode->xres == 524 && mode->yres == 374) - sensor_mode = IMX091_MODE_524X390; - else if (mode->xres == 348 && mode->yres == 260) - sensor_mode = IMX091_MODE_348X260; - else if (mode->xres == 1920 && mode->yres == 1080) - sensor_mode = IMX091_MODE_1948X1096; - else if (mode->xres == 1280 && mode->yres == 720) - sensor_mode = IMX091_MODE_1308X736; - else { - pr_err("%s: invalid resolution supplied to set mode %d %d\n", - __func__, mode->xres, mode->yres); + if (pattern >= ARRAY_SIZE(test_patterns)) return -EINVAL; + + return imx091_i2c_wr_table(info, test_patterns[pattern]); +} + +static int imx091_gpio_rd(struct imx091_info *info, + enum imx091_gpio i) +{ + int val = -EINVAL; + + if (info->gpio[i].flag) { + val = gpio_get_value_cansleep(info->gpio[i].gpio); + if (val) + val = 1; + dev_dbg(&info->i2c_client->dev, "%s %u %d\n", + __func__, info->gpio[i].gpio, val); + if (!info->gpio[i].active_high) + val = !val; + val &= 1; } + return val; /* return read value or error */ +} - /* get a list of override regs for the asking frame length, */ - /* coarse integration time, and gain. */ - imx091_get_frame_length_regs(reg_list, mode->frame_length); - imx091_get_coarse_time_regs(reg_list + 2, mode->coarse_time); - imx091_get_gain_reg(reg_list + 4, mode->gain); +static int imx091_gpio_wr(struct imx091_info *info, + enum imx091_gpio i, + int val) /* val: 0=deassert, 1=assert */ +{ + int err = -EINVAL; + + if (info->gpio[i].flag) { + if (val) + val = 1; + if (!info->gpio[i].active_high) + val = !val; + val &= 1; + err = val; + gpio_set_value_cansleep(info->gpio[i].gpio, val); + dev_dbg(&info->i2c_client->dev, "%s %u %d\n", + __func__, info->gpio[i].gpio, val); + } + return err; /* return value written or error */ +} - err = imx091_write_table(info->i2c_client, mode_table[sensor_mode], - reg_list, 5); - if (err) - return err; +static int imx091_gpio_pwrdn(struct imx091_info *info, int val) +{ + int prev_val; - info->mode = sensor_mode; - pr_info("[IMX091]: stream on.\n"); - return 0; + prev_val = imx091_gpio_rd(info, IMX091_GPIO_PWDN); + if (prev_val < 0) + return 1; /* assume PWRDN hardwired deasserted */ + + if (val == prev_val) + return 0; /* no change */ + + imx091_gpio_wr(info, IMX091_GPIO_PWDN, val); + return 1; /* return state change */ } -static int -imx091_get_status(struct imx091_info *info, u8 *dev_status) +static int imx091_gpio_reset(struct imx091_info *info, int val) { - *dev_status = 0; - return 0; + int err = 0; + + if (val) { + if (!info->reset_flag) { + info->reset_flag = true; + err = imx091_gpio_wr(info, IMX091_GPIO_RESET, 1); + if (err < 0) + return 0; /* flag no reset */ + + usleep_range(1000, 1500); + imx091_gpio_wr(info, IMX091_GPIO_RESET, 0); + msleep(IMX091_STARTUP_DELAY_MS); /* startup delay */ + err = 1; /* flag that a reset was done */ + } + } else { + info->reset_flag = false; + } + return err; } -static int -imx091_set_frame_length(struct imx091_info *info, u32 frame_length, - bool group_hold) +static void imx091_gpio_able(struct imx091_info *info, int val) { - struct imx091_reg reg_list[2]; - int i = 0; - int ret; + if (val) + imx091_gpio_wr(info, IMX091_GPIO_GP1, val); + else + imx091_gpio_wr(info, IMX091_GPIO_GP1, val); +} - imx091_get_frame_length_regs(reg_list, frame_length); +static void imx091_gpio_exit(struct imx091_info *info) +{ + unsigned i; - if (group_hold) { - ret = imx091_write_reg(info->i2c_client, 0x0104, 0x01); - if (ret) - return ret; + for (i = 0; i <= ARRAY_SIZE(imx091_gpios); i++) { + if (info->gpio[i].flag && info->gpio[i].own) { + gpio_free(info->gpio[i].gpio); + info->gpio[i].own = false; + } } +} - for (i = 0; i < 2; i++) { - ret = imx091_write_reg(info->i2c_client, reg_list[i].addr, - reg_list[i].val); - if (ret) - return ret; - } +static void imx091_gpio_init(struct imx091_info *info) +{ + char label[32]; + unsigned long flags; + unsigned type; + unsigned i; + unsigned j; + int err; + + for (i = 0; i < ARRAY_SIZE(imx091_gpios); i++) + info->gpio[i].flag = false; + if (!info->pdata->gpio_count || !info->pdata->gpio) + return; + + for (i = 0; i <= ARRAY_SIZE(imx091_gpios); i++) { + type = imx091_gpios[i].gpio_type; + for (j = 0; j < info->pdata->gpio_count; j++) { + if (type == info->pdata->gpio[j].gpio_type) + break; + } + if (j == info->pdata->gpio_count) + continue; + + info->gpio[type].gpio = info->pdata->gpio[j].gpio; + info->gpio[type].flag = true; + if (imx091_gpios[i].use_flags) { + flags = imx091_gpios[i].flags; + info->gpio[type].active_high = + imx091_gpios[i].active_high; + } else { + info->gpio[type].active_high = + info->pdata->gpio[j].active_high; + if (info->gpio[type].active_high) + flags = GPIOF_OUT_INIT_LOW; + else + flags = GPIOF_OUT_INIT_HIGH; + } + if (!info->pdata->gpio[j].init_en) + continue; - if (group_hold) { - ret = imx091_write_reg(info->i2c_client, 0x0104, 0x0); - if (ret) - return ret; + snprintf(label, sizeof(label), "imx091_%u_%s", + info->pdata->num, imx091_gpios[i].label); + err = gpio_request_one(info->gpio[type].gpio, flags, label); + if (err) { + dev_err(&info->i2c_client->dev, "%s ERR %s %u\n", + __func__, label, info->gpio[type].gpio); + } else { + info->gpio[type].own = true; + dev_dbg(&info->i2c_client->dev, "%s %s %u\n", + __func__, label, info->gpio[type].gpio); + } } +} - return 0; +static int imx091_vreg_dis_all(struct imx091_info *info) +{ + if (!info->pdata || !info->pdata->power_off) + return -EFAULT; + + return info->pdata->power_off(info->vreg); } -static int -imx091_set_coarse_time(struct imx091_info *info, u32 coarse_time, - bool group_hold) +static int imx091_vreg_en_all(struct imx091_info *info) { - int ret; + if (!info->pdata || !info->pdata->power_on) + return -EFAULT; - struct imx091_reg reg_list[2]; - int i = 0; + return info->pdata->power_on(info->vreg); +} - imx091_get_coarse_time_regs(reg_list, coarse_time); +static void imx091_vreg_exit(struct imx091_info *info) +{ + unsigned i; - if (group_hold) { - ret = imx091_write_reg(info->i2c_client, 0x104, 0x01); - if (ret) - return ret; + for (i = 0; i < ARRAY_SIZE(imx091_vregs); i++) { + regulator_put(info->vreg[i].vreg); + info->vreg[i].vreg = NULL; } +} - for (i = 0; i < 2; i++) { - ret = imx091_write_reg(info->i2c_client, reg_list[i].addr, - reg_list[i].val); - if (ret) - return ret; - } +static int imx091_vreg_init(struct imx091_info *info) +{ + unsigned i; + unsigned j; + int err = 0; - if (group_hold) { - ret = imx091_write_reg(info->i2c_client, 0x104, 0x0); - if (ret) - return ret; + for (i = 0; i < ARRAY_SIZE(imx091_vregs); i++) { + j = imx091_vregs[i].vreg_num; + info->vreg[j].vreg_name = imx091_vregs[i].vreg_name; + info->vreg[j].vreg_flag = false; + info->vreg[j].vreg = regulator_get(&info->i2c_client->dev, + info->vreg[j].vreg_name); + if (IS_ERR(info->vreg[j].vreg)) { + dev_dbg(&info->i2c_client->dev, "%s %s ERR: %d\n", + __func__, info->vreg[j].vreg_name, + (int)info->vreg[j].vreg); + err |= PTR_ERR(info->vreg[j].vreg); + info->vreg[j].vreg = NULL; + } else { + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, info->vreg[j].vreg_name); + } } - return 0; + return err; } -static int -imx091_set_gain(struct imx091_info *info, u16 gain, bool group_hold) +static int imx091_pm_wr(struct imx091_info *info, int pwr) { int ret; - struct imx091_reg reg_list; + int err = 0; + + if ((info->pdata->cfg & (NVC_CFG_OFF2STDBY | NVC_CFG_BOOT_INIT)) && + (pwr == NVC_PWR_OFF || + pwr == NVC_PWR_STDBY_OFF)) + pwr = NVC_PWR_STDBY; + if (pwr == info->pwr_dev) + return 0; - imx091_get_gain_reg(®_list, gain); + switch (pwr) { + case NVC_PWR_OFF_FORCE: + case NVC_PWR_OFF: + case NVC_PWR_STDBY_OFF: + imx091_gpio_pwrdn(info, 1); + err = imx091_vreg_dis_all(info); + imx091_gpio_able(info, 0); + imx091_gpio_reset(info, 0); + info->mode_valid = false; + info->bin_en = 0; + break; + + case NVC_PWR_STDBY: + imx091_gpio_pwrdn(info, 1); + err = imx091_vreg_en_all(info); + imx091_gpio_able(info, 1); + break; + + case NVC_PWR_COMM: + case NVC_PWR_ON: + if (info->pwr_dev != NVC_PWR_ON && + info->pwr_dev != NVC_PWR_COMM) + imx091_gpio_pwrdn(info, 1); + err = imx091_vreg_en_all(info); + imx091_gpio_able(info, 1); + ret = imx091_gpio_pwrdn(info, 0); + ret &= !imx091_gpio_reset(info, 1); + if (ret) /* if no reset && pwrdn changed states then delay */ + msleep(IMX091_STARTUP_DELAY_MS); + break; - if (group_hold) { - ret = imx091_write_reg(info->i2c_client, 0x104, 0x1); - if (ret) - return ret; + default: + err = -EINVAL; + break; } - ret = imx091_write_reg(info->i2c_client, reg_list.addr, reg_list.val); - if (ret) - return ret; - - if (group_hold) { - ret = imx091_write_reg(info->i2c_client, 0x104, 0x0); - if (ret) - return ret; + if (err < 0) { + dev_err(&info->i2c_client->dev, "%s err %d\n", __func__, err); + pwr = NVC_PWR_ERR; } - return 0; + info->pwr_dev = pwr; + dev_dbg(&info->i2c_client->dev, "%s pwr_dev=%d\n", + __func__, info->pwr_dev); + if (err > 0) + return 0; + + return err; } -static int -imx091_set_group_hold(struct imx091_info *info, struct imx091_ae *ae) +static int imx091_pm_wr_s(struct imx091_info *info, int pwr) { - int ret; - int count = 0; - bool groupHoldEnabled = false; - - if (ae->gain_enable) - count++; - if (ae->coarse_time_enable) - count++; - if (ae->frame_length_enable) - count++; - if (count >= 2) - groupHoldEnabled = true; - - if (groupHoldEnabled) { - ret = imx091_write_reg(info->i2c_client, 0x104, 0x1); - if (ret) - return ret; + int err1 = 0; + int err2 = 0; + + if ((info->s_mode == NVC_SYNC_OFF) || + (info->s_mode == NVC_SYNC_MASTER) || + (info->s_mode == NVC_SYNC_STEREO)) + err1 = imx091_pm_wr(info, pwr); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + err2 = imx091_pm_wr(info->s_info, pwr); + return err1 | err2; +} + +static int imx091_pm_api_wr(struct imx091_info *info, int pwr) +{ + int err = 0; + + if (!pwr || (pwr > NVC_PWR_ON)) + return 0; + + if (pwr > info->pwr_dev) + err = imx091_pm_wr_s(info, pwr); + if (!err) + info->pwr_api = pwr; + else + info->pwr_api = NVC_PWR_ERR; + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + + return err; +} + +static int imx091_pm_dev_wr(struct imx091_info *info, int pwr) +{ + if (pwr < info->pwr_api) + pwr = info->pwr_api; + if (info->mode_enable) + pwr = NVC_PWR_ON; + return imx091_pm_wr(info, pwr); +} + +static void imx091_pm_exit(struct imx091_info *info) +{ + imx091_pm_wr(info, NVC_PWR_OFF_FORCE); + imx091_vreg_exit(info); + imx091_gpio_exit(info); +} + +static void imx091_pm_init(struct imx091_info *info) +{ + imx091_gpio_init(info); + imx091_vreg_init(info); +} + +static int imx091_reset(struct imx091_info *info, u32 level) +{ + int err; + + if (level == NVC_RESET_SOFT) { + err = imx091_pm_wr(info, NVC_PWR_COMM); + err |= imx091_i2c_wr8(info, 0x0103, 0x01); /* SW reset */ + } else { + err = imx091_pm_wr(info, NVC_PWR_OFF_FORCE); } + err |= imx091_pm_wr(info, info->pwr_api); + return err; +} - if (ae->gain_enable) - imx091_set_gain(info, ae->gain, false); - if (ae->coarse_time_enable) - imx091_set_coarse_time(info, ae->coarse_time, false); - if (ae->frame_length_enable) - imx091_set_frame_length(info, ae->frame_length, false); - - if (groupHoldEnabled) { - ret = imx091_write_reg(info->i2c_client, 0x104, 0x0); - if (ret) - return ret; +static int imx091_dev_id(struct imx091_info *info) +{ + u16 val = 0; + unsigned i; + int err; + + dev_dbg(&info->i2c_client->dev, "%s +++++\n", + __func__); + imx091_pm_dev_wr(info, NVC_PWR_COMM); + dev_dbg(&info->i2c_client->dev, "DUCK:%s:%d\n", + __func__, __LINE__); + err = imx091_i2c_rd16(info, IMX091_ID_ADDRESS, &val); + if (!err) { + dev_dbg(&info->i2c_client->dev, "%s found devId: %x\n", + __func__, val); + info->sdata.sensor_id_minor = 0; + for (i = 0; i < ARRAY_SIZE(imx091_ids); i++) { + if (val == imx091_ids[i]) { + info->sdata.sensor_id_minor = val; + break; + } + } + if (!info->sdata.sensor_id_minor) { + err = -ENODEV; + dev_dbg(&info->i2c_client->dev, "%s No devId match\n", + __func__); + } } + imx091_pm_dev_wr(info, NVC_PWR_OFF); + dev_dbg(&info->i2c_client->dev, "%s -----\n", + __func__); + return err; +} - return 0; +static int imx091_mode_able(struct imx091_info *info, bool mode_enable) +{ + u8 val; + int err; + + if (mode_enable) + val = IMX091_STREAM_ENABLE; + else + val = IMX091_STREAM_DISABLE; + err = imx091_i2c_wr8(info, IMX091_STREAM_CONTROL_REG, val); + if (!err) { + info->mode_enable = mode_enable; + dev_dbg(&info->i2c_client->dev, "%s streaming=%x\n", + __func__, info->mode_enable); + if (!mode_enable) + imx091_pm_dev_wr(info, NVC_PWR_STDBY); + } + msleep(IMX091_WAIT_MS); + return err; } -static int imx091_get_sensor_id(struct imx091_info *info) +static int imx091_mode_rd(struct imx091_info *info, + s32 res_x, + s32 res_y, + u32 *index) { - int ret = 0; int i; - u8 bak = 0; - pr_info("%s\n", __func__); - if (info->sensor_data.fuse_id_size) + if (!res_x && !res_y) { + *index = info->cap->preferred_mode_index; return 0; + } - /* Note 1: If the sensor does not have power at this point - Need to supply the power, e.g. by calling power on function */ + for (i = 0; i < IMX091_NUM_MODES; i++) { + if ((res_x == imx091_mode_table[i]->sensor_mode.res_x) && + (res_y == imx091_mode_table[i]->sensor_mode.res_y)) { + break; + } + } - ret |= imx091_write_reg(info->i2c_client, 0x34C9, 0x10); - for (i = 0; i < 8 ; i++) { - ret |= imx091_read_reg(info->i2c_client, 0x3580 + i, &bak); - info->sensor_data.fuse_id[i] = bak; + if (i == IMX091_NUM_MODES) { + dev_err(&info->i2c_client->dev, + "%s invalid resolution: %dx%d\n", + __func__, res_x, res_y); + return -EINVAL; } - if (!ret) - info->sensor_data.fuse_id_size = i; + *index = i; + return 0; +} - /* Note 2: Need to clean up any action carried out in Note 1 */ +static int imx091_mode_wr_full(struct imx091_info *info, u32 mode_index) +{ + int err; - return ret; + imx091_pm_dev_wr(info, NVC_PWR_ON); + imx091_bin_wr(info, 0); + err = imx091_i2c_wr_table(info, + imx091_mode_table[mode_index]->p_mode_i2c); + if (!err) { + info->mode_index = mode_index; + info->mode_valid = true; + } else { + info->mode_valid = false; + } + return err; } -static long -imx091_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static int imx091_mode_wr(struct imx091_info *info, + struct nvc_imager_bayer *mode) { + u32 mode_index; int err; - struct imx091_info *info = file->private_data; - switch (cmd) { - case IMX091_IOCTL_SET_MODE: - { - struct imx091_mode mode; - if (copy_from_user(&mode, (const void __user *)arg, - sizeof(struct imx091_mode))) { - pr_err("%s:Failed to get mode from user.\n", __func__); - return -EFAULT; + err = imx091_mode_rd(info, mode->res_x, mode->res_y, &mode_index); + if (err < 0) + return err; + + if (!mode->res_x && !mode->res_y) { + if (mode->frame_length || mode->coarse_time || mode->gain) { + /* write exposure only */ + err = imx091_exposure_wr(info, mode); + return err; + } else { + /* turn off streaming */ + err = imx091_mode_able(info, false); + return err; } - return imx091_set_mode(info, &mode); } - case IMX091_IOCTL_SET_FRAME_LENGTH: - return imx091_set_frame_length(info, (u32)arg, true); - case IMX091_IOCTL_SET_COARSE_TIME: - return imx091_set_coarse_time(info, (u32)arg, true); - case IMX091_IOCTL_SET_GAIN: - return imx091_set_gain(info, (u16)arg, true); - case IMX091_IOCTL_GET_STATUS: - { - u8 status; - err = imx091_get_status(info, &status); + if (!info->mode_valid || (info->mode_index != mode_index)) + err = imx091_mode_wr_full(info, mode_index); + else + dev_dbg(&info->i2c_client->dev, "%s short mode\n", __func__); + err |= imx091_exposure_wr(info, mode); + if (err < 0) { + info->mode_valid = false; + goto imx091_mode_wr_err; + } + + err = imx091_mode_able(info, true); + if (err < 0) + goto imx091_mode_wr_err; + + return 0; + +imx091_mode_wr_err: + if (!info->mode_enable) + imx091_pm_dev_wr(info, NVC_PWR_STDBY); + return err; +} + + +static int imx091_param_rd(struct imx091_info *info, unsigned long arg) +{ + struct nvc_param params; + struct imx091_reg *p_i2c_table; + const void *data_ptr; + u32 data_size = 0; + u32 u32val; + int err; + + if (copy_from_user(¶ms, + (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", __func__, __LINE__); + return -EFAULT; + } + + if (info->s_mode == NVC_SYNC_SLAVE) + info = info->s_info; + + switch (params.param) { + case NVC_PARAM_GAIN: + imx091_pm_dev_wr(info, NVC_PWR_COMM); + err = imx091_gain_rd(info, &u32val); + imx091_pm_dev_wr(info, NVC_PWR_OFF); + dev_dbg(&info->i2c_client->dev, "%s GAIN: %u err: %d\n", + __func__, u32val, err); if (err) return err; - if (copy_to_user((void __user *)arg, &status, 1)) { - pr_err("%s:Failed to copy status to user\n", __func__); - return -EFAULT; - } - return 0; + + data_ptr = &u32val; + data_size = sizeof(u32val); + break; + + case NVC_PARAM_STEREO_CAP: + if (info->s_info != NULL) + err = 0; + else + err = -ENODEV; + dev_dbg(&info->i2c_client->dev, "%s STEREO_CAP: %d\n", + __func__, err); + data_ptr = &err; + data_size = sizeof(err); + break; + + case NVC_PARAM_STEREO: + dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", + __func__, info->s_mode); + data_ptr = &info->s_mode; + data_size = sizeof(info->s_mode); + break; + + case NVC_PARAM_STS: + err = imx091_dev_id(info); + dev_dbg(&info->i2c_client->dev, "%s STS: %d\n", + __func__, err); + data_ptr = &err; + data_size = sizeof(err); + break; + + case NVC_PARAM_DEV_ID: + if (!info->sdata.sensor_id_minor) + imx091_dev_id(info); + data_ptr = &info->sdata.sensor_id; + data_size = sizeof(info->sdata.sensor_id) * 2; + dev_dbg(&info->i2c_client->dev, "%s DEV_ID: %x-%x\n", + __func__, info->sdata.sensor_id, + info->sdata.sensor_id_minor); + break; + + case NVC_PARAM_SENSOR_TYPE: + data_ptr = &info->sdata.sensor_type; + data_size = sizeof(info->sdata.sensor_type); + dev_dbg(&info->i2c_client->dev, "%s SENSOR_TYPE: %d\n", + __func__, info->sdata.sensor_type); + break; + + case NVC_PARAM_FOCAL_LEN: + data_ptr = &info->sdata.focal_len; + data_size = sizeof(info->sdata.focal_len); + dev_dbg(&info->i2c_client->dev, "%s FOCAL_LEN: %u\n", + __func__, info->sdata.focal_len); + break; + + case NVC_PARAM_MAX_APERTURE: + data_ptr = &info->sdata.max_aperture; + data_size = sizeof(info->sdata.max_aperture); + dev_dbg(&info->i2c_client->dev, "%s MAX_APERTURE: %u\n", + __func__, info->sdata.max_aperture); + break; + + case NVC_PARAM_FNUMBER: + data_ptr = &info->sdata.fnumber; + data_size = sizeof(info->sdata.fnumber); + dev_dbg(&info->i2c_client->dev, "%s FNUMBER: %u\n", + __func__, info->sdata.fnumber); + break; + + case NVC_PARAM_VIEW_ANGLE_H: + data_ptr = &info->sdata.view_angle_h; + data_size = sizeof(info->sdata.view_angle_h); + dev_dbg(&info->i2c_client->dev, "%s VIEW_ANGLE_H: %u\n", + __func__, info->sdata.view_angle_h); + break; + + case NVC_PARAM_VIEW_ANGLE_V: + data_ptr = &info->sdata.view_angle_v; + data_size = sizeof(info->sdata.view_angle_v); + dev_dbg(&info->i2c_client->dev, "%s VIEW_ANGLE_V: %u\n", + __func__, info->sdata.view_angle_v); + break; + + case NVC_PARAM_I2C: + dev_dbg(&info->i2c_client->dev, "%s I2C\n", __func__); + if (params.sizeofvalue > IMX091_I2C_TABLE_MAX_ENTRIES) { + dev_err(&info->i2c_client->dev, + "%s NVC_PARAM_I2C request size too large\n", + __func__); + return -EINVAL; + } + p_i2c_table = kzalloc(sizeof(params.sizeofvalue), GFP_KERNEL); + if (p_i2c_table == NULL) { + pr_err("%s: kzalloc error\n", __func__); + return -ENOMEM; + } + + if (copy_from_user(p_i2c_table, + (const void __user *)params.p_value, + params.sizeofvalue)) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", + __func__, __LINE__); + kfree(p_i2c_table); + return -EINVAL; + } + + imx091_pm_dev_wr(info, NVC_PWR_COMM); + err = imx091_i2c_rd_table(info, p_i2c_table); + imx091_pm_dev_wr(info, NVC_PWR_OFF); + if (copy_to_user((void __user *)params.p_value, + p_i2c_table, + params.sizeofvalue)) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + err = -EINVAL; + } + kfree(p_i2c_table); + return err; + + default: + dev_dbg(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params.param); + return -EINVAL; } - case IMX091_IOCTL_GET_SENSORDATA: - { - err = imx091_get_sensor_id(info); + if (params.sizeofvalue < data_size) { + dev_err(&info->i2c_client->dev, + "%s data size mismatch %d != %d Param: %d\n", + __func__, params.sizeofvalue, data_size, params.param); + return -EINVAL; + } + + if (copy_to_user((void __user *)params.p_value, + data_ptr, + data_size)) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", __func__, __LINE__); + return -EFAULT; + } + + return 0; +} + +static int imx091_param_wr_s(struct imx091_info *info, + struct nvc_param *params, + u32 u32val) +{ + struct imx091_reg *p_i2c_table; + u8 u8val; + int err; + + u8val = (u8)u32val; + switch (params->param) { + case NVC_PARAM_GAIN: + dev_dbg(&info->i2c_client->dev, "%s GAIN: %u\n", + __func__, u32val); + imx091_pm_dev_wr(info, NVC_PWR_COMM); + err = imx091_gain_wr(info, u32val); if (err) { - pr_err("%s:Failed to get fuse id info.\n", __func__); - return err; + dev_err(&info->i2c_client->dev, "Error: %s SET GAIN ERR", + __func__); } - if (copy_to_user((void __user *)arg, &info->sensor_data, - sizeof(struct imx091_sensordata))) { - pr_info("%s:Failed to copy fuse id to user space\n", + imx091_pm_dev_wr(info, NVC_PWR_STDBY); + return err; + + case NVC_PARAM_RESET: + err = imx091_reset(info, u32val); + dev_dbg(&info->i2c_client->dev, "%s RESET=%d err=%d\n", + __func__, u32val, err); + return err; + + case NVC_PARAM_TESTMODE: + dev_dbg(&info->i2c_client->dev, "%s TESTMODE: %u\n", + __func__, (unsigned)u8val); + if (u8val) + u32val = info->test_pattern; + else + u32val = 0; + imx091_pm_dev_wr(info, NVC_PWR_ON); + err = imx091_test_pattern_wr(info, u32val); + if (!u8val) + imx091_pm_dev_wr(info, NVC_PWR_OFF); + return err; + + case NVC_PARAM_TEST_PATTERN: + dev_dbg(&info->i2c_client->dev, "%s TEST_PATTERN: %d\n", + __func__, u32val); + info->test_pattern = u32val; + return 0; + + case NVC_PARAM_SELF_TEST: + err = imx091_dev_id(info); + dev_dbg(&info->i2c_client->dev, "%s SELF_TEST: %d\n", + __func__, err); + return err; + + case NVC_PARAM_I2C: + dev_dbg(&info->i2c_client->dev, "%s I2C\n", __func__); + if (params->sizeofvalue > IMX091_I2C_TABLE_MAX_ENTRIES) { + dev_err(&info->i2c_client->dev, + "%s NVC_PARAM_I2C request size too large\n", __func__); + return -EINVAL; + } + p_i2c_table = kzalloc(sizeof(params->sizeofvalue), GFP_KERNEL); + if (p_i2c_table == NULL) { + dev_err(&info->i2c_client->dev, + "%s kzalloc err line %d\n", + __func__, __LINE__); + return -ENOMEM; + } + + if (copy_from_user(p_i2c_table, + (const void __user *)params->p_value, + params->sizeofvalue)) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", + __func__, __LINE__); + kfree(p_i2c_table); return -EFAULT; } - return 0; - } - case IMX091_IOCTL_SET_GROUP_HOLD: - { - struct imx091_ae ae; - if (copy_from_user(&ae, (const void __user *)arg, - sizeof(struct imx091_ae))) { - pr_info("%s:fail group hold\n", __func__); - return -EFAULT; - } - return imx091_set_group_hold(info, &ae); - } + + imx091_pm_dev_wr(info, NVC_PWR_ON); + err = imx091_i2c_wr_table(info, p_i2c_table); + kfree(p_i2c_table); + return err; + default: - pr_err("%s:unknown cmd.\n", __func__); - return -EINVAL; + dev_dbg(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params->param); + return -EINVAL; } - return 0; } -static int -imx091_open(struct inode *inode, struct file *file) +static int imx091_param_wr(struct imx091_info *info, unsigned long arg) { - struct miscdevice *miscdev = file->private_data; - struct imx091_info *info; + struct nvc_param params; + u8 u8val; + u32 u32val; + int err = 0; - info = container_of(miscdev, struct imx091_info, miscdev_info); - /* check if the device is in use */ - if (atomic_xchg(&info->in_use, 1)) { - pr_info("%s:BUSY!\n", __func__); - return -EBUSY; + if (copy_from_user(¶ms, (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", __func__, __LINE__); + return -EFAULT; } - file->private_data = info; + if (copy_from_user(&u32val, (const void __user *)params.p_value, + sizeof(u32val))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); + return -EFAULT; + } + + u8val = (u8)u32val; + /* parameters independent of sync mode */ + switch (params.param) { + case NVC_PARAM_STEREO: + dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", + __func__, u8val); + if (u8val == info->s_mode) + return 0; + + switch (u8val) { + case NVC_SYNC_OFF: + info->s_mode = u8val; + if (info->s_info != NULL) { + info->s_info->s_mode = u8val; + imx091_pm_wr(info->s_info, NVC_PWR_OFF); + } + break; + + case NVC_SYNC_MASTER: + info->s_mode = u8val; + if (info->s_info != NULL) + info->s_info->s_mode = u8val; + break; + + case NVC_SYNC_SLAVE: + if (info->s_info != NULL) { + /* sync power */ + info->s_info->pwr_api = info->pwr_api; + err = imx091_pm_wr(info->s_info, + info->pwr_dev); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + } else { + if (info->s_mode != NVC_SYNC_STEREO) + imx091_pm_wr(info->s_info, + NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + + case NVC_SYNC_STEREO: + if (info->s_info != NULL) { + /* sync power */ + info->s_info->pwr_api = info->pwr_api; + err = imx091_pm_wr(info->s_info, + info->pwr_dev); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + } else { + if (info->s_mode != NVC_SYNC_SLAVE) + imx091_pm_wr(info->s_info, + NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + + default: + err = -EINVAL; + } + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + + return err; - if (info->pdata && info->pdata->power_on) - info->pdata->power_on(&info->power); - else{ - pr_err("%s:no valid power_on function.\n", __func__); - return -EEXIST; + case NVC_PARAM_GROUP_HOLD: + { + struct nvc_imager_ae ae; + dev_dbg(&info->i2c_client->dev, "%s GROUP_HOLD\n", + __func__); + if (copy_from_user(&ae, (const void __user *)params.p_value, + sizeof(struct nvc_imager_ae))) { + dev_err(&info->i2c_client->dev, "Error: %s %d copy_from_user err\n", + __func__, __LINE__); + return -EFAULT; + } + imx091_pm_dev_wr(info, NVC_PWR_COMM); + err = imx091_group_hold_wr(info, &ae); + imx091_pm_dev_wr(info, NVC_PWR_STDBY); + return err; } - return 0; + default: + /* parameters dependent on sync mode */ + switch (info->s_mode) { + case NVC_SYNC_OFF: + case NVC_SYNC_MASTER: + return imx091_param_wr_s(info, ¶ms, u32val); + + case NVC_SYNC_SLAVE: + return imx091_param_wr_s(info->s_info, ¶ms, + u32val); + + case NVC_SYNC_STEREO: + err = imx091_param_wr_s(info, ¶ms, u32val); + if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) + err |= imx091_param_wr_s(info->s_info, + ¶ms, u32val); + return err; + + default: + dev_err(&info->i2c_client->dev, "%s %d internal err\n", + __func__, __LINE__); + return -EINVAL; + } + } } -static int -imx091_release(struct inode *inode, struct file *file) +static long imx091_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) { struct imx091_info *info = file->private_data; + struct nvc_imager_bayer mode; + struct nvc_imager_mode_list mode_list; + struct nvc_imager_mode mode_table[IMX091_NUM_MODES]; + struct nvc_imager_dnvc dnvc; + const void *data_ptr; + s32 num_modes; + u32 i; + int pwr; + int err; - if (info->pdata && info->pdata->power_off) - info->pdata->power_off(&info->power); - file->private_data = NULL; + switch (cmd) { + case NVC_IOCTL_PARAM_WR: + err = imx091_param_wr(info, arg); + return err; - /* warn if device is already released */ - WARN_ON(!atomic_xchg(&info->in_use, 0)); - return 0; -} + case NVC_IOCTL_PARAM_RD: + err = imx091_param_rd(info, arg); + return err; -static int imx091_power_put(struct imx091_power_rail *pw) -{ - if (unlikely(!pw)) - return -EFAULT; + case NVC_IOCTL_DYNAMIC_RD: + if (copy_from_user(&dnvc, (const void __user *)arg, + sizeof(struct nvc_imager_dnvc))) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + + dev_dbg(&info->i2c_client->dev, "%s DYNAMIC_RD x=%d y=%d\n", + __func__, dnvc.res_x, dnvc.res_y); + err = imx091_mode_rd(info, dnvc.res_x, dnvc.res_y, &i); + if (err) + return -EINVAL; + + if (dnvc.p_mode) { + if (copy_to_user((void __user *)dnvc.p_mode, + &imx091_mode_table[i]->sensor_mode, + sizeof(struct nvc_imager_mode))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + } + + if (dnvc.p_dnvc) { + if (copy_to_user((void __user *)dnvc.p_dnvc, + &imx091_mode_table[i]->sensor_dnvc, + sizeof(struct nvc_imager_dynamic_nvc))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + } - if (likely(pw->avdd)) - regulator_put(pw->avdd); + return 0; - if (likely(pw->iovdd)) - regulator_put(pw->iovdd); + case NVC_IOCTL_MODE_WR: + if (copy_from_user(&mode, (const void __user *)arg, + sizeof(struct nvc_imager_bayer))) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } - if (likely(pw->dvdd)) - regulator_put(pw->dvdd); + dev_dbg(&info->i2c_client->dev, + "%s MODE_WR x=%d y=%d coarse=%u frame=%u gain=%u\n", + __func__, mode.res_x, mode.res_y, + mode.coarse_time, mode.frame_length, mode.gain); + err = imx091_mode_wr(info, &mode); + return err; - pw->avdd = NULL; - pw->iovdd = NULL; - pw->dvdd = NULL; + case NVC_IOCTL_MODE_RD: + /* + * Return a list of modes that sensor bayer supports. + * If called with a NULL ptr to pModes, + * then it just returns the count. + */ + dev_dbg(&info->i2c_client->dev, "%s MODE_RD n=%d\n", + __func__, IMX091_NUM_MODES); + if (copy_from_user(&mode_list, (const void __user *)arg, + sizeof(struct nvc_imager_mode_list))) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + + num_modes = IMX091_NUM_MODES; + if (mode_list.p_num_mode != NULL) { + if (copy_to_user((void __user *)mode_list.p_num_mode, + &num_modes, sizeof(num_modes))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + } + if (mode_list.p_modes != NULL) { + for (i = 0; i < IMX091_NUM_MODES; i++) { + mode_table[i] = + imx091_mode_table[i]->sensor_mode; + } + if (copy_to_user((void __user *)mode_list.p_modes, + (const void *)&mode_table, + sizeof(mode_table))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + } + + return 0; + + case NVC_IOCTL_PWR_WR: + /* This is a Guaranteed Level of Service (GLOS) call */ + pwr = (int)arg * 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_WR: %d\n", + __func__, pwr); + err = imx091_pm_api_wr(info, pwr); + return err; + + case NVC_IOCTL_PWR_RD: + if (info->s_mode == NVC_SYNC_SLAVE) + pwr = info->s_info->pwr_api / 2; + else + pwr = info->pwr_api / 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", + __func__, pwr); + if (copy_to_user((void __user *)arg, (const void *)&pwr, + sizeof(pwr))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + + return 0; + + case NVC_IOCTL_CAPS_RD: + dev_dbg(&info->i2c_client->dev, "%s CAPS_RD n=%d\n", + __func__, sizeof(imx091_dflt_cap)); + data_ptr = info->cap; + if (copy_to_user((void __user *)arg, + data_ptr, + sizeof(imx091_dflt_cap))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + + return 0; + + case NVC_IOCTL_STATIC_RD: + dev_dbg(&info->i2c_client->dev, "%s STATIC_RD n=%d\n", + __func__, sizeof(struct nvc_imager_static_nvc)); + data_ptr = &info->sdata; + if (copy_to_user((void __user *)arg, + data_ptr, + sizeof(struct nvc_imager_static_nvc))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + + return 0; + + default: + dev_dbg(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", + __func__, cmd); + } + + return -EINVAL; +} + +static void imx091_sdata_init(struct imx091_info *info) +{ + if (info->pdata->cap) + info->cap = info->pdata->cap; + else + info->cap = &imx091_dflt_cap; + memcpy(&info->sdata, &imx091_dflt_sdata, sizeof(info->sdata)); + if (info->pdata->lens_focal_length) + info->sdata.focal_len = info->pdata->lens_focal_length; + if (info->pdata->lens_max_aperture) + info->sdata.max_aperture = info->pdata->lens_max_aperture; + if (info->pdata->lens_fnumber) + info->sdata.fnumber = info->pdata->lens_fnumber; + if (info->pdata->lens_view_angle_h) + info->sdata.view_angle_h = info->pdata->lens_view_angle_h; + if (info->pdata->lens_view_angle_v) + info->sdata.view_angle_v = info->pdata->lens_view_angle_v; +} + +static int imx091_sync_en(unsigned num, unsigned sync) +{ + struct imx091_info *master = NULL; + struct imx091_info *slave = NULL; + struct imx091_info *pos = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(pos, &imx091_info_list, list) { + if (pos->pdata->num == num) { + master = pos; + break; + } + } + pos = NULL; + list_for_each_entry_rcu(pos, &imx091_info_list, list) { + if (pos->pdata->num == sync) { + slave = pos; + break; + } + } + rcu_read_unlock(); + if (master != NULL) + master->s_info = NULL; + if (slave != NULL) + slave->s_info = NULL; + if (!sync) + return 0; /* no err if sync disabled */ + + if (num == sync) + return -EINVAL; /* err if sync instance is itself */ + + if ((master != NULL) && (slave != NULL)) { + master->s_info = slave; + slave->s_info = master; + } return 0; } -static int imx091_regulator_get(struct imx091_info *info, - struct regulator **vreg, char vreg_name[]) +static int imx091_sync_dis(struct imx091_info *info) { - struct regulator *reg = NULL; - int err = 0; + if (info->s_info != NULL) { + info->s_info->s_mode = 0; + info->s_info->s_info = NULL; + info->s_mode = 0; + info->s_info = NULL; + return 0; + } - reg = regulator_get(&info->i2c_client->dev, vreg_name); - if (unlikely(IS_ERR(reg))) { - dev_err(&info->i2c_client->dev, "%s %s ERR: %d\n", - __func__, vreg_name, (int)reg); - err = PTR_ERR(reg); - reg = NULL; - } else - dev_dbg(&info->i2c_client->dev, "%s: %s\n", - __func__, vreg_name); - - *vreg = reg; - return err; + return -EINVAL; } -static int imx091_power_get(struct imx091_info *info) +static int imx091_open(struct inode *inode, struct file *file) { - struct imx091_power_rail *pw = &info->power; + struct imx091_info *info = NULL; + struct imx091_info *pos = NULL; + int err; - imx091_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.7v */ - imx091_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */ - imx091_regulator_get(info, &pw->iovdd, "vif"); /* interface 1.8v */ + rcu_read_lock(); + list_for_each_entry_rcu(pos, &imx091_info_list, list) { + if (pos->miscdev.minor == iminor(inode)) { + info = pos; + break; + } + } + rcu_read_unlock(); + if (!info) { + pr_err("%s err @%d info is null\n", __func__, __LINE__); + return -ENODEV; + } + + dev_dbg(&info->i2c_client->dev, "%s +++++\n", __func__); + err = imx091_sync_en(info->pdata->num, info->pdata->sync); + if (err == -EINVAL) + dev_err(&info->i2c_client->dev, + "%s err: invalid num (%u) and sync (%u) instance\n", + __func__, info->pdata->num, info->pdata->sync); + if (atomic_xchg(&info->in_use, 1)) { + dev_err(&info->i2c_client->dev, "%s err @%d device is busy\n", + __func__, __LINE__); + return -EBUSY; + } + if (info->s_info != NULL) { + if (atomic_xchg(&info->s_info->in_use, 1)) { + dev_err(&info->i2c_client->dev, "%s err @%d sync device is busy\n", + __func__, __LINE__); + return -EBUSY; + } + info->sdata.stereo_cap = 1; + } + + file->private_data = info; + dev_dbg(&info->i2c_client->dev, "%s -----\n", __func__); + return 0; +} + +static int imx091_release(struct inode *inode, struct file *file) +{ + struct imx091_info *info = file->private_data; + + dev_dbg(&info->i2c_client->dev, "%s +++++\n", __func__); + imx091_pm_wr_s(info, NVC_PWR_OFF); + file->private_data = NULL; + WARN_ON(!atomic_xchg(&info->in_use, 0)); + if (info->s_info != NULL) + WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); + imx091_sync_dis(info); + dev_dbg(&info->i2c_client->dev, "%s -----\n", __func__); return 0; } @@ -1342,69 +2296,116 @@ static const struct file_operations imx091_fileops = { .release = imx091_release, }; -static struct miscdevice imx091_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "imx091", - .fops = &imx091_fileops, -}; +static void imx091_del(struct imx091_info *info) +{ + imx091_pm_exit(info); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + imx091_pm_exit(info->s_info); + imx091_sync_dis(info); + spin_lock(&imx091_spinlock); + list_del_rcu(&info->list); + spin_unlock(&imx091_spinlock); + synchronize_rcu(); +} -static int -imx091_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int imx091_remove(struct i2c_client *client) +{ + struct imx091_info *info = i2c_get_clientdata(client); + + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + misc_deregister(&info->miscdev); + imx091_del(info); + return 0; +} + +static int imx091_probe( + struct i2c_client *client, + const struct i2c_device_id *id) { struct imx091_info *info; + char dname[16]; + unsigned long clock_probe_rate; int err; - pr_info("[IMX091]: probing sensor.\n"); - - info = devm_kzalloc(&client->dev, - sizeof(struct imx091_info), GFP_KERNEL); - if (!info) { - pr_err("%s:Unable to allocate memory!\n", __func__); + dev_dbg(&client->dev, "%s +++++\n", __func__); + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&client->dev, "%s: kzalloc error\n", __func__); return -ENOMEM; } - info->pdata = client->dev.platform_data; info->i2c_client = client; - atomic_set(&info->in_use, 0); - info->mode = -1; - - imx091_power_get(info); - - memcpy(&info->miscdev_info, - &imx091_device, - sizeof(struct miscdevice)); - - err = misc_register(&info->miscdev_info); - if (err) { - pr_err("%s:Unable to register misc device!\n", __func__); - goto imx091_probe_fail; + if (client->dev.platform_data) { + info->pdata = client->dev.platform_data; + } else { + info->pdata = &imx091_dflt_pdata; + dev_dbg(&client->dev, + "%s No platform data. Using defaults.\n", __func__); } - i2c_set_clientdata(client, info); - return 0; - -imx091_probe_fail: - imx091_power_put(&info->power); - - return err; -} - -static int -imx091_remove(struct i2c_client *client) -{ - struct imx091_info *info; - info = i2c_get_clientdata(client); - misc_deregister(&imx091_device); - - imx091_power_put(&info->power); + INIT_LIST_HEAD(&info->list); + spin_lock(&imx091_spinlock); + list_add_rcu(&info->list, &imx091_info_list); + spin_unlock(&imx091_spinlock); + imx091_pm_init(info); + imx091_sdata_init(info); + if (info->pdata->cfg & (NVC_CFG_NODEV | NVC_CFG_BOOT_INIT)) { + if (info->pdata->probe_clock) { + if (info->cap->initial_clock_rate_khz) + clock_probe_rate = info->cap-> + initial_clock_rate_khz; + else + clock_probe_rate = imx091_dflt_cap. + initial_clock_rate_khz; + clock_probe_rate *= 1000; + info->pdata->probe_clock(clock_probe_rate); + } + err = imx091_dev_id(info); + if (err < 0) { + if (info->pdata->cfg & NVC_CFG_NODEV) { + imx091_del(info); + if (info->pdata->probe_clock) + info->pdata->probe_clock(0); + return -ENODEV; + } else { + dev_err(&client->dev, "%s device not found\n", + __func__); + } + } else { + dev_dbg(&client->dev, "%s device found\n", __func__); + if (info->pdata->cfg & NVC_CFG_BOOT_INIT) + imx091_mode_wr_full(info, info->cap-> + preferred_mode_index); + } + imx091_pm_dev_wr(info, NVC_PWR_OFF); + if (info->pdata->probe_clock) + info->pdata->probe_clock(0); + } + if (info->pdata->dev_name != 0) + strcpy(dname, info->pdata->dev_name); + else + strcpy(dname, "imx091"); + if (info->pdata->num) + snprintf(dname, sizeof(dname), "%s.%u", + dname, info->pdata->num); + info->miscdev.name = dname; + info->miscdev.fops = &imx091_fileops; + info->miscdev.minor = MISC_DYNAMIC_MINOR; + if (misc_register(&info->miscdev)) { + dev_err(&client->dev, "%s unable to register misc device %s\n", + __func__, dname); + imx091_del(info); + return -ENODEV; + } + dev_dbg(&client->dev, "%s -----\n", __func__); return 0; } static const struct i2c_device_id imx091_id[] = { { "imx091", 0 }, - { } + { }, }; MODULE_DEVICE_TABLE(i2c, imx091_id); @@ -1414,21 +2415,10 @@ static struct i2c_driver imx091_i2c_driver = { .name = "imx091", .owner = THIS_MODULE, }, + .id_table = imx091_id, .probe = imx091_probe, .remove = imx091_remove, - .id_table = imx091_id, }; -static int __init imx091_init(void) -{ - pr_info("[IMX091] sensor driver loading\n"); - return i2c_add_driver(&imx091_i2c_driver); -} - -static void __exit imx091_exit(void) -{ - i2c_del_driver(&imx091_i2c_driver); -} - -module_init(imx091_init); -module_exit(imx091_exit); +module_i2c_driver(imx091_i2c_driver); +MODULE_LICENSE("GPL v2"); |