From 1a0e23a15528eb2715c13706fe6d74a32640cbb2 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Wed, 26 Sep 2018 03:48:08 +0200 Subject: [PATCH 058/122] Sony IMX219 driver Signed-off-by: Sergey Lapin --- .../devicetree/bindings/media/i2c/imx219.txt | 35 + drivers/media/i2c/soc_camera/Kconfig | 6 + drivers/media/i2c/soc_camera/Makefile | 1 + drivers/media/i2c/soc_camera/imx219.c | 1026 ++++++++++++++++++++ 4 files changed, 1068 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.txt create mode 100644 drivers/media/i2c/soc_camera/imx219.c diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.txt b/Documentation/devicetree/bindings/media/i2c/imx219.txt new file mode 100644 index 0000000..bc2ccf3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/imx219.txt @@ -0,0 +1,35 @@ +Sony IMX219 raw image sensor +---------------------------- + +IMX219 is a raw image sensor with MIPI CSI-2 image data interface +and CCI (I2C compatible) control bus. + +Required properties: + +- compatible : "sony,imx219". +- reg : I2C slave address of the sensor. + +The common video interfaces bindings (see video-interfaces.txt) should be +used to specify link to the image data receiver. The IMX219 device node +should contain one 'port' child node with an 'endpoint' subnode. + +Endpoint node mandatory properties: + +- remote-endpoint: A phandle to the bus receiver's endpoint node. + +Example: + + ... + imx219_cam: imx219@10 { + compatible = "sony,imx219"; + reg = <0x10>; + + port@0 { + rpi_camera_in: endpoint { + clock-lanes = <0>; + data-lanes = <1 2>; + remote-endpoint = <&vin4ep0>; + }; + }; + }; + }; diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 1db3c6b..627d8d9 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -100,3 +100,9 @@ config SOC_CAMERA_OV106XX depends on SOC_CAMERA && I2C help This is a runtime detected GMSL/FPDLink3 sensors driver + +config SOC_CAMERA_IMX219 + tristate "imx219 camera support" + depends on SOC_CAMERA && I2C + help + This is Sony IMX219 driver diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 0d4242e..eed6e24 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o obj-$(CONFIG_SOC_CAMERA_MAX9286) += max9286.o obj-$(CONFIG_SOC_CAMERA_TI9X4) += ti9x4.o obj-$(CONFIG_SOC_CAMERA_OV106XX) += ov106xx.o +obj-$(CONFIG_SOC_CAMERA_IMX219) += imx219.o diff --git a/drivers/media/i2c/soc_camera/imx219.c b/drivers/media/i2c/soc_camera/imx219.c new file mode 100644 index 0000000..f0ce3f7 --- /dev/null +++ b/drivers/media/i2c/soc_camera/imx219.c @@ -0,0 +1,1026 @@ +/* + * V4L2 driver for Sony IMX219 cameras. + * + * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver + * Copyright (C) 2011 Sylwester Nawrocki + * + * Based on Omnivision OV7670 Camera Driver + * Copyright (C) 2006-7 Jonathan Corbet + * + * Based on Omnivision OV5647 image sensor driver + * Copyright (C) 2016, Synopsys, Inc. + * + * Copyright (C) 2017-2018 Cogent Embedded, Inc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX219_REG_CHIPID_H 0x0 +#define IMX219_REG_CHIPID_L 0x1 + +#define REG_DLY 0xffff + +#define CSI_STBY_ON 1 +#define CSI_STBY_OFF 0 + +/* #define TEST_PATTERN */ + +struct regval_list { + u16 addr; + u8 data; +}; + +enum power_seq_cmd { + CSI_SUBDEV_PWR_OFF = 0x00, + CSI_SUBDEV_PWR_ON = 0x01, +}; + +struct sensor_format_struct { + __u8 *desc; + u32 mbus_code; + enum v4l2_colorspace colorspace; +}; + +struct cfg_array { + struct regval_list *regs; + int size; +}; + +struct sensor_win_size { + int width; + int height; + void *regs; + int regs_size; +}; + +struct imx219 { + struct device *dev; + struct v4l2_subdev subdev; + struct media_pad pad; + struct mutex lock; + struct v4l2_mbus_framefmt format; + struct sensor_format_struct *fmt; + unsigned int width; + unsigned int height; + unsigned int capture_mode; + struct v4l2_fract tpf; + struct sensor_win_size *current_wins; +}; + +static inline struct imx219 *to_state(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct imx219, subdev); +} + +static struct sensor_format_struct sensor_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + }, +}; + +/* +Used the following sources for register data: +Copyright (C) 2014, Andrew Chew +https://chromium.googlesource.com/chromiumos/third_party/kernel/+/factory-ryu-6486.14.B-chromeos-3.14/drivers/media/i2c/soc_camera/imx219.c + +Copyright (C) 2013 Broadcom Corporation +https://android.googlesource.com/kernel/bcm/+/android-bcm-tetra-3.10-lollipop-wear-release/drivers/media/video/imx219.c + +Chomoly (looks like Allwinner corporation made this one) +https://github.com/allwinner-zh/linux-3.4-sunxi/blob/master/drivers/media/video/sunxi-vfe/device/imx219.c + +https://github.com/rellimmot/Sony-IMX219-Raspberry-Pi-V2-CMOS + +Copyright (c) 2017, Raspberry Pi Foundation +Copyright (c) 2017, Dave Stevenson +https//github.com/6by9/raspiraw + +Register data was manually tuned and tweaked for use with Renesas +RCar CSI driver. +*/ + +/* unlock vendor registers */ +static struct regval_list sensor_unlock_regs[] = { + {0x30EB, 0x05}, + {0x30EB, 0x0C}, + {0x300A, 0xFF}, + {0x300B, 0xFF}, + {0x30EB, 0x05}, + {0x30EB, 0x09}, + {REG_DLY, 30}, +}; + +/* CIS tuning */ +static struct regval_list cis_tuning_regs[] = { + /* magic */ + {0x455E, 0x00}, + {0x471E, 0x4B}, + {0x4767, 0x0F}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47B4, 0x14}, + {0x4713, 0x30}, + {0x478B, 0x10}, + {0x478F, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0E}, + {0x479B, 0x0E}, +}; + +static struct regval_list sensor_hxga_regs[] = { + /* 0x114: 03 = 4 lanes, 01 = 2 lanes */ + {0x0114, 0x01}, + /* manual phy control */ + {0x0128, 0x01}, + /* EXCK_FREQ */ + {0x012A, 0x18}, + {0x012B, 0x00}, + /* gain */ + {0x0157, 0xe8}, + {0x0158, 0x01}, + {0x0159, 0x00}, + /* integration time */ + {0x015a, 0x02}, + {0x015b, 0x31}, + /* frame length */ + {0x0160, 0x0f}, + {0x0161, 0xe0}, + /* line length */ + {0x0162, 0x0f}, + {0x0163, 0xE8}, + /* x addr start */ + {0x0164, 0x00}, + {0x0165, 0x00}, + /* x addr end */ + {0x0166, 0x0C}, + {0x0167, 0xCF}, + /* y addr start */ + {0x0168, 0x00}, + {0x0169, 0x00}, + /* y addr end */ + {0x016A, 0x09}, + {0x016B, 0x9F}, + /* Output X size */ + {0x016C, 0x0C}, + {0x016D, 0xD0}, + /* Output Y size */ + {0x016E, 0x09}, + {0x016F, 0xA0}, + /* pixel increment */ + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x00}, + {0x0175, 0x00}, + /* pixell data format */ + {0x018C, 0x08}, + {0x018D, 0x08}, + /* pix clk div, */ + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + /* PLL1 */ + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x0309, 0x08}, + {0x030B, 0x01}, + /* PLL2 */ + {0x030C, 0x00}, + {0x030D, 0x72}, +}; + +static struct regval_list sensor_1080p_regs[] = { + /* 0x114: 03 = 4 lanes, 01 = 2 lanes */ + {0x0114, 0x01}, + /* manual phy control */ + {0x0128, 0x01}, + /* EXCK_FREQ */ + {0x012A, 0x18}, + {0x012B, 0x00}, + /* gain */ + {0x0157, 0xe8}, + {0x0158, 0x01}, + {0x0159, 0x00}, + /* coarse integration time */ + {0x015a, 0x05}, + {0x015b, 0x3f}, + {0x0160, 0x0A}, + {0x0161, 0x2F}, + {0x0162, 0x0D}, + {0x0163, 0x78}, + {0x0164, 0x02}, + {0x0165, 0xA8}, + {0x0166, 0x0A}, + {0x0167, 0x27}, + {0x0168, 0x02}, + {0x0169, 0xB4}, + {0x016A, 0x06}, + {0x016B, 0xEB}, + {0x016C, 0x07}, + {0x016D, 0x80}, + {0x016E, 0x04}, + {0x016F, 0x38}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x00}, + {0x0175, 0x00}, + /* pixell data format */ + {0x018C, 0x08}, + {0x018D, 0x08}, + /* pix clk div, */ + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + /* PLL1 */ + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x0309, 0x08}, + {0x030B, 0x01}, + /* PLL2 */ + {0x030C, 0x00}, + {0x030D, 0x72}, +}; + +static struct regval_list sensor_720p_regs[] = { + /* 0x114: 03 = 4 lanes, 01 = 2 lanes */ + {0x0114, 0x01}, + {0x0128, 0x01}, + /* EXCK_FREQ */ + {0x012A, 0x18}, + {0x012B, 0x00}, + /* gain */ + {0x0157, 0xc8}, + {0x0158, 0x01}, + {0x0159, 0x00}, + /* coarse integration time */ + {0x015a, 0x01}, + {0x015b, 0x7f}, + {0x0160, 0x02}, + {0x0161, 0x39}, + {0x0162, 0x0d}, + {0x0163, 0xe7}, + {0x0164, 0x01}, + {0x0165, 0x68}, + {0x0166, 0x0b}, + {0x0167, 0x67}, + {0x0168, 0x02}, + {0x0169, 0x00}, + {0x016A, 0x07}, + {0x016B, 0x9f}, + {0x016C, 0x05}, + {0x016D, 0x00}, + {0x016E, 0x02}, + {0x016F, 0xd0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0172, 0x03}, + {0x0174, 0x03}, + {0x0175, 0x03}, + /* pixell data format */ + {0x018C, 0x08}, + {0x018D, 0x08}, + /* pix clk div, */ + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + /* PLL1 */ + {0x0306, 0x00}, + {0x0307, 0x39}, + /* div2 */ + {0x0309, 0x08}, + {0x030B, 0x01}, + /* PLL2 */ + {0x030C, 0x00}, + {0x030D, 0x72}, +}; + +static struct regval_list sensor_480p_regs[] = { + /* 0x114: 03 = 4 lanes, 01 = 2 lanes */ + {0x0114, 0x01}, + {0x0128, 0x01}, + /* EXCK_FREQ */ + {0x012A, 0x18}, + {0x012B, 0x00}, + /* gain */ + {0x0157, 0xe8}, + {0x0158, 0x01}, + {0x0159, 0x00}, + /* coarse integration time */ + {0x015a, 0x01}, + {0x015b, 0x2f}, + {0x0160, 0x02}, + {0x0161, 0x39}, + {0x0162, 0x0d}, + {0x0163, 0xe7}, + /* x start */ + {0x0164, 0x03}, + {0x0165, 0xe8}, + /* x end */ + {0x0166, 0x08}, + {0x0167, 0xe7}, + /* y start */ + {0x0168, 0x02}, + {0x0169, 0xf0}, + /* y end */ + {0x016A, 0x06}, + {0x016B, 0xaf}, + {0x016C, 0x02}, + {0x016D, 0x80}, + {0x016E, 0x01}, + {0x016F, 0xe0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0172, 0x03}, + {0x0174, 0x03}, + {0x0175, 0x03}, + /* pixell data format */ + {0x018C, 0x08}, + {0x018D, 0x08}, + /* pix clk div, */ + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + /* PLL1 */ + {0x0306, 0x00}, + {0x0307, 0x39}, + /* div2 */ + {0x0309, 0x08}, + {0x030B, 0x01}, + /* PLL2 */ + {0x030C, 0x00}, + {0x030D, 0x72}, +}; + +static struct regval_list sensor_240p_regs[] = { + /* 0x114: 03 = 4 lanes, 01 = 2 lanes */ + {0x0114, 0x01}, + {0x0128, 0x01}, + /* EXCK_FREQ */ + {0x012A, 0x18}, + {0x012B, 0x00}, + /* gain */ + {0x0157, 0xd4}, + {0x0158, 0x01}, + {0x0159, 0x00}, + /* coarse integration time */ + {0x015a, 0x01}, + {0x015b, 0x30}, + /* frame time size */ + {0x0160, 0x02}, + {0x0161, 0x39}, + /* line time size */ + {0x0162, 0x0d}, + {0x0163, 0xE7}, + /* x start */ + {0x0164, 0x03}, + {0x0165, 0xe8}, + /* x end */ + {0x0166, 0x06}, + {0x0167, 0x67}, + /* y start */ + {0x0168, 0x03}, + {0x0169, 0xde}, + /* y end */ + {0x016A, 0x05}, + {0x016B, 0xbe}, + /* x width */ + {0x016C, 0x01}, + {0x016D, 0x40}, + /* x height */ + {0x016E, 0x00}, + {0x016F, 0xf0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0172, 0x03}, + {0x0174, 0x03}, + {0x0175, 0x03}, + /* pixell data format */ + {0x018C, 0x08}, + {0x018D, 0x08}, + /* pix clk div, */ + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + /* PLL1 */ + {0x0306, 0x00}, + {0x0307, 0x39}, + /* div2 */ + {0x0309, 0x08}, + {0x030B, 0x01}, + /* PLL2 */ + {0x030C, 0x00}, + {0x030D, 0x72}, +}; + +#define IMX219_DEFAULT_WIDTH 3296 +#define IMX219_DEFAULT_HEIGHT 2464 + +static struct sensor_win_size sensor_win_sizes[] = { + { + /* 3296x2464 */ + .width = 3296, + .height = 2464, + .regs = sensor_hxga_regs, + .regs_size = ARRAY_SIZE(sensor_hxga_regs), + }, + /* 1920x1080 */ + { + .width = 1920, + .height = 1080, + .regs = sensor_1080p_regs, + .regs_size = ARRAY_SIZE(sensor_1080p_regs), + }, + /* 1280x720 */ + { + .width = 1280, + .height = 720, + .regs = sensor_720p_regs, + .regs_size = ARRAY_SIZE(sensor_720p_regs), + }, + /* 640x480 */ + { + .width = 640, + .height = 480, + .regs = sensor_480p_regs, + .regs_size = ARRAY_SIZE(sensor_480p_regs), + }, + /* 320x240 */ + { + .width = 320, + .height = 240, + .regs = sensor_240p_regs, + .regs_size = ARRAY_SIZE(sensor_240p_regs), + }, +}; + +#define N_FMTS ARRAY_SIZE(sensor_formats) +#define N_WIN_SIZES ARRAY_SIZE(sensor_win_sizes) + +static int imx219_write(struct v4l2_subdev *sd, uint16_t reg, uint8_t val) +{ + int ret; + unsigned char data[3] = {reg >> 8, reg & 0xff, val}; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + ret = i2c_master_send(client, data, 3); + if (ret < 3) { + v4l2_err(sd, "%s: i2c write error, reg: %x, %d\n", + __func__, reg, ret); + return ret < 0 ? ret : -EIO; + } + return 0; +} + +static int imx219_read(struct v4l2_subdev *sd, uint16_t reg, uint8_t *val) +{ + int ret; + unsigned char data_w[2] = { reg >> 8, reg & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + ret = i2c_master_send(client, data_w, 2); + + if (ret < 2) { + v4l2_err(sd, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + ret = i2c_master_recv(client, val, 1); + + if (ret < 1) { + v4l2_err(sd, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int imx219_write_array(struct v4l2_subdev *subdev, + struct regval_list *regs, int array_size) +{ + int i = 0; + int ret = 0; + + if (!regs) + return -EINVAL; + + while (i < array_size) { + if (regs->addr == REG_DLY) + mdelay(regs->data); + else + ret = imx219_write(subdev, regs->addr, regs->data); + + if (ret == -EIO) { + v4l2_err(subdev, "register write error at %d, %04x\n", + i, regs->addr); + return ret; + } + + i++; + regs++; + } + return 0; +} + +static int sensor_s_sw_stby(struct v4l2_subdev *subdev, int on_off) +{ + int ret; + unsigned char rdval; + + ret = imx219_read(subdev, 0x0100, &rdval); + if (ret != 0) + return ret; + + if (on_off == CSI_STBY_ON) + ret = imx219_write(subdev, 0x0100, rdval & 0xfe); + else + ret = imx219_write(subdev, 0x0100, rdval | 0x01); + + msleep(30); + return ret; +} + +static int sensor_power(struct v4l2_subdev *subdev, int on) +{ + int ret = 0; + struct imx219 *imx219 = to_state(subdev); + + mutex_lock(&imx219->lock); + + switch (on) { + case CSI_SUBDEV_PWR_OFF: + ret = sensor_s_sw_stby(subdev, CSI_STBY_ON); + if (ret < 0) + v4l2_err(subdev, "soft stby failed!\n"); + break; + case CSI_SUBDEV_PWR_ON: + ret = sensor_s_sw_stby(subdev, CSI_STBY_OFF); + if (ret) { + /* soft reset */ + imx219_write(subdev, 0x103, 1); + msleep(120); + ret = sensor_s_sw_stby(subdev, CSI_STBY_OFF); + } + if (ret < 0) { + v4l2_err(subdev, + "Camera not available, check power\n"); + break; + } + break; + default: + return -EINVAL; + } + + mutex_unlock(&imx219->lock); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sensor_get_register(struct v4l2_subdev *subdev, + struct v4l2_dbg_register *reg) +{ + u8 val = 0; + int ret; + + ret = imx219_read(subdev, (u16)reg->reg, &val); + if (ret < 0) + return ret; + + reg->val = val; + reg->size = sizeof(u8); + + return ret; +} + +static int sensor_set_register(struct v4l2_subdev *subdev, + const struct v4l2_dbg_register *reg) +{ + imx219_write(subdev, (u16)reg->reg, (u8)reg->val); + + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops sensor_core_ops = { + .s_power = sensor_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = sensor_get_register, + .s_register = sensor_set_register, +#endif +}; + +#ifdef DUMP_REGS +static void sensor_dump_regs(struct v4l2_subdev *subdev) +{ + u8 val; + + imx219_read(subdev, 0x18, &val); + pr_info("FRM_CNT %02x\n", val); + imx219_read(subdev, 0x19, &val); + pr_info("PX_ORDER %02x\n", val); + imx219_read(subdev, 0x1a, &val); + pr_info("DT_PEDESTAL1 %02x\n", val); + imx219_read(subdev, 0x1b, &val); + pr_info("DT_PEDESTAL0 %02x\n", val); + imx219_read(subdev, 0x104, &val); + pr_info("corrupted frame status %02x\n", val); + imx219_read(subdev, 0x111, &val); + pr_info("CSI_SIG_MODE %02x\n", val); + imx219_read(subdev, 0x114, &val); + pr_info("CSI_LANE_MODE %02x\n", val); + imx219_read(subdev, 0x140, &val); + pr_info("TEMPERATURE_VAL %02x\n", val); + imx219_read(subdev, 0x142, &val); + pr_info("READOUT_V_CNT1 %02x\n", val); + imx219_read(subdev, 0x143, &val); + pr_info("READOUT_V_CNT0 %02x\n", val); + imx219_read(subdev, 0x150, &val); + pr_info("FRAME_BANK_STATUS %02x\n", val); + imx219_read(subdev, 0x151, &val); + pr_info("FRAME_BANK_FRM_COUNT %02x\n", val); +} +#else +#define sensor_dump_regs(x) +#endif +static int sensor_enum_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= N_FMTS) + return -EINVAL; + + code->code = sensor_formats[code->index].mbus_code; + return 0; +} + +static int sensor_try_fmt_internal(struct v4l2_subdev *subdev, + struct v4l2_mbus_framefmt *fmt, + struct sensor_format_struct **ret_fmt, + struct sensor_win_size **ret_wsize) +{ + int index; + struct sensor_win_size *wsize; + struct imx219 *imx219 = to_state(subdev); + + for (index = 0; index < N_FMTS; index++) + if (sensor_formats[index].mbus_code == fmt->code) + break; + + if (index >= N_FMTS) + return -EINVAL; + + if (ret_fmt) + *ret_fmt = sensor_formats + index; + + fmt->field = V4L2_FIELD_NONE; + for (wsize = sensor_win_sizes; + wsize < sensor_win_sizes + N_WIN_SIZES; + wsize++) + if (fmt->width >= wsize->width && fmt->height >= wsize->height) + break; + if (wsize >= sensor_win_sizes + N_WIN_SIZES) + wsize--; + if (ret_wsize) + *ret_wsize = wsize; + fmt->width = wsize->width; + fmt->height = wsize->height; + imx219->current_wins = wsize; + + return 0; +} + +static int sensor_s_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + int ret; + struct sensor_format_struct *sensor_fmt; + struct sensor_win_size *wsize = NULL; + struct imx219 *info = to_state(subdev); + + ret = sensor_try_fmt_internal(subdev, &fmt->format, + &sensor_fmt, &wsize); + if (ret) + return ret; + + info->fmt = sensor_fmt; + if (wsize->regs) { + /* putting sensor to sleep */ + imx219_write(subdev, 0x100, 0); + msleep(30); + ret = imx219_write_array(subdev, sensor_unlock_regs, + ARRAY_SIZE(sensor_hxga_regs)); + if (ret < 0) + return ret; + ret = imx219_write_array(subdev, + wsize->regs, + wsize->regs_size); + if (ret) + return ret; + ret = imx219_write_array(subdev, cis_tuning_regs, + ARRAY_SIZE(cis_tuning_regs)); + if (ret) + return ret; +#ifdef TEST_PATTERN + ret = imx219_write(subdev, 0x0600, 0x00); + ret |= imx219_write(subdev, 0x0601, 0x02); + ret |= imx219_write(subdev, 0x0620, 0x00); + ret |= imx219_write(subdev, 0x0621, 0x00); + ret |= imx219_write(subdev, 0x0622, 0x00); + ret |= imx219_write(subdev, 0x0623, 0x00); + ret |= imx219_write(subdev, 0x0624, + (wsize->width >> 8) & 0xff); + ret |= imx219_write(subdev, 0x0625, + wsize->width & 0xff); + ret |= imx219_write(subdev, 0x0626, + (wsize->height >> 8) & 0xff); + ret |= imx219_write(subdev, 0x0627, + wsize->height & 0xff); + if (ret) { + v4l2_err(subdev, "%s: i2c write error\n", + __func__); + return -EIO; + } +#endif + /* putting sensor out of sleep */ + imx219_write(subdev, 0x100, 1); + msleep(30); + sensor_dump_regs(sd); + } + + return 0; +} + +static int sensor_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct imx219 *info = to_state(sd); + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad != 0) + return -EINVAL; + + mf->width = info->current_wins->width; + mf->height = info->current_wins->height; + mf->code = info->fmt->mbus_code; + mf->colorspace = info->fmt->colorspace; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int sensor_s_parm(struct v4l2_subdev *subdev, + struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct imx219 *info = to_state(subdev); + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (info->tpf.numerator == 0) + return -EINVAL; + + info->capture_mode = cp->capturemode; + + return 0; +} + +static int sensor_g_parm(struct v4l2_subdev *subdev, + struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct imx219 *info = to_state(subdev); + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->capturemode = info->capture_mode; + + return 0; +} + +static int sensor_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->flags = V4L2_MBUS_CSI2_2_LANE; + cfg->flags |= V4L2_MBUS_CSI2_CHANNEL_0; + cfg->flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + cfg->flags |= V4L2_MBUS_MASTER; + cfg->type = V4L2_MBUS_CSI2; + + return 0; +} + +static const struct v4l2_subdev_pad_ops sensor_pad_ops = { + .enum_mbus_code = sensor_enum_fmt, + .set_fmt = sensor_s_fmt, + .get_fmt = sensor_g_fmt, +}; + +static const struct v4l2_subdev_video_ops sensor_video_ops = { + .s_parm = sensor_s_parm, + .g_parm = sensor_g_parm, + .g_mbus_config = sensor_g_mbus_config, +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &sensor_core_ops, + .video = &sensor_video_ops, + .pad = &sensor_pad_ops, +}; + +static int imx219_detect(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char id_h, id_l; + int ret; + + ret = sensor_power(sd, 1); + if (ret < 0) + return ret; + msleep(30); + + ret = imx219_read(sd, IMX219_REG_CHIPID_H, &id_h); + if (ret < 0) + return ret; + ret = imx219_read(sd, IMX219_REG_CHIPID_L, &id_l); + if (ret < 0) + return ret; + + if (id_h != 0x02 || id_l != 0x19) { + v4l2_info(sd, "Invalid device ID: %02x%02x\n", id_h, id_l); + return -ENODEV; + } + + v4l2_info(sd, "IMX219 detected at address 0x%02x\n", client->addr); + + ret = sensor_power(sd, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int imx219_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = + v4l2_subdev_get_try_format(subdev, fh->pad, 0); + struct v4l2_rect *crop = + v4l2_subdev_get_try_crop(subdev, fh->pad, 0); + + crop->left = 0; + crop->top = 0; + crop->width = IMX219_DEFAULT_WIDTH; + crop->height = IMX219_DEFAULT_HEIGHT; + + format->code = MEDIA_BUS_FMT_SBGGR8_1X8; + + format->width = IMX219_DEFAULT_WIDTH; + format->height = IMX219_DEFAULT_HEIGHT; + format->field = V4L2_FIELD_NONE; + format->colorspace = sensor_formats[0].colorspace; + + return sensor_power(subdev, 1); +} + +static int imx219_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return sensor_power(subdev, 0); +} + +static const struct v4l2_subdev_internal_ops imx219_subdev_internal_ops = { + .open = imx219_open, + .close = imx219_close, +}; + +static int imx219_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct imx219 *sensor; + int ret = 0, i; + struct v4l2_subdev *sd; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + mutex_init(&sensor->lock); + sensor->dev = dev; + sensor->fmt = &sensor_formats[0]; + sensor->width = sensor_win_sizes[0].width; + sensor->height = sensor_win_sizes[0].height; + sensor->current_wins = &sensor_win_sizes[0]; + + sd = &sensor->subdev; + v4l2_i2c_subdev_init(sd, client, &subdev_ops); + sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = imx219_detect(sd); + if (ret < 0) { + v4l2_err(sd, "IMX219 not found!\n"); + goto out; + } + /* soft reset sequence */ + for (i = 0; i < 3; i++) { + imx219_write(sd, 0x103, 1); + mdelay(30); + } + msleep(60); + + /* putting sensor to sleep */ + imx219_write(sd, 0x100, 0); + msleep(30); + ret = imx219_write_array(sd, sensor_unlock_regs, + ARRAY_SIZE(sensor_hxga_regs)); + if (ret < 0) + return ret; + ret = imx219_write_array(sd, sensor_win_sizes[0].regs, + sensor_win_sizes[0].regs_size); + if (ret < 0) + return ret; + ret = imx219_write_array(sd, cis_tuning_regs, + ARRAY_SIZE(cis_tuning_regs)); + if (ret < 0) + return ret; + /* getting sensor out of sleep */ + imx219_write(sd, 0x100, 1); + msleep(30); + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); + if (ret < 0) + return ret; + + ret = v4l2_async_register_subdev(sd); + if (ret < 0) + media_entity_cleanup(&sd->entity); + + /* putting sensor back to sleep to save power */ + imx219_write(sd, 0x100, 0); +out: + return ret; +} + +static int imx219_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct imx219 *imx219 = to_state(subdev); + + v4l2_async_unregister_subdev(&imx219->subdev); + media_entity_cleanup(&imx219->subdev.entity); + v4l2_device_unregister_subdev(subdev); + + return 0; +} + +static const struct i2c_device_id imx219_id[] = { + { "imx219", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, imx219_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx219_of_match[] = { + { .compatible = "sony,imx219" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx219_of_match); +#endif + +static struct i2c_driver imx219_driver = { + .driver = { + .of_match_table = of_match_ptr(imx219_of_match), + .owner = THIS_MODULE, + .name = "imx219", + }, + .probe = imx219_probe, + .remove = imx219_remove, + .id_table = imx219_id, +}; +module_i2c_driver(imx219_driver); + +MODULE_AUTHOR("Sergey Lapin "); +MODULE_DESCRIPTION("A low-level driver for Sony imx219 sensors"); +MODULE_LICENSE("GPL"); -- 2.7.4