From patchwork Fri Jan 30 23:46:01 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dominic Curran X-Patchwork-Id: 4805 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n0UNkJIW013485 for ; Fri, 30 Jan 2009 23:46:22 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757987AbZA3XqS (ORCPT ); Fri, 30 Jan 2009 18:46:18 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1757977AbZA3XqS (ORCPT ); Fri, 30 Jan 2009 18:46:18 -0500 Received: from bear.ext.ti.com ([192.94.94.41]:45251 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757952AbZA3XqI (ORCPT ); Fri, 30 Jan 2009 18:46:08 -0500 Received: from dlep33.itg.ti.com ([157.170.170.112]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n0UNk2R7013605; Fri, 30 Jan 2009 17:46:07 -0600 Received: from gromit.am.dhcp.ti.com (localhost [127.0.0.1]) by dlep33.itg.ti.com (8.13.7/8.13.7) with ESMTP id n0UNk1Ed022216; Fri, 30 Jan 2009 17:46:01 -0600 (CST) From: Dominic Curran To: linux-media@vger.kernel.org, "linux-omap" Subject: [OMAPZOOM][PATCH v2 3/6] IMX046: Add support for Sony imx046 sensor. Date: Fri, 30 Jan 2009 17:46:01 -0600 User-Agent: KMail/1.9.6 (enterprise 0.20071204.744707) Cc: greg.hofer@hp.com MIME-Version: 1.0 Content-Disposition: inline Message-Id: <200901301746.01504.dcurran@ti.com> Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Dominic Curran Subject: [OMAPZOOM][PATCH v2 3/6] IMX046: Add support for Sony imx046 sensor. This patch adds the driver files for the Sony IMX046 8MP camera sensor. Driver sets up the sensor to send frame data via the MIPI CSI2 i/f. Sensor is setup to output the following base sizes: - 3280 x 2464 (8MP) - 3280 x 616 (2MP) - 820 x 616 Sensor's output image format is Bayer10 (GrR/BGb). Driver has V4L2 controls for: - Exposure - Analog Gain Signed-off-by: Greg Hofer Signed-off-by: Dominic Curran --- drivers/media/video/Kconfig | 7 drivers/media/video/Makefile | 1 drivers/media/video/imx046.c | 1632 +++++++++++++++++++++++++++++++++++++++++++ drivers/media/video/imx046.h | 326 ++++++++ 4 files changed, 1966 insertions(+) create mode 100644 drivers/media/video/imx046.c create mode 100644 drivers/media/video/imx046.h -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Index: omapzoom04/drivers/media/video/Kconfig =================================================================== --- omapzoom04.orig/drivers/media/video/Kconfig +++ omapzoom04/drivers/media/video/Kconfig @@ -334,6 +334,13 @@ config VIDEO_OV3640_CSI2 This enables the use of the CSI2 serial bus for the ov3640 camera. +config VIDEO_IMX046 + tristate "Sony IMX046 sensor driver (8MP)" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the Sony + IMX046 camera. + config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" depends on VIDEO_V4L1 && I2C Index: omapzoom04/drivers/media/video/Makefile =================================================================== --- omapzoom04.orig/drivers/media/video/Makefile +++ omapzoom04/drivers/media/video/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_MT9P012) += mt9p012.o obj-$(CONFIG_VIDEO_DW9710) += dw9710.o obj-$(CONFIG_VIDEO_OV3640) += ov3640.o +obj-$(CONFIG_VIDEO_IMX046) += imx046.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o Index: omapzoom04/drivers/media/video/imx046.c =================================================================== --- /dev/null +++ omapzoom04/drivers/media/video/imx046.c @@ -0,0 +1,1632 @@ +/* + * drivers/media/video/imx046.c + * + * Sony imx046 sensor driver + * + * + * Copyright (C) 2008 Hewlett Packard + * + * Leverage mt9p012.c + * + * 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 +#include + +#include + +#include "imx046.h" +#include "omap34xxcam.h" +#include "isp/isp.h" +#include "isp/ispcsi2.h" + + +#define IMX046_DRIVER_NAME "imx046" +#define IMX046_MOD_NAME "IMX046: " + +#define I2C_M_WR 0 + + +/* from IMX046ES_registerSetting_I2C_MIPI_2lane_def_080925.xls */ +const static struct imx046_reg initial_list[] = { + {IMX046_REG_IMAGE_ORIENTATION, 0x03, I2C_8BIT}, + {IMX046_REG_COARSE_INT_TIME, 0x0120, I2C_16BIT}, + {IMX046_REG_ANALOG_GAIN_GLOBAL, 0x80, I2C_16BIT}, + {0x300A, 0x80, I2C_8BIT}, + {IMX046_REG_Y_OPBADDR_START_DI, 0x08, I2C_8BIT}, + {IMX046_REG_Y_OPBADDR_END_DI, 0x37, I2C_8BIT}, + {IMX046_REG_CHCODE_OUTCHSINGLE, 0x60, I2C_8BIT}, + {IMX046_REG_OUTIF, 0x01, I2C_8BIT}, + {IMX046_REG_RGPOF_RGPOFV2, 0x28, I2C_8BIT}, + {IMX046_REG_CPCKAUTOEN, 0x00, I2C_8BIT}, + {IMX046_REG_RGCPVFB, 0x60, I2C_8BIT}, + {IMX046_REG_RGAZPDRV, 0x24, I2C_8BIT}, + {IMX046_REG_RGAZTEST, 0x34, I2C_8BIT}, + {IMX046_REG_RGVSUNLV, 0x3B, I2C_8BIT}, + {IMX046_REG_CLPOWER, 0x30, I2C_8BIT}, + {IMX046_REG_CLPOWERSP, 0x00, I2C_8BIT}, + {IMX046_REG_ACLDIRV_TVADDCLP, 0x00, I2C_8BIT}, + {IMX046_REG_OPB_CTRL, 0x0080, I2C_16BIT}, + {0x30AB, 0x1C, I2C_8BIT}, + {0x30B0, 0x32, I2C_8BIT}, + {0x30B2, 0x83, I2C_8BIT}, + {IMX046_REG_RAW10CH2V2P_LO, 0xD8, I2C_8BIT}, + {IMX046_REG_RAW10CH2V2D_LO, 0x17, I2C_8BIT}, + {IMX046_REG_COMP8CH1V2P_LO, 0xCF, I2C_8BIT}, + {IMX046_REG_COMP8CH1V2D_LO, 0xF1, I2C_8BIT}, + {IMX046_REG_RAW10CH1V2P_LO, 0xD8, I2C_8BIT}, + {IMX046_REG_RAW10CH1V2D_LO, 0x17, I2C_8BIT}, + {0x3302, 0x0A, I2C_8BIT}, + {0x3303, 0x09, I2C_8BIT}, + {IMX046_REG_RGTLPX, 0x05, I2C_8BIT}, + {IMX046_REG_RGTCLKPREPARE, 0x04, I2C_8BIT}, + {IMX046_REG_RGTCLKZERO, 0x15, I2C_8BIT}, + {IMX046_REG_RGTCLKPRE, 0x03, I2C_8BIT}, + {IMX046_REG_RGTCLKPOST, 0x13, I2C_8BIT}, + {IMX046_REG_RGTCLKTRAIL, 0x05, I2C_8BIT}, + {IMX046_REG_RGTHSEXIT, 0x0B, I2C_8BIT}, + {0x302B, 0x38, I2C_8BIT}, /* for 18Mhz xclk */ + {I2C_REG_TERM, I2C_VAL_TERM, I2C_LEN_TERM} +}; + +/** + * struct imx046_sensor - main structure for storage of sensor information + * @pdata: access functions and data for platform level information + * @v4l2_int_device: V4L2 device structure structure + * @i2c_client: iic client device structure + * @pix: V4L2 pixel format information structure + * @timeperframe: time per frame expressed as V4L fraction + * @scaler: + * @ver: imx046 chip version + * @fps: frames per second value + */ +struct imx046_sensor { + const struct imx046_platform_data *pdata; + struct v4l2_int_device *v4l2_int_device; + struct i2c_client *i2c_client; + struct v4l2_pix_format pix; + struct v4l2_fract timeperframe; + int scaler; + int ver; + int fps; + int state; + bool resuming; +}; + +static struct imx046_sensor imx046; +static struct i2c_driver imx046sensor_i2c_driver; +static unsigned long xclk_current = IMX046_XCLK_NOM_1; +static enum imx046_image_size isize_current = EIGHT_MP; + +/* list of image formats supported by imx046 sensor */ +const static struct v4l2_fmtdesc imx046_formats[] = { + { + .description = "Bayer10 (GrR/BGb)", + .pixelformat = V4L2_PIX_FMT_SGRBG10, + } +}; + +#define NUM_CAPTURE_FORMATS ARRAY_SIZE(imx046_formats) + +static u32 min_exposure_time = 1000; +static u32 max_exposure_time = 128000; +static enum v4l2_power current_power_state; + +/* Structure of Sensor settings that change with image size */ +static struct imx046_sensor_settings sensor_settings[] = { + /* NOTE: must be in same order as image_size array */ + + /* HALF_MP */ + { + .clk = { + .pre_pll_div = 1, + .pll_mult = 18, + .post_pll_div = 1, + .vt_pix_clk_div = 10, + .vt_sys_clk_div = 1, + }, + .mipi = { + .data_lanes = 1, + .ths_prepare = 2, + .ths_zero = 5, + .ths_settle_lower = 8, + .ths_settle_upper = 28, + }, + .frame = { + .frame_len_lines_min = 629, + .line_len_pck = 3440, + .x_addr_start = 0, + .x_addr_end = 3279, + .y_addr_start = 0, + .y_addr_end = 2463, + .x_output_size = 820, + .y_output_size = 616, + .x_even_inc = 5, + .x_odd_inc = 3, + .y_even_inc = 5, + .y_odd_inc = 3, + .v_mode_add = 0, + .h_mode_add = 0, + .h_add_ave = 1, + }, + }, + + /* TWO_MP */ + { + .clk = { + .pre_pll_div = 1, + .pll_mult = 18, + .post_pll_div = 1, + .vt_pix_clk_div = 10, + .vt_sys_clk_div = 1, + }, + .mipi = { + .data_lanes = 2, + .ths_prepare = 4, + .ths_zero = 5, + .ths_settle_lower = 13, + .ths_settle_upper = 33, + }, + .frame = { + .frame_len_lines_min = 629, + .line_len_pck = 3440, + .x_addr_start = 0, + .x_addr_end = 3279, + .y_addr_start = 0, + .y_addr_end = 2463, + .x_output_size = 3280, + .y_output_size = 618, + .x_even_inc = 1, + .x_odd_inc = 1, + .y_even_inc = 5, + .y_odd_inc = 3, + .v_mode_add = 0, + .h_mode_add = 0, + .h_add_ave = 0, + }, + }, + + /* EIGHT_MP */ + { + .clk = { + .pre_pll_div = 1, + .pll_mult = 18, + .post_pll_div = 1, + .vt_pix_clk_div = 10, + .vt_sys_clk_div = 1, + }, + .mipi = { + .data_lanes = 2, + .ths_prepare = 4, + .ths_zero = 5, + .ths_settle_lower = 13, + .ths_settle_upper = 33, + }, + .frame = { + .frame_len_lines_min = 2510, + .line_len_pck = 3440, + .x_addr_start = 0, + .x_addr_end = 3279, + .y_addr_start = 0, + .y_addr_end = 2463, + .x_output_size = 3280, + .y_output_size = 2464, + .x_even_inc = 1, + .x_odd_inc = 1, + .y_even_inc = 1, + .y_odd_inc = 1, + .v_mode_add = 0, + .h_mode_add = 0, + .h_add_ave = 0, + }, + }, +}; + +static struct imx046_clock_freq current_clk; + +struct i2c_list { + struct i2c_msg *reg_list; + unsigned int list_size; +}; + +/** + * struct vcontrol - Video controls + * @v4l2_queryctrl: V4L2 VIDIOC_QUERYCTRL ioctl structure + * @current_value: current value of this control + */ +static struct vcontrol { + struct v4l2_queryctrl qc; + int current_value; +} imx046sensor_video_control[] = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = IMX046_MIN_EXPOSURE, + .maximum = IMX046_MAX_EXPOSURE, + .step = IMX046_EXPOSURE_STEP, + .default_value = IMX046_DEF_EXPOSURE, + }, + .current_value = IMX046_DEF_EXPOSURE, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Analog Gain", + .minimum = IMX046_MIN_GAIN, + .maximum = IMX046_MAX_GAIN, + .step = IMX046_GAIN_STEP, + .default_value = IMX046_DEF_GAIN, + }, + .current_value = IMX046_DEF_GAIN, + } +}; + +/** + * find_vctrl - Finds the requested ID in the video control structure array + * @id: ID of control to search the video control array for + * + * Returns the index of the requested ID from the control structure array + */ +static int +find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = (ARRAY_SIZE(imx046sensor_video_control) - 1); i >= 0; i--) + if (imx046sensor_video_control[i].qc.id == id) + break; + if (i < 0) + i = -EINVAL; + return i; +} + +/** + * imx046_read_reg - Read a value from a register in an imx046 sensor device + * @client: i2c driver client structure + * @data_length: length of data to be read + * @reg: register address / offset + * @val: stores the value that gets read + * + * Read a value from a register in an imx046 sensor device. + * The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int +imx046_read_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 *val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[4]; + + if (!client->adapter) + return -ENODEV; + if (data_length != I2C_8BIT && data_length != I2C_16BIT + && data_length != I2C_32BIT) + return -EINVAL; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + + /* Write addr - high byte goes out first */ + data[0] = (u8) (reg >> 8);; + data[1] = (u8) (reg & 0xff); + err = i2c_transfer(client->adapter, msg, 1); + + /* Read back data */ + if (err >= 0) { + msg->len = data_length; + msg->flags = I2C_M_RD; + err = i2c_transfer(client->adapter, msg, 1); + } + if (err >= 0) { + *val = 0; + /* high byte comes first */ + if (data_length == I2C_8BIT) + *val = data[0]; + else if (data_length == I2C_16BIT) + *val = data[1] + (data[0] << 8); + else + *val = data[3] + (data[2] << 8) + + (data[1] << 16) + (data[0] << 24); + return 0; + } + v4l_err(client, "read from offset 0x%x error %d", reg, err); + return err; +} + +/** + * Write a value to a register in imx046 sensor device. + * @client: i2c driver client structure. + * @reg: Address of the register to read value from. + * @val: Value to be written to a specific register. + * Returns zero if successful, or non-zero otherwise. + */ +static int imx046_write_reg(struct i2c_client *client, u16 reg, + u32 val, u16 data_length) +{ + int err = 0; + struct i2c_msg msg[1]; + unsigned char data[6]; + int retries = 0; + + if (!client->adapter) + return -ENODEV; + + if (data_length != I2C_8BIT && data_length != I2C_16BIT + && data_length != I2C_32BIT) + return -EINVAL; + +retry: + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = data_length+2; /* add address bytes */ + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + if (data_length == I2C_8BIT) { + data[2] = val & 0xff; + } else if (data_length == I2C_16BIT) { + data[2] = (val >> 8) & 0xff; + data[3] = val & 0xff; + } else { + data[2] = (val >> 24) & 0xff; + data[3] = (val >> 16) & 0xff; + data[4] = (val >> 8) & 0xff; + data[5] = val & 0xff; + } + + if (data_length == 1) + dev_dbg(&client->dev, "IMX046 Wrt:[0x%04X]=0x%02X\n", + reg, val); + else if (data_length == 2) + dev_dbg(&client->dev, "IMX046 Wrt:[0x%04X]=0x%04X\n", + reg, val); + + err = i2c_transfer(client->adapter, msg, 1); + + if (err >= 0) + return 0; + + if (retries <= 5) { + v4l_info(client, "Retrying I2C... %d", retries); + retries++; + mdelay(20); + goto retry; + } + + return err; +} + +/** + * Initialize a list of imx046 registers. + * The list of registers is terminated by the pair of values + * {OV3640_REG_TERM, OV3640_VAL_TERM}. + * @client: i2c driver client structure. + * @reglist[]: List of address of the registers to write data. + * Returns zero if successful, or non-zero otherwise. + */ +static int imx046_write_regs(struct i2c_client *client, + const struct imx046_reg reglist[]) +{ + int err = 0; + const struct imx046_reg *list = reglist; + + while (!((list->reg == I2C_REG_TERM) + && (list->val == I2C_VAL_TERM))) { + err = imx046_write_reg(client, list->reg, + list->val, list->length); + if (err) + return err; + list++; + } + return 0; +} + +/** + * imx046_find_size - Find the best match for a requested image capture size + * @width: requested image width in pixels + * @height: requested image height in pixels + * + * Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + * Since the available sizes are subsampled in the vertical direction only, + * the routine will find the size with a height that is equal to or less + * than the requested height. + */ +static enum imx046_image_size imx046_find_size(unsigned int width, + unsigned int height) +{ + enum imx046_image_size isize; + + for (isize = HALF_MP; isize <= EIGHT_MP; isize++) { + if ((imx046_sizes[isize].height >= height) && + (imx046_sizes[isize].width >= width)) { + break; + } + } + + printk(KERN_DEBUG "imx046_find_size: Req Size=%dx%d, " + "Calc Size=%dx%d\n", + width, height, (int)imx046_sizes[isize].width, + (int)imx046_sizes[isize].height); + + return isize; +} + +/** + * Set CSI2 Virtual ID. + * @client: i2c client driver structure + * @id: Virtual channel ID. + * + * Sets the channel ID which identifies data packets sent by this device + * on the CSI2 bus. + **/ +static int imx046_set_virtual_id(struct i2c_client *client, u32 id) +{ + return imx046_write_reg(client, IMX046_REG_CCP2_CHANNEL_ID, + (0x3 & id), I2C_8BIT); +} + +/** + * imx046_set_framerate - Sets framerate by adjusting frame_length_lines reg. + * @s: pointer to standard V4L2 device structure + * @fper: frame period numerator and denominator in seconds + * + * The maximum exposure time is also updated since it is affected by the + * frame rate. + **/ +static int imx046_set_framerate(struct v4l2_int_device *s, + struct v4l2_fract *fper) +{ + int err = 0; + u16 isize = isize_current; + u32 frame_length_lines, line_time_q8; + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + struct imx046_sensor_settings *ss; + + if ((fper->numerator == 0) || (fper->denominator == 0)) { + /* supply a default nominal_timeperframe */ + fper->numerator = 1; + fper->denominator = IMX046_DEF_FPS; + } + + sensor->fps = fper->denominator / fper->numerator; + if (sensor->fps < IMX046_MIN_FPS) { + sensor->fps = IMX046_MIN_FPS; + fper->numerator = 1; + fper->denominator = sensor->fps; + } else if (sensor->fps > IMX046_MAX_FPS) { + sensor->fps = IMX046_MAX_FPS; + fper->numerator = 1; + fper->denominator = sensor->fps; + } + + ss = &sensor_settings[isize_current]; + + line_time_q8 = ((u32)ss->frame.line_len_pck * 1000000) / + (current_clk.vt_pix_clk >> 8); /* usec's */ + + frame_length_lines = (((u32)fper->numerator * 1000000 * 256 / + fper->denominator)) / line_time_q8; + + /* Range check frame_length_lines */ + if (frame_length_lines > IMX046_MAX_FRAME_LENGTH_LINES) + frame_length_lines = IMX046_MAX_FRAME_LENGTH_LINES; + else if (frame_length_lines < ss->frame.frame_len_lines_min) + frame_length_lines = ss->frame.frame_len_lines_min; + + imx046_write_reg(client, IMX046_REG_FRAME_LEN_LINES, + frame_length_lines, I2C_16BIT); + + sensor_settings[isize].frame.frame_len_lines = frame_length_lines; + + /* Update max exposure time */ + max_exposure_time = (line_time_q8 * (frame_length_lines - 1)) >> 8; + + printk(KERN_DEBUG "IMX046 Set Framerate: fper=%d/%d, " + "frame_len_lines=%d, max_expT=%dus\n", fper->numerator, + fper->denominator, frame_length_lines, max_exposure_time); + + return err; +} + +/** + * imx046sensor_calc_xclk - Calculate the required xclk frequency + * + * Xclk is not determined from framerate for the IMX046 + */ +static unsigned long imx046sensor_calc_xclk(void) +{ + xclk_current = IMX046_XCLK_NOM_1; + + return xclk_current; +} + +/** + * Sets the correct orientation based on the sensor version. + * IU046F2-Z version=2 orientation=3 + * IU046F4-2D version>2 orientation=0 + */ +static int imx046_set_orientation(struct i2c_client *client, u32 ver) +{ + int err; + u8 orient; + + orient = (ver <= 0x2) ? 0x3 : 0x0; + err = imx046_write_reg(client, IMX046_REG_IMAGE_ORIENTATION, + orient, I2C_8BIT); + return err; +} + +/** + * imx046sensor_set_exposure_time - sets exposure time per input value + * @exp_time: exposure time to be set on device (in usec) + * @s: pointer to standard V4L2 device structure + * @lvc: pointer to V4L2 exposure entry in imx046sensor_video_controls array + * + * If the requested exposure time is within the allowed limits, the HW + * is configured to use the new exposure time, and the + * imx046sensor_video_control[] array is updated with the new current value. + * The function returns 0 upon success. Otherwise an error code is + * returned. + */ +int imx046sensor_set_exposure_time(u32 exp_time, struct v4l2_int_device *s, + struct vcontrol *lvc) +{ + int err = 0, i; + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + u16 coarse_int_time = 0; + u32 line_time_q8 = 0; + struct imx046_sensor_settings *ss; + + if ((current_power_state == V4L2_POWER_ON) || sensor->resuming) { + if (exp_time < min_exposure_time) { + v4l_err(client, "Exposure time %d us not within" + " the legal range.\n", exp_time); + v4l_err(client, "Exposure time must be between" + " %d us and %d us\n", + min_exposure_time, max_exposure_time); + exp_time = min_exposure_time; + } + + if (exp_time > max_exposure_time) { + v4l_err(client, "Exposure time %d us not within" + " the legal range.\n", exp_time); + v4l_err(client, "Exposure time must be between" + " %d us and %d us\n", + min_exposure_time, max_exposure_time); + exp_time = max_exposure_time; + } + + ss = &sensor_settings[isize_current]; + + line_time_q8 = + ((u32)ss->frame.line_len_pck * 1000000) / + (current_clk.vt_pix_clk >> 8); /* usec's */ + + coarse_int_time = ((exp_time * 256) + (line_time_q8 >> 1)) / + line_time_q8; + + if (coarse_int_time > ss->frame.frame_len_lines - 2) + coarse_int_time = ss->frame.frame_len_lines - 2; + + err = imx046_write_reg(client, IMX046_REG_COARSE_INT_TIME, + coarse_int_time, I2C_16BIT); + } + + if (err) { + v4l_err(client, "Error setting exposure time: %d", err); + } else { + i = find_vctrl(V4L2_CID_EXPOSURE); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = exp_time; + } + } + + return err; +} + +/** + * imx046sensor_set_gain - sets sensor analog gain per input value + * @gain: analog gain value to be set on device + * @s: pointer to standard V4L2 device structure + * @lvc: pointer to V4L2 analog gain entry in imx046sensor_video_control array + * + * If the requested analog gain is within the allowed limits, the HW + * is configured to use the new gain value, and the imx046sensor_video_control + * array is updated with the new current value. + * The function returns 0 upon success. Otherwise an error code is + * returned. + */ +int imx046sensor_set_gain(u16 lineargain, struct v4l2_int_device *s, + struct vcontrol *lvc) +{ + int err = 0, i; + u16 reg_gain = 0; + + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + + if (current_power_state == V4L2_POWER_ON || sensor->resuming) { + if (lineargain < IMX046_MIN_GAIN) { + lineargain = IMX046_MIN_GAIN; + v4l_err(client, "Gain out of legal range."); + } + if (lineargain > IMX046_MAX_GAIN) { + lineargain = IMX046_MAX_GAIN; + v4l_err(client, "Gain out of legal range."); + } + + /* Convert gain from linear to register value + - reg_gain = 256 - 256 / linear_gain + - do divide with rounding + */ + reg_gain = 256 - + ((((u32)(256 << 9) + (1 << 8)) / lineargain) + >> 1); + + err = imx046_write_reg(client, IMX046_REG_ANALOG_GAIN_GLOBAL, + reg_gain, I2C_16BIT); + } + + if (err) { + v4l_err(client, "Error setting analog gain: %d", err); + } else { + i = find_vctrl(V4L2_CID_GAIN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = lineargain; + } + } + + return err; +} + +/** + * imx046_update_clocks - calcs sensor clocks based on sensor settings. + * @isize: image size enum + */ +int imx046_update_clocks(u32 xclk, enum imx046_image_size isize) +{ + current_clk.vco_clk = + xclk * sensor_settings[isize].clk.pll_mult / + sensor_settings[isize].clk.pre_pll_div / + sensor_settings[isize].clk.post_pll_div; + + current_clk.vt_pix_clk = current_clk.vco_clk * 2 / + (sensor_settings[isize].clk.vt_pix_clk_div * + sensor_settings[isize].clk.vt_sys_clk_div); + + if (sensor_settings[isize].mipi.data_lanes == 2) + current_clk.mipi_clk = current_clk.vco_clk; + else + current_clk.mipi_clk = current_clk.vco_clk / 2; + + current_clk.ddr_clk = current_clk.mipi_clk / 2; + + printk(KERN_DEBUG "IMX046: xclk=%u, vco_clk=%u, " + "vt_pix_clk=%u, mipi_clk=%u, ddr_clk=%u\n", + xclk, current_clk.vco_clk, current_clk.vt_pix_clk, + current_clk.mipi_clk, current_clk.ddr_clk); + + return 0; +} + +/** + * imx046_setup_pll - initializes sensor PLL registers. + * @c: i2c client driver structure + * @isize: image size enum + */ +int imx046_setup_pll(struct i2c_client *client, enum imx046_image_size isize) +{ + u32 rgpltd_reg; + u32 rgpltd[3] = {2, 0, 1}; + + imx046_write_reg(client, IMX046_REG_PRE_PLL_CLK_DIV, + sensor_settings[isize].clk.pre_pll_div, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_PLL_MULTIPLIER, + sensor_settings[isize].clk.pll_mult, I2C_16BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_RGPLTD_RGCLKEN, + &rgpltd_reg); + rgpltd_reg &= ~RGPLTD_MASK; + rgpltd_reg |= rgpltd[sensor_settings[isize].clk.post_pll_div >> 1]; + imx046_write_reg(client, IMX046_REG_RGPLTD_RGCLKEN, + rgpltd_reg, I2C_8BIT); + + imx046_write_reg(client, IMX046_REG_VT_PIX_CLK_DIV, + sensor_settings[isize].clk.vt_pix_clk_div, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_VT_SYS_CLK_DIV, + sensor_settings[isize].clk.vt_sys_clk_div, I2C_16BIT); + + printk(KERN_DEBUG "IMX046: pre_pll_clk_div=%u, pll_mult=%u, " + "rgpltd=0x%x, vt_pix_clk_div=%u, vt_sys_clk_div=%u\n", + sensor_settings[isize].clk.pre_pll_div, + sensor_settings[isize].clk.pll_mult, rgpltd_reg, + sensor_settings[isize].clk.vt_pix_clk_div, + sensor_settings[isize].clk.vt_sys_clk_div); + + return 0; +} + +/** + * imx046_setup_mipi - initializes sensor & isp MIPI registers. + * @c: i2c client driver structure + * @isize: image size enum + */ +int imx046_setup_mipi(struct imx046_sensor *sensor, + enum imx046_image_size isize) +{ + struct i2c_client *client = sensor->i2c_client; + + /* NOTE: Make sure imx046_update_clocks is called 1st */ + + /* Enable MIPI */ + imx046_write_reg(client, IMX046_REG_RGOUTSEL1, 0x00, I2C_8BIT); + imx046_write_reg(client, IMX046_REG_TESTDI, 0x04, I2C_8BIT); + + /* Set sensor Mipi timing params */ + imx046_write_reg(client, IMX046_REG_RGTHSTRAIL, 0x06, I2C_8BIT); + + imx046_write_reg(client, IMX046_REG_RGTHSPREPARE, + sensor_settings[isize].mipi.ths_prepare, I2C_8BIT); + + imx046_write_reg(client, IMX046_REG_RGTHSZERO, + sensor_settings[isize].mipi.ths_zero, I2C_8BIT); + + /* Set number of lanes in sensor */ + if (sensor_settings[isize].mipi.data_lanes == 2) + imx046_write_reg(client, IMX046_REG_RGLANESEL, 0x00, I2C_8BIT); + else + imx046_write_reg(client, IMX046_REG_RGLANESEL, 0x01, I2C_8BIT); + + /* Set number of lanes in isp */ + sensor->pdata->csi2_lane_count(sensor_settings[isize].mipi.data_lanes); + + /* Send settings to ISP-CSI2 Receiver PHY */ + sensor->pdata->csi2_calc_phy_cfg0(current_clk.mipi_clk, + sensor_settings[isize].mipi.ths_settle_lower, + sensor_settings[isize].mipi.ths_settle_upper); + + /* Dump some registers for debug purposes */ + printk(KERN_DEBUG "imx:THSPREPARE=0x%02X\n", + sensor_settings[isize].mipi.ths_prepare); + printk(KERN_DEBUG "imx:THSZERO=0x%02X\n", + sensor_settings[isize].mipi.ths_zero); + printk(KERN_DEBUG "imx:LANESEL=0x%02X\n", + (sensor_settings[isize].mipi.data_lanes == 2) ? 0 : 1); + + return 0; +} + +/** + * imx046_configure_frame - initializes image frame registers + * @c: i2c client driver structure + * @isize: image size enum + */ +int imx046_configure_frame(struct i2c_client *client, + enum imx046_image_size isize) +{ + u32 val; + + imx046_write_reg(client, IMX046_REG_FRAME_LEN_LINES, + sensor_settings[isize].frame.frame_len_lines_min, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_LINE_LEN_PCK, + sensor_settings[isize].frame.line_len_pck, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_ADDR_START, + sensor_settings[isize].frame.x_addr_start, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_ADDR_END, + sensor_settings[isize].frame.x_addr_end, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_ADDR_START, + sensor_settings[isize].frame.y_addr_start, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_ADDR_END, + sensor_settings[isize].frame.y_addr_end, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_OUTPUT_SIZE, + sensor_settings[isize].frame.x_output_size, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_OUTPUT_SIZE, + sensor_settings[isize].frame.y_output_size, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_EVEN_INC, + sensor_settings[isize].frame.x_even_inc, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_X_ODD_INC, + sensor_settings[isize].frame.x_odd_inc, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_EVEN_INC, + sensor_settings[isize].frame.y_even_inc, I2C_16BIT); + + imx046_write_reg(client, IMX046_REG_Y_ODD_INC, + sensor_settings[isize].frame.y_odd_inc, I2C_16BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_PGACUR_VMODEADD, &val); + val &= ~VMODEADD_MASK; + val |= sensor_settings[isize].frame.v_mode_add << VMODEADD_SHIFT; + imx046_write_reg(client, IMX046_REG_PGACUR_VMODEADD, val, I2C_8BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_HMODEADD, &val); + val &= ~HMODEADD_MASK; + val |= sensor_settings[isize].frame.h_mode_add << HMODEADD_SHIFT; + imx046_write_reg(client, IMX046_REG_HMODEADD, val, I2C_8BIT); + + imx046_read_reg(client, I2C_8BIT, IMX046_REG_HADDAVE, &val); + val &= ~HADDAVE_MASK; + val |= sensor_settings[isize].frame.h_add_ave << HADDAVE_SHIFT; + imx046_write_reg(client, IMX046_REG_HADDAVE, val, I2C_8BIT); + + return 0; +} + +/** + * imx046_configure - Configure the imx046 for the specified image mode + * @s: pointer to standard V4L2 device structure + * + * Configure the imx046 for a specified image size, pixel format, and frame + * period. xclk is the frequency (in Hz) of the xclk input to the imx046. + * fper is the frame period (in seconds) expressed as a fraction. + * Returns zero if successful, or non-zero otherwise. + * The actual frame period is returned in fper. + */ +static int imx046_configure(struct v4l2_int_device *s) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &sensor->pix; + struct i2c_client *client = sensor->i2c_client; + enum imx046_image_size isize; + int err, i; + struct vcontrol *lvc = NULL; + + isize = imx046_find_size(pix->width, pix->height); + isize_current = isize; + + err = imx046_write_reg(client, IMX046_REG_SW_RESET, 0x01, I2C_8BIT); + mdelay(5); + + imx046_write_regs(client, initial_list); + + imx046_update_clocks(xclk_current, isize); + imx046_setup_pll(client, isize); + + imx046_setup_mipi(sensor, isize); + + /* configure image size and pixel format */ + imx046_configure_frame(client, isize); + + /* Setting of frame rate */ + err = imx046_set_framerate(s, &sensor->timeperframe); + + imx046_set_orientation(client, sensor->ver); + + sensor->pdata->cfg_interface_bridge(0x00); + sensor->pdata->csi2_cfg_vp_out_ctrl(2); + sensor->pdata->csi2_ctrl_update(false); + + sensor->pdata->csi2_cfg_virtual_id(0, IMX046_CSI2_VIRTUAL_ID); + sensor->pdata->csi2_ctx_update(0, false); + imx046_set_virtual_id(client, IMX046_CSI2_VIRTUAL_ID); + + /* Set initial exposure and gain */ + i = find_vctrl(V4L2_CID_EXPOSURE); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + imx046sensor_set_exposure_time(lvc->current_value, + sensor->v4l2_int_device, lvc); + } + + i = find_vctrl(V4L2_CID_GAIN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + imx046sensor_set_gain(lvc->current_value, + sensor->v4l2_int_device, lvc); + } + + /* configure streaming ON */ + err = imx046_write_reg(client, IMX046_REG_MODE_SELECT, 0x01, I2C_8BIT); + mdelay(1); + + return err; +} + +/** + * imx046_detect - Detect if an imx046 is present, and if so which revision + * @client: pointer to the i2c client driver structure + * + * Detect if an imx046 is present, and if so which revision. + * A device is considered to be detected if the manufacturer ID (MIDH and MIDL) + * and the product ID (PID) registers match the expected values. + * Any value of the version ID (VER) register is accepted. + * Returns a negative error number if no device is detected, or the + * non-negative value of the version ID register if a device is detected. + */ +static int +imx046_detect(struct i2c_client *client) +{ + u32 model_id, mfr_id, rev; + struct imx046_sensor *sensor; + + if (!client) + return -ENODEV; + + sensor = i2c_get_clientdata(client); + + if (imx046_read_reg(client, I2C_16BIT, IMX046_REG_MODEL_ID, &model_id)) + return -ENODEV; + if (imx046_read_reg(client, I2C_8BIT, IMX046_REG_MFR_ID, &mfr_id)) + return -ENODEV; + if (imx046_read_reg(client, I2C_8BIT, IMX046_REG_REV_NUMBER, &rev)) + return -ENODEV; + + v4l_info(client, "model id detected 0x%x mfr 0x%x, rev# 0x%x\n", + model_id, mfr_id, rev); + if ((model_id != IMX046_MOD_ID) || (mfr_id != IMX046_MFR_ID)) { + /* We didn't read the values we expected, so + * this must not be an IMX046. + */ + v4l_warn(client, "model id mismatch 0x%x mfr 0x%x\n", + model_id, mfr_id); + + return -ENODEV; + } + return rev; +} + +/** + * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the imx046sensor_video_control[] array. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int ioctl_queryctrl(struct v4l2_int_device *s, + struct v4l2_queryctrl *qc) +{ + int i; + + i = find_vctrl(qc->id); + if (i == -EINVAL) + qc->flags = V4L2_CTRL_FLAG_DISABLED; + + if (i < 0) + return -EINVAL; + + *qc = imx046sensor_video_control[i].qc; + return 0; +} + +/** + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the imx046sensor_video_control[] array. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + struct vcontrol *lvc; + int i; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &imx046sensor_video_control[i]; + + switch (vc->id) { + case V4L2_CID_EXPOSURE: + vc->value = lvc->current_value; + break; + case V4L2_CID_GAIN: + vc->value = lvc->current_value; + break; + } + + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the imx046sensor_video_control[] array). + * Otherwise, * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, + struct v4l2_control *vc) +{ + int retval = -EINVAL; + int i; + struct vcontrol *lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + lvc = &imx046sensor_video_control[i]; + + switch (vc->id) { + case V4L2_CID_EXPOSURE: + retval = imx046sensor_set_exposure_time(vc->value, s, lvc); + break; + case V4L2_CID_GAIN: + retval = imx046sensor_set_gain(vc->value, s, lvc); + break; + } + + return retval; +} + +/** + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + default: + return -EINVAL; + } + + fmt->flags = imx046_formats[index].flags; + strlcpy(fmt->description, imx046_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = imx046_formats[index].pixelformat; + + return 0; +} + +/** + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + enum imx046_image_size isize; + int ifmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct imx046_sensor *sensor = s->priv; + struct v4l2_pix_format *pix2 = &sensor->pix; + + isize = imx046_find_size(pix->width, pix->height); + + pix->width = imx046_sizes[isize].width; + pix->height = imx046_sizes[isize].height; + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (pix->pixelformat == imx046_formats[ifmt].pixelformat) + break; + } + if (ifmt == NUM_CAPTURE_FORMATS) + ifmt = 0; + pix->pixelformat = imx046_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + pix->colorspace = V4L2_COLORSPACE_SRGB; + *pix2 = *pix; + return 0; +} + +/** + * ioctl_s_fmt_cap - V4L2 sensor interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int ioctl_s_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + int rval; + + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + else + sensor->pix = *pix; + + + return rval; +} + +/** + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct imx046_sensor *sensor = s->priv; + f->fmt.pix = sensor->pix; + + return 0; +} + +/** + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = sensor->timeperframe; + + return 0; +} + +/** + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, + struct v4l2_streamparm *a) +{ + struct imx046_sensor *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + + sensor->timeperframe = *timeperframe; + imx046sensor_calc_xclk(); + *timeperframe = sensor->timeperframe; + + return 0; +} + + +/** + * ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold sensor's private data address + * + * Returns device's (sensor's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct imx046_sensor *sensor = s->priv; + + return sensor->pdata->priv_data_set(p); +} + +/** + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +{ + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + struct omap34xxcam_hw_config hw_config; + struct vcontrol *lvc; + int rval, i; + + rval = ioctl_g_priv(s, &hw_config); + if (rval) { + v4l_err(client, "Unable to get hw params\n"); + return rval; + } + + if ((on == V4L2_POWER_STANDBY) && (sensor->state == SENSOR_DETECTED)) { + /* imx046_write_regs(c, stream_off_list, + I2C_STREAM_OFF_LIST_SIZE); */ + } + + if (on != V4L2_POWER_ON) + sensor->pdata->set_xclk(0, hw_config.u.sensor.xclk); + else + sensor->pdata->set_xclk(xclk_current, hw_config.u.sensor.xclk); + + rval = sensor->pdata->power_set(on); + if (rval < 0) { + v4l_err(client, "Unable to set the power state: " + IMX046_DRIVER_NAME " sensor\n"); + sensor->pdata->set_xclk(0, hw_config.u.sensor.xclk); + return rval; + } + + if ((current_power_state == V4L2_POWER_STANDBY) && + (on == V4L2_POWER_ON) && + (sensor->state == SENSOR_DETECTED)) { + sensor->resuming = true; + imx046_configure(s); + } + + if ((on == V4L2_POWER_ON) && (sensor->state == SENSOR_NOT_DETECTED)) { + rval = imx046_detect(client); + if (rval < 0) { + v4l_err(client, "Unable to detect " + IMX046_DRIVER_NAME " sensor\n"); + sensor->state = SENSOR_NOT_DETECTED; + return rval; + } + sensor->state = SENSOR_DETECTED; + sensor->ver = rval; + v4l_info(client, IMX046_DRIVER_NAME + " chip version 0x%02x detected\n", sensor->ver); + } + + if (on == V4L2_POWER_OFF) { + /* Reset defaults for controls */ + i = find_vctrl(V4L2_CID_GAIN); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = IMX046_DEF_GAIN; + } + i = find_vctrl(V4L2_CID_EXPOSURE); + if (i >= 0) { + lvc = &imx046sensor_video_control[i]; + lvc->current_value = IMX046_DEF_EXPOSURE; + } + } + + sensor->resuming = false; + current_power_state = on; + return 0; +} + +/** + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the sensor device (call imx046_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * imx046 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct imx046_sensor *sensor = s->priv; + struct i2c_client *client = sensor->i2c_client; + int err; + + err = imx046_detect(client); + if (err < 0) { + v4l_err(client, "Unable to detect " IMX046_DRIVER_NAME + " sensor\n"); + return err; + } + + sensor->ver = err; + v4l_info(client, IMX046_DRIVER_NAME " chip version " + "0x%02x detected\n", sensor->ver); + + return 0; +} + +/** + * ioctl_enum_framesizes - V4L2 sensor if handler for vidioc_int_enum_framesizes + * @s: pointer to standard V4L2 device structure + * @frms: pointer to standard V4L2 framesizes enumeration structure + * + * Returns possible framesizes depending on choosen pixel format + **/ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *frms) +{ + int ifmt; + + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (frms->pixel_format == imx046_formats[ifmt].pixelformat) + break; + } + /* Is requested pixelformat not found on sensor? */ + if (ifmt == NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Check that the index we are being asked for is not + out of bounds. */ + if (frms->index >= ARRAY_SIZE(imx046_sizes)) + return -EINVAL; + + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = imx046_sizes[frms->index].width; + frms->discrete.height = imx046_sizes[frms->index].height; + + return 0; +} + +const struct v4l2_fract imx046_frameintervals[] = { + { .numerator = 3, .denominator = 30 }, + { .numerator = 1, .denominator = 30 }, +}; + +static int ioctl_enum_frameintervals(struct v4l2_int_device *s, + struct v4l2_frmivalenum *frmi) +{ + int ifmt; + + /* Check that the requested format is one we support */ + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (frmi->pixel_format == imx046_formats[ifmt].pixelformat) + break; + } + + if (ifmt == NUM_CAPTURE_FORMATS) + return -EINVAL; + + /* Check that the index we are being asked for is not + out of bounds. */ + if (frmi->index >= ARRAY_SIZE(imx046_frameintervals)) + return -EINVAL; + + /* Make sure that the 8MP size reports a max of 10fps */ + if (frmi->width == 3280 && frmi->height == 2464) { + if (frmi->index != 0) + return -EINVAL; + } + + frmi->type = V4L2_FRMIVAL_TYPE_DISCRETE; + frmi->discrete.numerator = + imx046_frameintervals[frmi->index].numerator; + frmi->discrete.denominator = + imx046_frameintervals[frmi->index].denominator; + + return 0; +} + +static struct v4l2_int_ioctl_desc imx046_ioctl_desc[] = { + { .num = vidioc_int_enum_framesizes_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_framesizes}, + { .num = vidioc_int_enum_frameintervals_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_frameintervals}, + { .num = vidioc_int_dev_init_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_init}, + { .num = vidioc_int_dev_exit_num, + .func = (v4l2_int_ioctl_func *)ioctl_dev_exit}, + { .num = vidioc_int_s_power_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_power }, + { .num = vidioc_int_g_priv_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_priv }, + { .num = vidioc_int_init_num, + .func = (v4l2_int_ioctl_func *)ioctl_init }, + { .num = vidioc_int_enum_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, + { .num = vidioc_int_try_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, + { .num = vidioc_int_g_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, + { .num = vidioc_int_s_fmt_cap_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, + { .num = vidioc_int_g_parm_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_parm }, + { .num = vidioc_int_s_parm_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_parm }, + { .num = vidioc_int_queryctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_queryctrl }, + { .num = vidioc_int_g_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { .num = vidioc_int_s_ctrl_num, + .func = (v4l2_int_ioctl_func *)ioctl_s_ctrl }, +}; + +static struct v4l2_int_slave imx046_slave = { + .ioctls = imx046_ioctl_desc, + .num_ioctls = ARRAY_SIZE(imx046_ioctl_desc), +}; + +static struct v4l2_int_device imx046_int_device = { + .module = THIS_MODULE, + .name = IMX046_DRIVER_NAME, + .priv = &imx046, + .type = v4l2_int_type_slave, + .u = { + .slave = &imx046_slave, + }, +}; + +/** + * imx046_probe - sensor driver i2c probe handler + * @client: i2c driver client device structure + * + * Register sensor as an i2c client device and V4L2 + * device. + */ +static int __devinit imx046_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct imx046_sensor *sensor = &imx046; + int err; + + if (i2c_get_clientdata(client)) + return -EBUSY; + + sensor->pdata = client->dev.platform_data; + + if (!sensor->pdata) { + v4l_err(client, "no platform data?\n"); + return -ENODEV; + } + + sensor->v4l2_int_device = &imx046_int_device; + sensor->i2c_client = client; + + i2c_set_clientdata(client, sensor); + + /* Make the default capture format QCIF V4L2_PIX_FMT_SGRBG10 */ + sensor->pix.width = IMX046_IMAGE_WIDTH_MAX; + sensor->pix.height = IMX046_IMAGE_HEIGHT_MAX; + sensor->pix.pixelformat = V4L2_PIX_FMT_SGRBG10; + + err = v4l2_int_device_register(sensor->v4l2_int_device); + if (err) + i2c_set_clientdata(client, NULL); + + return 0; +} + +/** + * imx046_remove - sensor driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister sensor as an i2c client device and V4L2 + * device. Complement of imx046_probe(). + */ +static int __exit +imx046_remove(struct i2c_client *client) +{ + struct imx046_sensor *sensor = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(sensor->v4l2_int_device); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id imx046_id[] = { + { IMX046_DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, imx046_id); + +static struct i2c_driver imx046sensor_i2c_driver = { + .driver = { + .name = IMX046_DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = imx046_probe, + .remove = __exit_p(imx046_remove), + .id_table = imx046_id, +}; + +static struct imx046_sensor imx046 = { + .timeperframe = { + .numerator = 1, + .denominator = 30, + }, + .state = SENSOR_NOT_DETECTED, +}; + +/** + * imx046sensor_init - sensor driver module_init handler + * + * Registers driver as an i2c client driver. Returns 0 on success, + * error code otherwise. + */ +static int __init imx046sensor_init(void) +{ + int err; + + err = i2c_add_driver(&imx046sensor_i2c_driver); + if (err) { + printk(KERN_ERR "Failed to register" IMX046_DRIVER_NAME ".\n"); + return err; + } + return 0; +} +late_initcall(imx046sensor_init); + +/** + * imx046sensor_cleanup - sensor driver module_exit handler + * + * Unregisters/deletes driver as an i2c client driver. + * Complement of imx046sensor_init. + */ +static void __exit imx046sensor_cleanup(void) +{ + i2c_del_driver(&imx046sensor_i2c_driver); +} +module_exit(imx046sensor_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("imx046 camera sensor driver"); Index: omapzoom04/drivers/media/video/imx046.h =================================================================== --- /dev/null +++ omapzoom04/drivers/media/video/imx046.h @@ -0,0 +1,326 @@ +/* + * drivers/media/video/imx046.h + * + * Register definitions for the IMX046 Sensor. + * + * Leverage MT9P012.h + * + * Copyright (C) 2008 Hewlett Packard. + * + * 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. + */ + +#ifndef IMX046_H +#define IMX046_H + +#define IMX046_I2C_ADDR 0x1A + +/* The ID values we are looking for */ +#define IMX046_MOD_ID 0x0046 +#define IMX046_MFR_ID 0x000B + +#define VAUX_2_8_V 0x09 +#define VAUX_1_8_V 0x05 +#define VAUX_DEV_GRP_P1 0x20 +#define VAUX_DEV_GRP_NONE 0x00 + +/* IMX046 has 8/16/32 I2C registers */ +#define I2C_8BIT 1 +#define I2C_16BIT 2 +#define I2C_32BIT 4 + +/* Terminating list entry for reg */ +#define I2C_REG_TERM 0xFFFF +/* Terminating list entry for val */ +#define I2C_VAL_TERM 0xFFFFFFFF +/* Terminating list entry for len */ +#define I2C_LEN_TERM 0xFFFF + +/* terminating token for reg list */ +#define IMX046_TOK_TERM 0xFF + +/* delay token for reg list */ +#define IMX046_TOK_DELAY 100 + +/* Sensor specific GPIO signals */ +#define IMX046_RESET_GPIO 98 +#define IMX046_STANDBY_GPIO 58 + +/* CSI2 Virtual ID */ +#define IMX046_CSI2_VIRTUAL_ID 0x0 + +#define IMX046_CLKRC 0x11 + +/* Average black level */ +#define IMX046_BLACK_LEVEL_AVG 64 + +/* Used registers */ +#define IMX046_REG_MODEL_ID 0x0000 +#define IMX046_REG_REV_NUMBER 0x0002 +#define IMX046_REG_MFR_ID 0x0003 + +#define IMX046_REG_MODE_SELECT 0x0100 +#define IMX046_REG_IMAGE_ORIENTATION 0x0101 +#define IMX046_REG_SW_RESET 0x0103 +#define IMX046_REG_GROUPED_PAR_HOLD 0x0104 +#define IMX046_REG_CCP2_CHANNEL_ID 0x0110 + +#define IMX046_REG_FINE_INT_TIME 0x0200 +#define IMX046_REG_COARSE_INT_TIME 0x0202 + +#define IMX046_REG_ANALOG_GAIN_GLOBAL 0x0204 +#define IMX046_REG_ANALOG_GAIN_GREENR 0x0206 +#define IMX046_REG_ANALOG_GAIN_RED 0x0208 +#define IMX046_REG_ANALOG_GAIN_BLUE 0x020A +#define IMX046_REG_ANALOG_GAIN_GREENB 0x020C +#define IMX046_REG_DIGITAL_GAIN_GREENR 0x020E +#define IMX046_REG_DIGITAL_GAIN_RED 0x0210 +#define IMX046_REG_DIGITAL_GAIN_BLUE 0x0212 +#define IMX046_REG_DIGITAL_GAIN_GREENB 0x0214 + +#define IMX046_REG_VT_PIX_CLK_DIV 0x0300 +#define IMX046_REG_VT_SYS_CLK_DIV 0x0302 +#define IMX046_REG_PRE_PLL_CLK_DIV 0x0304 +#define IMX046_REG_PLL_MULTIPLIER 0x0306 +#define IMX046_REG_OP_PIX_CLK_DIV 0x0308 +#define IMX046_REG_OP_SYS_CLK_DIV 0x030A + +#define IMX046_REG_FRAME_LEN_LINES 0x0340 +#define IMX046_REG_LINE_LEN_PCK 0x0342 + +#define IMX046_REG_X_ADDR_START 0x0344 +#define IMX046_REG_Y_ADDR_START 0x0346 +#define IMX046_REG_X_ADDR_END 0x0348 +#define IMX046_REG_Y_ADDR_END 0x034A +#define IMX046_REG_X_OUTPUT_SIZE 0x034C +#define IMX046_REG_Y_OUTPUT_SIZE 0x034E +#define IMX046_REG_X_EVEN_INC 0x0380 +#define IMX046_REG_X_ODD_INC 0x0382 +#define IMX046_REG_Y_EVEN_INC 0x0384 +#define IMX046_REG_Y_ODD_INC 0x0386 + +#define IMX046_REG_HMODEADD 0x3001 +#define HMODEADD_SHIFT 7 +#define HMODEADD_MASK (0x1 << HMODEADD_SHIFT) +#define IMX046_REG_OPB_CTRL 0x300C +#define IMX046_REG_Y_OPBADDR_START_DI 0x3014 +#define IMX046_REG_Y_OPBADDR_END_DI 0x3015 +#define IMX046_REG_PGACUR_VMODEADD 0x3016 +#define VMODEADD_SHIFT 6 +#define VMODEADD_MASK (0x1 << VMODEADD_SHIFT) +#define IMX046_REG_CHCODE_OUTCHSINGLE 0x3017 +#define IMX046_REG_OUTIF 0x301C +#define IMX046_REG_RGPLTD_RGCLKEN 0x3022 +#define RGPLTD_MASK 0x3 +#define IMX046_REG_RGPOF_RGPOFV2 0x3031 +#define IMX046_REG_CPCKAUTOEN 0x3040 +#define IMX046_REG_RGCPVFB 0x3041 +#define IMX046_REG_RGAZPDRV 0x3051 +#define IMX046_REG_RGAZTEST 0x3053 +#define IMX046_REG_RGVSUNLV 0x3055 +#define IMX046_REG_CLPOWER 0x3060 +#define IMX046_REG_CLPOWERSP 0x3065 +#define IMX046_REG_ACLDIRV_TVADDCLP 0x30AA +#define IMX046_REG_TESTDI 0x30E5 +#define IMX046_REG_HADDAVE 0x30E8 +#define HADDAVE_SHIFT 7 +#define HADDAVE_MASK (0x1 << HADDAVE_SHIFT) + +#define IMX046_REG_RAW10CH2V2P_LO 0x31A4 +#define IMX046_REG_RAW10CH2V2D_LO 0x31A6 +#define IMX046_REG_COMP8CH1V2P_LO 0x31AC +#define IMX046_REG_COMP8CH1V2D_LO 0x31AE +#define IMX046_REG_RAW10CH1V2P_LO 0x31B4 +#define IMX046_REG_RAW10CH1V2D_LO 0x31B6 + +#define IMX046_REG_RGOUTSEL1 0x3300 +#define IMX046_REG_RGLANESEL 0x3301 +#define IMX046_REG_RGTLPX 0x3304 +#define IMX046_REG_RGTCLKPREPARE 0x3305 +#define IMX046_REG_RGTCLKZERO 0x3306 +#define IMX046_REG_RGTCLKPRE 0x3307 +#define IMX046_REG_RGTCLKPOST 0x3308 +#define IMX046_REG_RGTCLKTRAIL 0x3309 +#define IMX046_REG_RGTHSEXIT 0x330A +#define IMX046_REG_RGTHSPREPARE 0x330B +#define IMX046_REG_RGTHSZERO 0x330C +#define IMX046_REG_RGTHSTRAIL 0x330D + + +/* + * The nominal xclk input frequency of the IMX046 is 18MHz, maximum + * frequency is 45MHz, and minimum frequency is 6MHz. + */ +#define IMX046_XCLK_MIN 6000000 +#define IMX046_XCLK_MAX 45000000 +#define IMX046_XCLK_NOM_1 18000000 +#define IMX046_XCLK_NOM_2 18000000 + +/* FPS Capabilities */ +#define IMX046_MIN_FPS 7 +#define IMX046_DEF_FPS 15 +#define IMX046_MAX_FPS 30 + +#define I2C_RETRY_COUNT 5 + +/* Still capture 8 MP */ +#define IMX046_IMAGE_WIDTH_MAX 3280 +#define IMX046_IMAGE_HEIGHT_MAX 2464 + +/* Analog gain values */ +#define IMX046_MIN_GAIN (256*1) +#define IMX046_MAX_GAIN (256*8) +#define IMX046_DEF_GAIN (256*2) +#define IMX046_GAIN_STEP 0x1 + +/* Exposure time values */ +#define IMX046_MIN_EXPOSURE 250 +#define IMX046_MAX_EXPOSURE 128000 +#define IMX046_DEF_EXPOSURE 33000 +#define IMX046_EXPOSURE_STEP 50 + +#define IMX046_MAX_FRAME_LENGTH_LINES 0xFFFF + +#define SENSOR_DETECTED 1 +#define SENSOR_NOT_DETECTED 0 + +/** + * struct imx046_reg - imx046 register format + * @reg: 16-bit offset to register + * @val: 8/16/32-bit register value + * @length: length of the register + * + * Define a structure for IMX046 register initialization values + */ +struct imx046_reg { + u16 reg; + u32 val; + u16 length; +}; + +enum imx046_image_size { + HALF_MP, + TWO_MP, + EIGHT_MP +}; + +/** + * struct imx046_capture_size - image capture size information + * @width: image width in pixels + * @height: image height in pixels + */ +struct imx046_capture_size { + unsigned long width; + unsigned long height; +}; + +/** + * struct imx046_platform_data - platform data values and access functions + * @power_set: Power state access function, zero is off, non-zero is on. + * @default_regs: Default registers written after power-on or reset. + * @ifparm: Interface parameters access function + * @priv_data_set: device private data (pointer) access function + */ +struct imx046_platform_data { + int (*power_set)(enum v4l2_power power); + const struct imx046_reg *default_regs; + int (*ifparm)(struct v4l2_ifparm *p); + int (*priv_data_set)(void *); + u32 (*set_xclk)(u32 xclk, u8 xclksel); + int (*cfg_interface_bridge)(u32); + int (*csi2_lane_count)(int count); + int (*csi2_cfg_vp_out_ctrl)(u8 vp_out_ctrl); + int (*csi2_ctrl_update)(bool); + int (*csi2_cfg_virtual_id)(u8 ctx, u8 id); + int (*csi2_ctx_update)(u8 ctx, bool); + int (*csi2_calc_phy_cfg0)(u32, u32, u32); +}; + +/** + * struct struct clk_settings - struct for storage of sensor + * clock settings + */ +struct imx046_clk_settings { + u16 pre_pll_div; + u16 pll_mult; + u16 post_pll_div; + u16 vt_pix_clk_div; + u16 vt_sys_clk_div; +}; + +/** + * struct struct mipi_settings - struct for storage of sensor + * mipi settings + */ +struct imx046_mipi_settings { + u16 data_lanes; + u16 ths_prepare; + u16 ths_zero; + u16 ths_settle_lower; + u16 ths_settle_upper; +}; + +/** + * struct struct frame_settings - struct for storage of sensor + * frame settings + */ +struct imx046_frame_settings { + u16 frame_len_lines_min; + u16 frame_len_lines; + u16 line_len_pck; + u16 x_addr_start; + u16 x_addr_end; + u16 y_addr_start; + u16 y_addr_end; + u16 x_output_size; + u16 y_output_size; + u16 x_even_inc; + u16 x_odd_inc; + u16 y_even_inc; + u16 y_odd_inc; + u16 v_mode_add; + u16 h_mode_add; + u16 h_add_ave; +}; + +/** + * struct struct imx046_sensor_settings - struct for storage of + * sensor settings. + */ +struct imx046_sensor_settings { + struct imx046_clk_settings clk; + struct imx046_mipi_settings mipi; + struct imx046_frame_settings frame; +}; + +/** + * struct struct imx046_clock_freq - struct for storage of sensor + * clock frequencies + */ +struct imx046_clock_freq { + u32 vco_clk; + u32 mipi_clk; + u32 ddr_clk; + u32 vt_pix_clk; +}; + +/** + * Array of image sizes supported by IMX046. These must be ordered from + * smallest image size to largest. + */ +const static struct imx046_capture_size imx046_sizes[] = { + { 820, 616 }, /* 0.5Mp - 4X Horizontal & Vertical Elim. */ + { 3280, 616 }, /* 2Mp - 4X Vertical Elim. */ + { 3280, 2464}, /* 8MP - Full Resolution */ +}; + +/* PLL settings for imx046 */ +enum imx046_pll_type { + PLL_0_5MP = 0, + PLL_2MP, + PLL_8MP, +}; + +#endif /* ifndef IMX046_H */