summaryrefslogtreecommitdiff
path: root/drivers/media/video
diff options
context:
space:
mode:
authorWei Chen <wechen@nvidia.com>2012-10-31 12:55:01 -0700
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-11-08 07:19:04 -0800
commitf8b5d012af3f4da4150b4ac2de4fb7382a1cf6f9 (patch)
tree3dc15af74de51e710a57a57aab86c35ce4d1caea /drivers/media/video
parentf266d25b766a90c10770857f2404cc71fd4c3d7a (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/media/video')
-rw-r--r--drivers/media/video/tegra/imx091.c2716
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(&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(&params,
+ (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(&params, (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, &params, u32val);
+
+ case NVC_SYNC_SLAVE:
+ return imx091_param_wr_s(info->s_info, &params,
+ u32val);
+
+ case NVC_SYNC_STEREO:
+ err = imx091_param_wr_s(info, &params, u32val);
+ if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX))
+ err |= imx091_param_wr_s(info->s_info,
+ &params, 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");