diff options
Diffstat (limited to 'bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-Sony-IMX219-driver.patch')
-rw-r--r-- | bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-Sony-IMX219-driver.patch | 1115 |
1 files changed, 1115 insertions, 0 deletions
diff --git a/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-Sony-IMX219-driver.patch b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-Sony-IMX219-driver.patch new file mode 100644 index 00000000..ee21c701 --- /dev/null +++ b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-Sony-IMX219-driver.patch @@ -0,0 +1,1115 @@ +From 1a0e23a15528eb2715c13706fe6d74a32640cbb2 Mon Sep 17 00:00:00 2001 +From: Sergey Lapin <sergey.lapin@cogentembedded.com> +Date: Wed, 26 Sep 2018 03:48:08 +0200 +Subject: [PATCH 058/122] Sony IMX219 driver + +Signed-off-by: Sergey Lapin <sergey.lapin@cogentembedded.com> +--- + .../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 <s.nawrocki@samsung.com> ++ * ++ * Based on Omnivision OV7670 Camera Driver ++ * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net> ++ * ++ * Based on Omnivision OV5647 image sensor driver ++ * Copyright (C) 2016, Synopsys, Inc. ++ * ++ * Copyright (C) 2017-2018 Cogent Embedded, Inc ++ */ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/i2c.h> ++#include <linux/delay.h> ++#include <linux/videodev2.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-mediabus.h> ++#include <media/v4l2-image-sizes.h> ++#include <linux/io.h> ++ ++#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 <achew@nvidia.com> ++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 <sergey.lapin@cogentembedded.com>"); ++MODULE_DESCRIPTION("A low-level driver for Sony imx219 sensors"); ++MODULE_LICENSE("GPL"); +-- +2.7.4 + |