imx135 camera sensor driver
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 878f66e..5bead52 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -738,6 +738,19 @@
To compile this driver as a module, choose M here: the
module will be called hi556.
+config VIDEO_IMX135
+ tristate "Sony IMX135 sensor support"
+ depends on I2C && VIDEO_V4L2
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor driver for the Sony
+ IMX135 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx135.
+
config VIDEO_IMX214
tristate "Sony IMX214 sensor support"
depends on GPIOLIB && I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index f0a7747..a1092ea 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -112,6 +112,7 @@
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
obj-$(CONFIG_VIDEO_HI556) += hi556.o
+obj-$(CONFIG_VIDEO_IMX135) += imx135.o
obj-$(CONFIG_VIDEO_IMX214) += imx214.o
obj-$(CONFIG_VIDEO_IMX219) += imx219.o
obj-$(CONFIG_VIDEO_IMX258) += imx258.o
diff --git a/drivers/media/i2c/imx135.c b/drivers/media/i2c/imx135.c
new file mode 100644
index 0000000..79d0b1f
--- /dev/null
+++ b/drivers/media/i2c/imx135.c
@@ -0,0 +1,1482 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sony IMX135 CMOS Image Sensor Driver
+ *
+ * Author: Jake Day <jake@ninebysix.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define IMX135_STANDBY 0x0100
+#define IMX135_REGHOLD 0x0104
+#define IMX135_XMSTA 0x0100
+#define IMX135_GAIN 0x0205
+
+#define IMX135_DEFAULT_LINK_FREQ 451200000
+
+#define IMX135_TABLE_WAIT_MS 0
+#define IMX135_TABLE_END 1
+#define IMX135_MAX_RETRIES 3
+#define IMX135_WAIT_MS 3
+
+static const char * const imx135_supply_name[] = {
+ "vana", /* analog 2.7v */
+ "vdig", /* digital 1.05v */
+ "vif" /* IO 1.8v */
+};
+
+#define IMX135_NUM_SUPPLIES ARRAY_SIZE(imx135_supply_name)
+
+struct imx135_regval {
+ u16 reg;
+ u8 val;
+};
+
+struct imx135_mode {
+ u32 width;
+ u32 height;
+ u32 pixel_rate;
+ u32 link_freq_index;
+
+ const struct imx135_regval *data;
+ u32 data_size;
+};
+
+struct imx135 {
+ struct device *dev;
+ struct clk *xclk;
+ struct regmap *regmap;
+
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_mbus_framefmt current_format;
+ const struct imx135_mode *current_mode;
+
+ struct regulator_bulk_data supplies[IMX135_NUM_SUPPLIES];
+ struct gpio_desc *rst_gpio;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+
+ struct mutex lock;
+};
+
+struct imx135_pixfmt {
+ u32 code;
+};
+
+static const struct imx135_pixfmt imx135_formats[] = {
+ { MEDIA_BUS_FMT_SRGGB10_1X10 },
+};
+
+static const struct regmap_config imx135_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct imx135_regval imx135_global_init_settings[] = {
+ /* software reset */
+ {0x0103, 0x01},
+ /* Clock Setting */
+ {0x011E, 0x18},
+ {0x011F, 0x00},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0305, 0x0C},
+ {0x0309, 0x05},
+ {0x030B, 0x02},
+ {0x030C, 0x01},
+ {0x030D, 0xC2},
+ {0x030E, 0x01},
+ {0x3A06, 0x12},
+ /* global settings */
+ {0x0101, 0x00},
+ {0x0105, 0x01},
+ {0x0110, 0x00},
+ {0x0220, 0x01},
+ {0x3302, 0x11},
+ {0x3833, 0x20},
+ {0x3893, 0x00},
+ {0x3906, 0x08},
+ {0x3907, 0x01},
+ {0x391B, 0x01},
+ {0x3C09, 0x01},
+ {0x600A, 0x00},
+ {0x3008, 0xB0},
+ {0x320A, 0x01},
+ {0x320D, 0x10},
+ {0x3216, 0x2E},
+ {0x322C, 0x02},
+ {0x3409, 0x0C},
+ {0x340C, 0x2D},
+ {0x3411, 0x39},
+ {0x3414, 0x1E},
+ {0x3427, 0x04},
+ {0x3480, 0x1E},
+ {0x3484, 0x1E},
+ {0x3488, 0x1E},
+ {0x348C, 0x1E},
+ {0x3490, 0x1E},
+ {0x3494, 0x1E},
+ {0x3511, 0x8F},
+ {0x364F, 0x2D},
+ /* Global Timing Setting */
+ {0x0830, 0x67},
+ {0x0831, 0x27},
+ {0x0832, 0x47},
+ {0x0833, 0x27},
+ {0x0834, 0x27},
+ {0x0835, 0x1F},
+ {0x0836, 0x87},
+ {0x0837, 0x2F},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02}
+};
+
+static struct imx135_regval imx135_3120p_settings[] = {
+ /* Mode Settings */
+ {0x0108, 0x03},
+ {0x0112, 0x0A},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x00},
+ {0x0391, 0x11},
+ {0x0392, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4082, 0x01},
+ {0x4083, 0x01},
+ {0x7006, 0x04},
+ /* Optinal/Function settings */
+ {0x0700, 0x00},
+ {0x3A63, 0x00},
+ {0x4100, 0xF8},
+ {0x4203, 0xFF},
+ {0x4344, 0x00},
+ {0x441C, 0x01},
+ /* Size Setting */
+ {0x0340, 0x0C},
+ {0x0341, 0xD0},
+ {0x0342, 0x11},
+ {0x0343, 0xDC},
+ {0x0344, 0x00},
+ {0x0345, 0x00},
+ {0x0346, 0x00},
+ {0x0347, 0x00},
+ {0x0348, 0x10},
+ {0x0349, 0x6F},
+ {0x034A, 0x0C},
+ {0x034B, 0x2F},
+ {0x034C, 0x10},
+ {0x034D, 0x70},
+ {0x034E, 0x0C},
+ {0x034F, 0x30},
+ {0x0350, 0x00},
+ {0x0351, 0x00},
+ {0x0352, 0x00},
+ {0x0353, 0x00},
+ {0x0354, 0x10},
+ {0x0355, 0x70},
+ {0x0356, 0x0C},
+ {0x0357, 0x30},
+ {0x301D, 0x30},
+ {0x3310, 0x10},
+ {0x3311, 0x70},
+ {0x3312, 0x0C},
+ {0x3313, 0x30},
+ {0x331C, 0x01},
+ {0x331D, 0x68},
+ {0x4084, 0x00},
+ {0x4085, 0x00},
+ {0x4086, 0x00},
+ {0x4087, 0x00},
+ {0x4400, 0x00},
+ /* Global Timing Setting */
+ {0x0830, 0x87},
+ {0x0831, 0x3F},
+ {0x0832, 0x67},
+ {0x0833, 0x3F},
+ {0x0834, 0x3F},
+ {0x0835, 0x4F},
+ {0x0836, 0xDF},
+ {0x0837, 0x47},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+ /* Integration Time Setting */
+ {0x0202, 0x0C},
+ {0x0203, 0xCC},
+ /* Gain Setting */
+ {0x0205, 0x04},
+ {0x020E, 0x01},
+ {0x020F, 0x00},
+ {0x0210, 0x01},
+ {0x0211, 0x00},
+ {0x0212, 0x01},
+ {0x0213, 0x00},
+ {0x0214, 0x01},
+ {0x0215, 0x00},
+ /* HDR Setting */
+ {0x0230, 0x00},
+ {0x0231, 0x00},
+ {0x0233, 0x00},
+ {0x0234, 0x00},
+ {0x0235, 0x40},
+ {0x0238, 0x01},
+ {0x0239, 0x04},
+ {0x023B, 0x00},
+ {0x023C, 0x01},
+ {0x33B0, 0x04},
+ {0x33B1, 0x00},
+ {0x33B3, 0x00},
+ {0x33B4, 0x01},
+ {0x3800, 0x00},
+ {0x3A43, 0x01},
+ /* stream on */
+ {0x0100, 0x01},
+ {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
+ {IMX135_TABLE_END, 0x00}
+};
+
+static struct imx135_regval imx135_2192p_settings[] = {
+ /* Mode Settings */
+ {0x0108, 0x03},
+ {0x0112, 0x0E},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x00},
+ {0x0391, 0x11},
+ {0x0392, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4082, 0x01},
+ {0x4083, 0x01},
+ {0x7006, 0x04},
+ /* Optinal/Function settings */
+ {0x0700, 0x00},
+ {0x3A63, 0x00},
+ {0x4100, 0xF8},
+ {0x4203, 0xFF},
+ {0x4344, 0x00},
+ {0x441C, 0x01},
+ /* Size Setting */
+ {0x0340, 0x0A},
+ {0x0341, 0x40},
+ {0x0342, 0x11},
+ {0x0343, 0xDC},
+ {0x0344, 0x00},
+ {0x0345, 0x9C},
+ {0x0346, 0x01},
+ {0x0347, 0xD0},
+ {0x0348, 0x0F},
+ {0x0349, 0xD3},
+ {0x034A, 0x0A},
+ {0x034B, 0x5F},
+ {0x034C, 0x0F},
+ {0x034D, 0x38},
+ {0x034E, 0x08},
+ {0x034F, 0x90},
+ {0x0350, 0x00},
+ {0x0351, 0x00},
+ {0x0352, 0x00},
+ {0x0353, 0x00},
+ {0x0354, 0x0F},
+ {0x0355, 0x38},
+ {0x0356, 0x08},
+ {0x0357, 0x90},
+ {0x301D, 0x30},
+ {0x3310, 0x0F},
+ {0x3311, 0x38},
+ {0x3312, 0x08},
+ {0x3313, 0x90},
+ {0x331C, 0x0F},
+ {0x331D, 0x32},
+ {0x4084, 0x00},
+ {0x4085, 0x00},
+ {0x4086, 0x00},
+ {0x4087, 0x00},
+ {0x4400, 0x00},
+ /* Global Timing Setting */
+ {0x0830, 0x87},
+ {0x0831, 0x3F},
+ {0x0832, 0x67},
+ {0x0833, 0x3F},
+ {0x0834, 0x3F},
+ {0x0835, 0x4F},
+ {0x0836, 0xDF},
+ {0x0837, 0x47},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+ /* Integration Time Setting */
+ {0x0202, 0x0A},
+ {0x0203, 0x3C},
+ /* Gain Setting */
+ {0x0205, 0x00},
+ {0x020E, 0x01},
+ {0x020F, 0x00},
+ {0x0210, 0x01},
+ {0x0211, 0x00},
+ {0x0212, 0x01},
+ {0x0213, 0x00},
+ {0x0214, 0x01},
+ {0x0215, 0x00},
+ /* HDR Setting */
+ {0x0230, 0x00},
+ {0x0231, 0x00},
+ {0x0233, 0x00},
+ {0x0234, 0x00},
+ {0x0235, 0x40},
+ {0x0238, 0x01},
+ {0x0239, 0x04},
+ {0x023B, 0x00},
+ {0x023C, 0x01},
+ {0x33B0, 0x0F},
+ {0x33B1, 0x38},
+ {0x33B3, 0x01},
+ {0x33B4, 0x01},
+ {0x3800, 0x00},
+ {0x3A43, 0x01},
+ /* stream on */
+ {0x0100, 0x01},
+ {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
+ {IMX135_TABLE_END, 0x00}
+};
+
+static struct imx135_regval imx135_1472p_settings[] = {
+ /* Mode Settings */
+ {0x0108, 0x03},
+ {0x0112, 0x0E},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x00},
+ {0x0391, 0x11},
+ {0x0392, 0x00},
+ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x10},
+ {0x4082, 0x01},
+ {0x4083, 0x01},
+ {0x7006, 0x04},
+ /* Optinal/Function settings */
+ {0x0700, 0x00},
+ {0x3A63, 0x00},
+ {0x4100, 0xF8},
+ {0x4203, 0xFF},
+ {0x4344, 0x00},
+ {0x441C, 0x01},
+ /* Size Setting */
+ {0x0340, 0x0A},
+ {0x0341, 0x40},
+ {0x0342, 0x11},
+ {0x0343, 0xDC},
+ {0x0344, 0x03},
+ {0x0345, 0x1C},
+ {0x0346, 0x03},
+ {0x0347, 0x38},
+ {0x0348, 0x0D},
+ {0x0349, 0x53},
+ {0x034A, 0x08},
+ {0x034B, 0xF7},
+ {0x034C, 0x0A},
+ {0x034D, 0x38},
+ {0x034E, 0x05},
+ {0x034F, 0xC0},
+ {0x0350, 0x00},
+ {0x0351, 0x00},
+ {0x0352, 0x00},
+ {0x0353, 0x00},
+ {0x0354, 0x0A},
+ {0x0355, 0x38},
+ {0x0356, 0x05},
+ {0x0357, 0xC0},
+ {0x301D, 0x30},
+ {0x3310, 0x0A},
+ {0x3311, 0x38},
+ {0x3312, 0x05},
+ {0x3313, 0xC0},
+ {0x331C, 0x08},
+ {0x331D, 0xD4},
+ {0x4084, 0x00},
+ {0x4085, 0x00},
+ {0x4086, 0x00},
+ {0x4087, 0x00},
+ {0x4400, 0x00},
+ /* Global Timing Setting */
+ {0x0830, 0x87},
+ {0x0831, 0x3F},
+ {0x0832, 0x67},
+ {0x0833, 0x3F},
+ {0x0834, 0x3F},
+ {0x0835, 0x4F},
+ {0x0836, 0xDF},
+ {0x0837, 0x47},
+ {0x0839, 0x1F},
+ {0x083A, 0x17},
+ {0x083B, 0x02},
+ /* Integration Time Setting */
+ {0x0202, 0x0A},
+ {0x0203, 0x3C},
+ /* Gain Setting */
+ {0x0205, 0x00},
+ {0x020E, 0x01},
+ {0x020F, 0x00},
+ {0x0210, 0x01},
+ {0x0211, 0x00},
+ {0x0212, 0x01},
+ {0x0213, 0x00},
+ {0x0214, 0x01},
+ {0x0215, 0x00},
+ /* HDR Setting */
+ {0x0230, 0x00},
+ {0x0231, 0x00},
+ {0x0233, 0x00},
+ {0x0234, 0x00},
+ {0x0235, 0x40},
+ {0x0238, 0x01},
+ {0x0239, 0x04},
+ {0x023B, 0x00},
+ {0x023C, 0x01},
+ {0x33B0, 0x0A},
+ {0x33B1, 0x38},
+ {0x33B3, 0x01},
+ {0x33B4, 0x01},
+ {0x3800, 0x00},
+ {0x3A43, 0x01},
+ /* stream on */
+ {0x0100, 0x01},
+ {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
+ {IMX135_TABLE_END, 0x00}
+};
+
+static const struct imx135_regval imx135_1080p_settings[] = {
+ /* Mode Settings */
+ {0x0108, 0x03},
+ {0x0112, 0x0A},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x01},
+ {0x0391, 0x22},
+ {0x0392, 0x00},
+ {0x0401, 0x02},
+ {0x0404, 0x00},
+ {0x0405, 0x11},
+ {0x4082, 0x00},
+ {0x4083, 0x00},
+ {0x7006, 0x04},
+ /* Optinal/Function settings */
+ {0x0700, 0x00},
+ {0x3A63, 0x00},
+ {0x4100, 0xF8},
+ {0x4203, 0xFF},
+ {0x4344, 0x00},
+ {0x441C, 0x01},
+ /* Size Setting */
+ {0x0340, 0x0A},
+ {0x0341, 0x40},
+ {0x0342, 0x11},
+ {0x0343, 0xDC},
+ {0x0344, 0x00},
+ {0x0345, 0x40},
+ {0x0346, 0x01},
+ {0x0347, 0x9C},
+ {0x0348, 0x10},
+ {0x0349, 0x2F},
+ {0x034A, 0x0A},
+ {0x034B, 0x93},
+ {0x034C, 0x07},
+ {0x034D, 0x80},
+ {0x034E, 0x04},
+ {0x034F, 0x38},
+ {0x0350, 0x00},
+ {0x0351, 0x00},
+ {0x0352, 0x00},
+ {0x0353, 0x00},
+ {0x0354, 0x07},
+ {0x0355, 0xF8},
+ {0x0356, 0x04},
+ {0x0357, 0x7C},
+ {0x301D, 0x30},
+ {0x3310, 0x07},
+ {0x3311, 0x80},
+ {0x3312, 0x04},
+ {0x3313, 0x38},
+ {0x331C, 0x00},
+ {0x331D, 0xD2},
+ {0x4084, 0x07},
+ {0x4085, 0x80},
+ {0x4086, 0x04},
+ {0x4087, 0x38},
+ {0x4400, 0x00},
+ /* Integration Time Setting */
+ {0x0202, 0x0A},
+ {0x0203, 0x3C},
+ /* Gain Setting */
+ {0x0205, 0x00},
+ {0x020E, 0x01},
+ {0x020F, 0x00},
+ {0x0210, 0x01},
+ {0x0211, 0x00},
+ {0x0212, 0x01},
+ {0x0213, 0x00},
+ {0x0214, 0x01},
+ {0x0215, 0x00},
+ /* HDR Setting */
+ {0x0230, 0x00},
+ {0x0231, 0x00},
+ {0x0233, 0x00},
+ {0x0234, 0x00},
+ {0x0235, 0x40},
+ {0x0238, 0x01},
+ {0x0239, 0x04},
+ {0x023B, 0x00},
+ {0x023C, 0x01},
+ {0x33B0, 0x04},
+ {0x33B1, 0x00},
+ {0x33B3, 0x00},
+ {0x33B4, 0x01},
+ {0x3800, 0x00},
+ {0x3A43, 0x01},
+ /* stream on */
+ {0x0100, 0x01},
+ {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
+ {IMX135_TABLE_END, 0x00}
+};
+
+static const struct imx135_regval imx135_720p_settings[] = {
+ /* Mode Settings */
+ {0x0108, 0x03},
+ {0x0112, 0x0A},
+ {0x0113, 0x0A},
+ {0x0381, 0x01},
+ {0x0383, 0x01},
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0390, 0x01},
+ {0x0391, 0x22},
+ {0x0392, 0x00},
+ {0x0401, 0x02},
+ {0x0404, 0x00},
+ {0x0405, 0x1A},
+ {0x4082, 0x00},
+ {0x4083, 0x00},
+ {0x7006, 0x04},
+ /* Optinal/Function settings */
+ {0x0700, 0x00},
+ {0x3A63, 0x00},
+ {0x4100, 0xF8},
+ {0x4203, 0xFF},
+ {0x4344, 0x00},
+ {0x441C, 0x01},
+ /* Size Setting */
+ {0x0340, 0x0A},
+ {0x0341, 0x40},
+ {0x0342, 0x11},
+ {0x0343, 0xDC},
+ {0x0344, 0x00},
+ {0x0345, 0x18},
+ {0x0346, 0x01},
+ {0x0347, 0x88},
+ {0x0348, 0x10},
+ {0x0349, 0x57},
+ {0x034A, 0x0A},
+ {0x034B, 0xAB},
+ {0x034C, 0x05},
+ {0x034D, 0x00},
+ {0x034E, 0x02},
+ {0x034F, 0xD0},
+ {0x0350, 0x00},
+ {0x0351, 0x00},
+ {0x0352, 0x00},
+ {0x0353, 0x00},
+ {0x0354, 0x08},
+ {0x0355, 0x20},
+ {0x0356, 0x04},
+ {0x0357, 0x92},
+ {0x301D, 0x30},
+ {0x3310, 0x05},
+ {0x3311, 0x00},
+ {0x3312, 0x02},
+ {0x3313, 0xD0},
+ {0x331C, 0x02},
+ {0x331D, 0x18},
+ {0x4084, 0x05},
+ {0x4085, 0x00},
+ {0x4086, 0x02},
+ {0x4087, 0xD0},
+ {0x4400, 0x00},
+ /* Integration Time Setting */
+ {0x0202, 0x0A},
+ {0x0203, 0x3C},
+ /* Gain Setting */
+ {0x0205, 0x00},
+ {0x020E, 0x01},
+ {0x020F, 0x00},
+ {0x0210, 0x01},
+ {0x0211, 0x00},
+ {0x0212, 0x01},
+ {0x0213, 0x00},
+ {0x0214, 0x01},
+ {0x0215, 0x00},
+ /* HDR Setting */
+ {0x0230, 0x00},
+ {0x0231, 0x00},
+ {0x0233, 0x00},
+ {0x0234, 0x00},
+ {0x0235, 0x40},
+ {0x0238, 0x01},
+ {0x0239, 0x04},
+ {0x023B, 0x00},
+ {0x023C, 0x01},
+ {0x33B0, 0x04},
+ {0x33B1, 0x00},
+ {0x33B3, 0x00},
+ {0x33B4, 0x01},
+ {0x3800, 0x00},
+ {0x3A43, 0x01},
+ /* stream on */
+ {0x0100, 0x01},
+ {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
+ {IMX135_TABLE_END, 0x00}
+};
+
+static const struct imx135_regval imx135_10bit_settings[] = {
+ /* defect correction */
+ {0x380A, 0x00},
+ {0x380B, 0x00},
+ {0x4103, 0x00},
+ /* color artifact */
+ {0x4243, 0x9A},
+ {0x4330, 0x01},
+ {0x4331, 0x90},
+ {0x4332, 0x02},
+ {0x4333, 0x58},
+ {0x4334, 0x03},
+ {0x4335, 0x20},
+ {0x4336, 0x03},
+ {0x4337, 0x84},
+ {0x433C, 0x01},
+ {0x4340, 0x02},
+ {0x4341, 0x58},
+ {0x4342, 0x03},
+ {0x4343, 0x52},
+ /* moire reduction */
+ {0x4364, 0x0B},
+ {0x4368, 0x00},
+ {0x4369, 0x0F},
+ {0x436A, 0x03},
+ {0x436B, 0xA8},
+ {0x436C, 0x00},
+ {0x436D, 0x00},
+ {0x436E, 0x00},
+ {0x436F, 0x06},
+ /* CNR parameter */
+ {0x4281, 0x21},
+ {0x4282, 0x18},
+ {0x4283, 0x04},
+ {0x4284, 0x08},
+ {0x4287, 0x7F},
+ {0x4288, 0x08},
+ {0x428B, 0x7F},
+ {0x428C, 0x08},
+ {0x428F, 0x7F},
+ {0x4297, 0x00},
+ {0x4298, 0x7E},
+ {0x4299, 0x7E},
+ {0x429A, 0x7E},
+ {0x42A4, 0xFB},
+ {0x42A5, 0x7E},
+ {0x42A6, 0xDF},
+ {0x42A7, 0xB7},
+ {0x42AF, 0x03},
+ /* ARNR Parameter Settings */
+ {0x4207, 0x03},
+ {0x4216, 0x08},
+ {0x4217, 0x08},
+ /* DLC parameter */
+ {0x4218, 0x00},
+ {0x421B, 0x20},
+ {0x421F, 0x04},
+ {0x4222, 0x02},
+ {0x4223, 0x22},
+ {0x422E, 0x54},
+ {0x422F, 0xFB},
+ {0x4230, 0xFF},
+ {0x4231, 0xFE},
+ {0x4232, 0xFF},
+ {0x4235, 0x58},
+ {0x4236, 0xF7},
+ {0x4237, 0xFD},
+ {0x4239, 0x4E},
+ {0x423A, 0xFC},
+ {0x423B, 0xFD},
+ /* HDR Setting */
+ {0x4300, 0x00},
+ {0x4316, 0x12},
+ {0x4317, 0x22},
+ {0x4318, 0x00},
+ {0x4319, 0x00},
+ {0x431A, 0x00},
+ {0x4324, 0x03},
+ {0x4325, 0x20},
+ {0x4326, 0x03},
+ {0x4327, 0x84},
+ {0x4328, 0x03},
+ {0x4329, 0x20},
+ {0x432A, 0x03},
+ {0x432B, 0x20},
+ {0x432C, 0x01},
+ {0x432D, 0x01},
+ {0x4338, 0x02},
+ {0x4339, 0x00},
+ {0x433A, 0x00},
+ {0x433B, 0x02},
+ {0x435A, 0x03},
+ {0x435B, 0x84},
+ {0x435E, 0x01},
+ {0x435F, 0xFF},
+ {0x4360, 0x01},
+ {0x4361, 0xF4},
+ {0x4362, 0x03},
+ {0x4363, 0x84},
+ {0x437B, 0x01},
+ {0x4401, 0x3F},
+ {0x4402, 0xFF},
+ {0x4404, 0x13},
+ {0x4405, 0x26},
+ {0x4406, 0x07},
+ {0x4408, 0x20},
+ {0x4409, 0xE5},
+ {0x440A, 0xFB},
+ {0x440C, 0xF6},
+ {0x440D, 0xEA},
+ {0x440E, 0x20},
+ {0x4410, 0x00},
+ {0x4411, 0x00},
+ {0x4412, 0x3F},
+ {0x4413, 0xFF},
+ {0x4414, 0x1F},
+ {0x4415, 0xFF},
+ {0x4416, 0x20},
+ {0x4417, 0x00},
+ {0x4418, 0x1F},
+ {0x4419, 0xFF},
+ {0x441A, 0x20},
+ {0x441B, 0x00},
+ {0x441D, 0x40},
+ {0x441E, 0x1E},
+ {0x441F, 0x38},
+ {0x4420, 0x01},
+ {0x4444, 0x00},
+ {0x4445, 0x00},
+ {0x4446, 0x1D},
+ {0x4447, 0xF9},
+ {0x4452, 0x00},
+ {0x4453, 0xA0},
+ {0x4454, 0x08},
+ {0x4455, 0x00},
+ {0x4456, 0x0F},
+ {0x4457, 0xFF},
+ {0x4458, 0x18},
+ {0x4459, 0x18},
+ {0x445A, 0x3F},
+ {0x445B, 0x3A},
+ {0x445C, 0x00},
+ {0x445D, 0x28},
+ {0x445E, 0x01},
+ {0x445F, 0x90},
+ {0x4460, 0x00},
+ {0x4461, 0x60},
+ {0x4462, 0x00},
+ {0x4463, 0x00},
+ {0x4464, 0x00},
+ {0x4465, 0x00},
+ {0x446C, 0x01},
+ {0x446D, 0x00},
+ {0x446E, 0x00},
+ /* LSC setting */
+ {0x452A, 0x02},
+ /* White balance */
+ {0x0712, 0x01},
+ {0x0713, 0x00},
+ {0x0714, 0x01},
+ {0x0715, 0x00},
+ {0x0716, 0x01},
+ {0x0717, 0x00},
+ {0x0718, 0x01},
+ {0x0719, 0x00},
+ /* Shading */
+ {0x4500, 0x1F},
+ {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS},
+ {IMX135_TABLE_END, 0x00}
+};
+
+/* supported link frequencies */
+static const s64 imx135_link_freq[] = {
+ IMX135_DEFAULT_LINK_FREQ,
+};
+
+/* Mode configs */
+static const struct imx135_mode imx135_modes[] = {
+ {
+ .width = 4208,
+ .height = 3120,
+ .data = imx135_3120p_settings,
+ .data_size = ARRAY_SIZE(imx135_3120p_settings),
+ .pixel_rate = 360960000,
+ .link_freq_index = 0,
+ },
+ {
+ .width = 3896,
+ .height = 2192,
+ .data = imx135_2192p_settings,
+ .data_size = ARRAY_SIZE(imx135_2192p_settings),
+ .pixel_rate = 360960000,
+ .link_freq_index = 0,
+ },
+ {
+ .width = 2616,
+ .height = 1472,
+ .data = imx135_1472p_settings,
+ .data_size = ARRAY_SIZE(imx135_1472p_settings),
+ .pixel_rate = 360960000,
+ .link_freq_index = 0,
+ },
+ {
+ .width = 1920,
+ .height = 1080,
+ .data = imx135_1080p_settings,
+ .data_size = ARRAY_SIZE(imx135_1080p_settings),
+ .pixel_rate = 360960000,
+ .link_freq_index = 0,
+ },
+ {
+ .width = 1280,
+ .height = 720,
+ .data = imx135_720p_settings,
+ .data_size = ARRAY_SIZE(imx135_720p_settings),
+ .pixel_rate = 360960000,
+ .link_freq_index = 0,
+ },
+};
+
+static inline struct imx135 *to_imx135(struct v4l2_subdev *_sd)
+{
+ return container_of(_sd, struct imx135, sd);
+}
+
+static inline int imx135_read_reg(struct imx135 *imx135, u16 addr, u8 *value)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(imx135->regmap, addr, ®val);
+ if (ret) {
+ dev_err(imx135->dev, "I2C read failed for addr: %x\n", addr);
+ return ret;
+ }
+
+ *value = regval & 0xff;
+
+ return 0;
+}
+
+static int imx135_write_reg(struct imx135 *imx135, u16 addr, u8 value)
+{
+ int ret;
+
+ ret = regmap_write(imx135->regmap, addr, value);
+ if (ret) {
+ dev_err(imx135->dev, "I2C write failed for addr: %x\n", addr);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int imx135_set_register_array(struct imx135 *imx135,
+ const struct imx135_regval *settings,
+ unsigned int num_settings)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < num_settings; ++i, ++settings) {
+ ret = imx135_write_reg(imx135, settings->reg, settings->val);
+ if (ret < 0)
+ return ret;
+
+ /* Settle time is 10ms for all registers */
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int imx135_write_buffered_reg(struct imx135 *imx135, u16 address_low,
+ u8 nr_regs, u32 value)
+{
+ unsigned int i;
+ int ret;
+
+ ret = imx135_write_reg(imx135, IMX135_REGHOLD, 0x01);
+ if (ret) {
+ dev_err(imx135->dev, "Error setting hold register\n");
+ return ret;
+ }
+
+ for (i = 0; i < nr_regs; i++) {
+ ret = imx135_write_reg(imx135, address_low + i,
+ (u8)(value >> (i * 8)));
+ if (ret) {
+ dev_err(imx135->dev, "Error writing buffered registers\n");
+ return ret;
+ }
+ }
+
+ ret = imx135_write_reg(imx135, IMX135_REGHOLD, 0x00);
+ if (ret) {
+ dev_err(imx135->dev, "Error setting hold register\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int imx135_set_gain(struct imx135 *imx135, u32 value)
+{
+ int ret;
+
+ ret = imx135_write_buffered_reg(imx135, IMX135_GAIN, 1, value);
+ if (ret)
+ dev_err(imx135->dev, "Unable to write gain\n");
+
+ return ret;
+}
+
+/* Stop streaming */
+static int imx135_stop_streaming(struct imx135 *imx135)
+{
+ int ret;
+
+ ret = imx135_write_reg(imx135, IMX135_STANDBY, 0x01);
+ if (ret < 0)
+ return ret;
+
+ msleep(30);
+
+ return imx135_write_reg(imx135, IMX135_XMSTA, 0x01);
+}
+
+static int imx135_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx135 *imx135 = container_of(ctrl->handler,
+ struct imx135, ctrls);
+ int ret = 0;
+
+ /* V4L2 controls values will be applied only when power is already up */
+ if (!pm_runtime_get_if_in_use(imx135->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ret = imx135_set_gain(imx135, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(imx135->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops imx135_ctrl_ops = {
+ .s_ctrl = imx135_set_ctrl,
+};
+
+static int imx135_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(imx135_formats))
+ return -EINVAL;
+
+ code->code = imx135_formats[code->index].code;
+
+ return 0;
+}
+
+static int imx135_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx135 *imx135 = to_imx135(sd);
+ struct v4l2_mbus_framefmt *framefmt;
+
+ mutex_lock(&imx135->lock);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ framefmt = v4l2_subdev_get_try_format(&imx135->sd, cfg,
+ fmt->pad);
+ else
+ framefmt = &imx135->current_format;
+
+ fmt->format = *framefmt;
+
+ mutex_unlock(&imx135->lock);
+
+ return 0;
+}
+
+static int imx135_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx135 *imx135 = to_imx135(sd);
+ const struct imx135_mode *mode;
+ struct v4l2_mbus_framefmt *format;
+ unsigned int i;
+
+ mutex_lock(&imx135->lock);
+
+ mode = v4l2_find_nearest_size(imx135_modes,
+ ARRAY_SIZE(imx135_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
+
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+
+ for (i = 0; i < ARRAY_SIZE(imx135_formats); i++)
+ if (imx135_formats[i].code == fmt->format.code)
+ break;
+
+ if (i >= ARRAY_SIZE(imx135_formats))
+ i = 0;
+
+ fmt->format.code = imx135_formats[i].code;
+ fmt->format.field = V4L2_FIELD_NONE;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ } else {
+ format = &imx135->current_format;
+ __v4l2_ctrl_s_ctrl(imx135->link_freq, mode->link_freq_index);
+ __v4l2_ctrl_s_ctrl_int64(imx135->pixel_rate, mode->pixel_rate);
+
+ imx135->current_mode = mode;
+ }
+
+ *format = fmt->format;
+
+ mutex_unlock(&imx135->lock);
+
+ return 0;
+}
+
+static int imx135_entity_init_cfg(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_subdev_format fmt = { 0 };
+
+ fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt.format.width = 1920;
+ fmt.format.height = 1080;
+
+ imx135_set_fmt(subdev, cfg, &fmt);
+
+ return 0;
+}
+
+static int imx135_write_current_format(struct imx135 *imx135,
+ struct v4l2_mbus_framefmt *format)
+{
+ int ret;
+
+ switch (format->code) {
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ ret = imx135_set_register_array(imx135, imx135_10bit_settings,
+ ARRAY_SIZE(
+ imx135_10bit_settings));
+ if (ret < 0) {
+ dev_err(imx135->dev, "Could not set format registers\n");
+ return ret;
+ }
+ break;
+ default:
+ dev_err(imx135->dev, "Unknown pixel format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Start streaming */
+static int imx135_start_streaming(struct imx135 *imx135)
+{
+ int ret;
+
+ /* Set init register settings */
+ ret = imx135_set_register_array(imx135, imx135_global_init_settings,
+ ARRAY_SIZE(
+ imx135_global_init_settings));
+ if (ret < 0) {
+ dev_err(imx135->dev, "Could not set init registers\n");
+ return ret;
+ }
+
+ /* Set current frame format */
+ ret = imx135_write_current_format(imx135, &imx135->current_format);
+ if (ret < 0) {
+ dev_err(imx135->dev, "Could not set frame format\n");
+ return ret;
+ }
+
+ /* Apply default values of current mode */
+ ret = imx135_set_register_array(imx135, imx135->current_mode->data,
+ imx135->current_mode->data_size);
+ if (ret < 0) {
+ dev_err(imx135->dev, "Could not set current mode\n");
+ return ret;
+ }
+
+ /* Apply customized values from user */
+ ret = v4l2_ctrl_handler_setup(imx135->sd.ctrl_handler);
+ if (ret) {
+ dev_err(imx135->dev, "Could not sync v4l2 controls\n");
+ return ret;
+ }
+
+ ret = imx135_write_reg(imx135, IMX135_STANDBY, 0x00);
+ if (ret < 0)
+ return ret;
+
+ msleep(30);
+
+ /* Start streaming */
+ return imx135_write_reg(imx135, IMX135_XMSTA, 0x00);
+}
+
+static int imx135_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx135 *imx135 = to_imx135(sd);
+ int ret = 0;
+
+ if (enable) {
+ ret = pm_runtime_get_sync(imx135->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(imx135->dev);
+ goto unlock_and_return;
+ }
+
+ ret = imx135_start_streaming(imx135);
+ if (ret) {
+ dev_err(imx135->dev, "Start stream failed\n");
+ pm_runtime_put(imx135->dev);
+ goto unlock_and_return;
+ }
+ } else {
+ imx135_stop_streaming(imx135);
+ pm_runtime_put(imx135->dev);
+ }
+
+unlock_and_return:
+
+ return ret;
+}
+
+static int imx135_get_regulators(struct device *dev, struct imx135 *imx135)
+{
+ unsigned int i;
+
+ for (i = 0; i < IMX135_NUM_SUPPLIES; i++)
+ imx135->supplies[i].supply = imx135_supply_name[i];
+
+ return devm_regulator_bulk_get(dev, IMX135_NUM_SUPPLIES,
+ imx135->supplies);
+}
+
+static int match_depend(struct device *dev, const void *data)
+{
+ return (dev && dev->fwnode == data) ? 1 : 0;
+}
+
+static int imx135_power_on(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx135 *imx135 = to_imx135(sd);
+ struct acpi_handle *dev_handle = ACPI_HANDLE(&client->dev);
+ struct acpi_handle_list dep_devices;
+ acpi_status status;
+ int ret;
+ int i;
+
+ if (acpi_has_method(dev_handle, "_DEP")) {
+ status = acpi_evaluate_reference(dev_handle, "_DEP", NULL,
+ &dep_devices);
+ if (ACPI_FAILURE(status)) {
+ printk("Failed to evaluate _DEP.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < dep_devices.count; i++) {
+ struct acpi_device *device;
+ struct acpi_device_info *info;
+
+ status = acpi_get_object_info(dep_devices.handles[i], &info);
+ if (ACPI_FAILURE(status)) {
+ printk("Error reading _DEP device info\n");
+ return -ENODEV;
+ }
+
+ if (info->valid & ACPI_VALID_HID &&
+ !strcmp(info->hardware_id.string, "INT3472")) {
+ if (acpi_bus_get_device(dep_devices.handles[i], &device))
+ return -ENODEV;
+
+ dev = bus_find_device(&platform_bus_type, NULL,
+ &device->fwnode, match_depend);
+ if (dev) {
+ dev_info(&client->dev, "Dependent platform device found %s\n",
+ dev_name(dev));
+ break;
+ }
+ }
+ }
+ }
+
+ ret = regulator_bulk_enable(IMX135_NUM_SUPPLIES, imx135->supplies);
+ if (ret) {
+ dev_err(imx135->dev, "Failed to enable regulators\n");
+ return ret;
+ }
+
+ usleep_range(1, 2);
+ gpiod_set_value_cansleep(imx135->rst_gpio, 1);
+ usleep_range(30000, 31000);
+
+ return 0;
+}
+
+static int imx135_power_off(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx135 *imx135 = to_imx135(sd);
+
+ gpiod_set_value_cansleep(imx135->rst_gpio, 0);
+ regulator_bulk_disable(IMX135_NUM_SUPPLIES, imx135->supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx135_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx135_power_on, imx135_power_off, NULL)
+};
+
+static const struct v4l2_subdev_video_ops imx135_video_ops = {
+ .s_stream = imx135_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx135_pad_ops = {
+ .init_cfg = imx135_entity_init_cfg,
+ .enum_mbus_code = imx135_enum_mbus_code,
+ .get_fmt = imx135_get_fmt,
+ .set_fmt = imx135_set_fmt,
+};
+
+static const struct v4l2_subdev_ops imx135_subdev_ops = {
+ .video = &imx135_video_ops,
+ .pad = &imx135_pad_ops,
+};
+
+static const struct media_entity_operations imx135_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int imx135_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct imx135 *imx135;
+ int ret;
+
+ printk("imx135: probing new device");
+
+ imx135 = devm_kzalloc(dev, sizeof(*imx135), GFP_KERNEL);
+ if (!imx135)
+ return -ENOMEM;
+
+ imx135->dev = dev;
+ imx135->regmap = devm_regmap_init_i2c(client, &imx135_regmap_config);
+ if (IS_ERR(imx135->regmap)) {
+ dev_err(dev, "Unable to initialize I2C\n");
+ return -ENODEV;
+ }
+
+ /* set default mode to max resolution */
+ imx135->current_mode = &imx135_modes[0];
+
+ ret = imx135_get_regulators(dev, imx135);
+ if (ret < 0) {
+ dev_err(dev, "Cannot get regulators\n");
+ return ret;
+ }
+
+ imx135->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(imx135->rst_gpio)) {
+ dev_err(dev, "Cannot get reset gpio\n");
+ ret = PTR_ERR(imx135->rst_gpio);
+ return ret;
+ }
+
+ mutex_init(&imx135->lock);
+
+ v4l2_ctrl_handler_init(&imx135->ctrls, 3);
+
+ v4l2_ctrl_new_std(&imx135->ctrls, &imx135_ctrl_ops,
+ V4L2_CID_GAIN, 0, 72, 1, 0);
+ imx135->link_freq =
+ v4l2_ctrl_new_int_menu(&imx135->ctrls,
+ &imx135_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(imx135_link_freq) - 1,
+ 0, imx135_link_freq);
+ if (imx135->link_freq)
+ imx135->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx135->pixel_rate = v4l2_ctrl_new_std(&imx135->ctrls, &imx135_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 1,
+ INT_MAX, 1,
+ imx135_modes[0].pixel_rate);
+
+ imx135->sd.ctrl_handler = &imx135->ctrls;
+
+ if (imx135->ctrls.error) {
+ dev_err(dev, "Control initialization error %d\n",
+ imx135->ctrls.error);
+ ret = imx135->ctrls.error;
+ goto free_ctrl;
+ }
+
+ v4l2_i2c_subdev_init(&imx135->sd, client, &imx135_subdev_ops);
+ imx135->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ imx135->sd.dev = &client->dev;
+ imx135->sd.entity.ops = &imx135_subdev_entity_ops;
+ imx135->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ imx135->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&imx135->sd.entity, 1, &imx135->pad);
+ if (ret < 0) {
+ dev_err(dev, "Could not register media entity\n");
+ goto free_ctrl;
+ }
+
+ ret = v4l2_async_register_subdev(&imx135->sd);
+ if (ret < 0) {
+ dev_err(dev, "Could not register v4l2 device\n");
+ goto free_entity;
+ }
+
+ /* Power on the device to match runtime PM state below */
+ ret = imx135_power_on(dev);
+ if (ret < 0) {
+ dev_err(dev, "Could not power on the device\n");
+ goto free_entity;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+
+free_entity:
+ media_entity_cleanup(&imx135->sd.entity);
+free_ctrl:
+ v4l2_ctrl_handler_free(&imx135->ctrls);
+ mutex_destroy(&imx135->lock);
+
+ return ret;
+}
+
+static int imx135_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx135 *imx135 = to_imx135(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+
+ mutex_destroy(&imx135->lock);
+
+ pm_runtime_disable(imx135->dev);
+ if (!pm_runtime_status_suspended(imx135->dev))
+ imx135_power_off(imx135->dev);
+ pm_runtime_set_suspended(imx135->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id imx135_acpi_ids[] = {
+ {"INT3471"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, imx135_acpi_ids);
+#endif
+
+static struct i2c_driver imx135_i2c_driver = {
+ .driver = {
+ .name = "imx135",
+ .pm = &imx135_pm_ops,
+ .acpi_match_table = ACPI_PTR(imx135_acpi_ids),
+ },
+ .probe = imx135_probe,
+ .remove = imx135_remove,
+};
+
+module_i2c_driver(imx135_i2c_driver);
+
+MODULE_DESCRIPTION("Sony IMX135 CMOS Image Sensor Driver");
+MODULE_AUTHOR("Jake Day <jake@ninebysix.com>");
+MODULE_LICENSE("GPL v2");