summaryrefslogtreecommitdiffstats
path: root/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-Sony-IMX219-driver.patch
diff options
context:
space:
mode:
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.patch1115
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
+