summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbhishek Sharma <abhishek.sharma@ti.com>2024-03-14 16:55:41 +0530
committerPraneeth Bajjuri <praneeth@ti.com>2024-03-14 07:53:57 -0500
commitbaed438193a0f3edf9ddb5cd652b6ecb8cb1c3da (patch)
treead718e63229e150b302577bec727fad382567720
parent43a9172360a59bc314979be22056e9c3d23c1695 (diff)
media: i2c: ox05b1s: add OmniVision OX05B1S driver
OmniVision OX05B1S is an RGB-IR sensor, i.e. it uses a 4x4 R,G,B,Ir bayer pattern to capture both visible and near-infrared light. Every alternate frame, the sensor changes the exposure and the analogue/digital gain registers to stream an - A. IR-dominant frame on CSI-2 virtual channel 0 B. RGB-dominant frame on CSI-2 virtual channel 1 Both of these streams are captured at a resolution of 2592x1944, 30 fps each (60fps total). This driver also supports a few v4l2 controls like exposure and gain controls. The RGB dominant stream uses the normal v4l2 control identifiers (CIDs) for exposure and gain change while custom CIDs have been defined for the IR dominant stream. Signed-off-by: Abhishek Sharma <abhishek.sharma@ti.com>
-rw-r--r--drivers/media/i2c/Kconfig13
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/ox05b1s.c1139
3 files changed, 1153 insertions, 0 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 3410f5093a9b..d97cd1924e19 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -720,6 +720,19 @@ config VIDEO_OV9734
To compile this driver as a module, choose M here: the
module's name is ov9734.
+config VIDEO_OX05B1S
+ tristate "OmniVision OX05B1S sensor support"
+ depends on OF_GPIO
+ depends on I2C && VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This is a Video4Linux2 sensor driver for the OmniVision
+ OX05B1S camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called OX05B1S.
+
config VIDEO_RDACM20
tristate "IMI RDACM20 camera support"
depends on I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 0b1e811b4c13..aea3f7e03bfe 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -105,6 +105,7 @@ obj-$(CONFIG_VIDEO_OV9282) += ov9282.o
obj-$(CONFIG_VIDEO_OV9640) += ov9640.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
obj-$(CONFIG_VIDEO_OV9734) += ov9734.o
+obj-$(CONFIG_VIDEO_OX05B1S) += ox05b1s.o
obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o
obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o
obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o
diff --git a/drivers/media/i2c/ox05b1s.c b/drivers/media/i2c/ox05b1s.c
new file mode 100644
index 000000000000..ab0c828e3948
--- /dev/null
+++ b/drivers/media/i2c/ox05b1s.c
@@ -0,0 +1,1139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OmniVision OX05B1S RGB-IR Image Sensor driver
+ *
+ * Copyright (c) 2023-2024 Abhishek Sharma <abhishek.sharma@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+#include <linux/media-bus-format.h>
+
+#define OX05B_CHIP_ID 0x0558
+#define OX05B_FRAMERATE_DEFAULT 60
+
+#define OX05B_OUT_WIDTH 2592
+#define OX05B_OUT_HEIGHT 1944
+
+#define OX05B_SYS_MODE_SEL 0x0100
+#define OX05B_SC_CHIP_ID_HI 0x300a
+#define OX05B_AEC_PK_EXPO_HI 0x3501
+#define OX05B_AEC_PK_EXPO_LO 0x3502
+#define OX05B_AEC_PK_AGAIN_HI 0x3508
+#define OX05B_AEC_PK_AGAIN_LO 0x3509
+#define OX05B_AEC_PK_DGAIN_HI 0x350a
+#define OX05B_AEC_PK_DGAIN_LO 0x350b
+#define OX05B_DEFAULT_LINK_FREQ 480000000
+
+/*
+ * Exposure control
+ * Set max value as 0x850 (frame length = 2128) - 30 = 0x0832 for 16.66 ms exposure
+ * Set default value to be 1000 (0x03E8).
+ */
+#define OX05B_EXPOSURE_MAX 0x0832
+#define OX05B_EXPOSURE_DEFAULT 0x03E8
+
+/* Analog gain control */
+#define OX05B_AGAIN_MAX 0x0F80
+#define OX05B_AGAIN_DEFAULT 0x010
+
+/* Digital gain control */
+#define OX05B_DGAIN_MAX 0x0FFF
+#define OX05B_DGAIN_DEFAULT 0x0100
+
+static const struct v4l2_area ox05b_framesizes[] = {
+ {
+ .width = OX05B_OUT_WIDTH,
+ .height = OX05B_OUT_HEIGHT,
+ },
+};
+
+static const u32 ox05b_mbus_formats[] = {
+ MEDIA_BUS_FMT_SBGGI10_1X10,
+};
+
+static const struct regmap_config ox05b_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+};
+
+static const s64 ox05b_link_freq_menu[] = {
+ OX05B_DEFAULT_LINK_FREQ,
+};
+
+static const struct reg_sequence ox05b_linear_2592x1944[] = {
+ {0x0107, 0x01}, {0x0104, 0x00}, {0x0301, 0x1a}, {0x0304, 0x01},
+ {0x0305, 0xe0}, {0x0306, 0x04}, {0x0307, 0x01}, {0x0321, 0x03},
+ {0x0324, 0x01}, {0x0325, 0x80}, {0x0341, 0x03}, {0x0344, 0x01},
+ {0x0345, 0xb0}, {0x0347, 0x07}, {0x034b, 0x06}, {0x0360, 0x80},
+ {0x040b, 0x5c}, {0x040c, 0xcd}, {0x2805, 0xff}, {0x2806, 0x0f},
+ {0x3000, 0x00}, {0x3001, 0x00}, {0x3002, 0x10}, {0x3004, 0x00},
+ {0x3009, 0x2e}, {0x3010, 0x41}, {0x3015, 0xf0}, {0x3016, 0xf0},
+ {0x3017, 0xf0}, {0x3018, 0xf0}, {0x301a, 0x78}, {0x301b, 0xb4},
+ {0x301f, 0xe9}, {0x3024, 0x80}, {0x302b, 0x00}, {0x3039, 0x00},
+ {0x3044, 0x70}, {0x3101, 0x32}, {0x3182, 0x10}, {0x3187, 0xff},
+ {0x320a, 0x00}, {0x320b, 0x00}, {0x320c, 0x00}, {0x320d, 0x00},
+ {0x320e, 0x00}, {0x320f, 0x00}, {0x3211, 0x61}, {0x3212, 0x00},
+ {0x3215, 0xcc}, {0x3218, 0x06}, {0x3251, 0x00}, {0x3252, 0xe4},
+ {0x3253, 0x00}, {0x3304, 0x11}, {0x3305, 0x00}, {0x3306, 0x01},
+ {0x3307, 0x00}, {0x3308, 0x02}, {0x3309, 0x00}, {0x330a, 0x02},
+ {0x330b, 0x00}, {0x330c, 0x02}, {0x330d, 0x00}, {0x330e, 0x02},
+ {0x330f, 0x00}, {0x3310, 0x02}, {0x3311, 0x00}, {0x3312, 0x02},
+ {0x3313, 0x00}, {0x3314, 0x02}, {0x3315, 0x00}, {0x3316, 0x02},
+ {0x3317, 0x11}, {0x3400, 0x0c}, {0x3421, 0x00}, {0x3422, 0x00},
+ {0x3423, 0x00}, {0x3424, 0x00}, {0x3425, 0x00}, {0x3426, 0x00},
+ {0x3427, 0x00}, {0x3428, 0x00}, {0x3429, 0x00}, {0x342a, 0x00},
+ {0x342b, 0x00}, {0x342c, 0x00}, {0x342d, 0x00}, {0x342e, 0x00},
+ {0x3500, 0x00}, {0x3501, 0x00}, {0x3502, 0x08}, {0x3503, 0xa8},
+ {0x3504, 0x08}, {0x3505, 0x00}, {0x3506, 0x00}, {0x3507, 0x00},
+ {0x3508, 0x01}, {0x3509, 0x00}, {0x350a, 0x01}, {0x350b, 0x00},
+ {0x350c, 0x00}, {0x351e, 0x00}, {0x351f, 0x00}, {0x3541, 0x00},
+ {0x3542, 0x08}, {0x3603, 0x65}, {0x3604, 0x24}, {0x3608, 0x08},
+ {0x3610, 0x00}, {0x3612, 0x00}, {0x3619, 0x09}, {0x361a, 0x27},
+ {0x3620, 0x40}, {0x3622, 0x15}, {0x3623, 0x0e}, {0x3624, 0x1f},
+ {0x3625, 0x1f}, {0x362a, 0x01}, {0x362b, 0x00}, {0x3633, 0x88},
+ {0x3634, 0x86}, {0x3636, 0x80}, {0x3638, 0x5b}, {0x363b, 0x22},
+ {0x363c, 0x07}, {0x363d, 0x11}, {0x363e, 0x21}, {0x363f, 0x24},
+ {0x3640, 0xd3}, {0x3641, 0x00}, {0x3650, 0xe4}, {0x3651, 0x80},
+ {0x3652, 0xff}, {0x3653, 0x00}, {0x3654, 0x05}, {0x3655, 0xf8},
+ {0x3656, 0x00}, {0x3660, 0x00}, {0x3664, 0x00}, {0x366a, 0x80},
+ {0x366b, 0x00}, {0x3670, 0x00}, {0x3674, 0x00}, {0x3684, 0x6d},
+ {0x3685, 0x6d}, {0x3686, 0x6d}, {0x3687, 0x6d}, {0x368c, 0x07},
+ {0x368d, 0x07}, {0x368e, 0x07}, {0x368f, 0x07}, {0x3690, 0x04},
+ {0x3691, 0x04}, {0x3692, 0x04}, {0x3693, 0x04}, {0x3698, 0x00},
+ {0x369e, 0x1f}, {0x369f, 0x19}, {0x36a0, 0x05}, {0x36a2, 0x19},
+ {0x36a3, 0x05}, {0x36a4, 0x07}, {0x36a5, 0x27}, {0x36a6, 0x00},
+ {0x36a7, 0x80}, {0x36e3, 0x09}, {0x3700, 0x07}, {0x3701, 0x1b},
+ {0x3702, 0x0a}, {0x3703, 0x21}, {0x3704, 0x19}, {0x3705, 0x07},
+ {0x3706, 0x36}, {0x370a, 0x1c}, {0x370b, 0x02}, {0x370c, 0x00},
+ {0x370d, 0x6e}, {0x370f, 0x80}, {0x3710, 0x10}, {0x3712, 0x09},
+ {0x3714, 0x42}, {0x3715, 0x00}, {0x3716, 0x02}, {0x3717, 0xa2},
+ {0x3718, 0x41}, {0x371a, 0x80}, {0x371b, 0x0a}, {0x371c, 0x0a},
+ {0x371d, 0x08}, {0x371e, 0x01}, {0x371f, 0x20}, {0x3720, 0x0e},
+ {0x3721, 0x22}, {0x3722, 0x0c}, {0x3727, 0x84}, {0x3728, 0x03},
+ {0x3729, 0x64}, {0x372a, 0x0c}, {0x372b, 0x14}, {0x372d, 0x50},
+ {0x372e, 0x14}, {0x3731, 0x11}, {0x3732, 0x24}, {0x3733, 0x00},
+ {0x3734, 0x00}, {0x3735, 0x12}, {0x3736, 0x00}, {0x373b, 0x0b},
+ {0x373c, 0x14}, {0x373f, 0x3e}, {0x3740, 0x12}, {0x3741, 0x12},
+ {0x3753, 0x80}, {0x3754, 0x01}, {0x3756, 0x11}, {0x375c, 0x0f},
+ {0x375d, 0x35}, {0x375e, 0x0f}, {0x375f, 0x37}, {0x3760, 0x0f},
+ {0x3761, 0x27}, {0x3762, 0x3f}, {0x3763, 0x5d}, {0x3764, 0x01},
+ {0x3765, 0x17}, {0x3766, 0x02}, {0x3768, 0x52}, {0x376a, 0x30},
+ {0x376b, 0x02}, {0x376c, 0x08}, {0x376d, 0x2a}, {0x376e, 0x00},
+ {0x376f, 0x18}, {0x3770, 0x2c}, {0x3771, 0x0c}, {0x3776, 0xc0},
+ {0x3778, 0x00}, {0x3779, 0x80}, {0x377a, 0x00}, {0x377d, 0x14},
+ {0x377e, 0x0c}, {0x379f, 0x00}, {0x37a3, 0x40}, {0x37a4, 0x03},
+ {0x37a5, 0x10}, {0x37a6, 0x02}, {0x37a7, 0x0e}, {0x37a9, 0x00},
+ {0x37aa, 0x08}, {0x37ab, 0x08}, {0x37ac, 0x36}, {0x37ad, 0x40},
+ {0x37b0, 0x48}, {0x37d0, 0x00}, {0x37d1, 0x0b}, {0x37d2, 0x0c},
+ {0x37d3, 0x10}, {0x37d4, 0x10}, {0x37d5, 0x10}, {0x37d8, 0x0e},
+ {0x37d9, 0x0e}, {0x37da, 0x3c}, {0x37db, 0x52}, {0x37dc, 0x50},
+ {0x37dd, 0x00}, {0x37de, 0x55}, {0x37df, 0x7d}, {0x37e5, 0x88},
+ {0x37e7, 0x68}, {0x37e8, 0x07}, {0x37f0, 0x00}, {0x37f1, 0x0e},
+ {0x37f2, 0x35}, {0x37f3, 0x14}, {0x37f4, 0x0c}, {0x37f5, 0x14},
+ {0x37f6, 0x0c}, {0x37f7, 0x35}, {0x37f8, 0x35}, {0x37f9, 0x37},
+ {0x37fa, 0x37}, {0x37fb, 0x37}, {0x3800, 0x00}, {0x3801, 0x00},
+ {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x2f},
+ {0x3806, 0x07}, {0x3807, 0xa7}, {0x3808, 0x0a}, {0x3809, 0x20},
+ {0x380a, 0x07}, {0x380b, 0x98}, {0x380c, 0x01}, {0x380d, 0x78},
+ {0x380e, 0x08}, {0x380f, 0x50}, {0x3810, 0x00}, {0x3811, 0x05},
+ {0x3812, 0x00}, {0x3813, 0x08}, {0x3814, 0x11}, {0x3815, 0x11},
+ {0x3820, 0x40}, {0x3821, 0x04}, {0x3822, 0x10}, {0x3823, 0x00},
+ {0x3826, 0x00}, {0x3827, 0x00}, {0x382b, 0x03}, {0x382c, 0x0c},
+ {0x382d, 0x15}, {0x382e, 0x01}, {0x3830, 0x00}, {0x3838, 0x00},
+ {0x383b, 0x00}, {0x3840, 0x00}, {0x384a, 0xa2}, {0x3858, 0x00},
+ {0x3859, 0x00}, {0x3860, 0x00}, {0x3861, 0x00}, {0x3866, 0x10},
+ {0x3867, 0x07}, {0x3868, 0x01}, {0x3869, 0x01}, {0x386a, 0x01},
+ {0x386b, 0x01}, {0x386c, 0x46}, {0x386d, 0x07}, {0x386e, 0xd2},
+ {0x3871, 0x01}, {0x3872, 0x01}, {0x3873, 0x01}, {0x3874, 0x01},
+ {0x3880, 0x00}, {0x3881, 0x00}, {0x3882, 0x00}, {0x3883, 0x00},
+ {0x3884, 0x00}, {0x3885, 0x00}, {0x3886, 0x00}, {0x3887, 0x00},
+ {0x3888, 0x00}, {0x3889, 0x00}, {0x3900, 0x13}, {0x3901, 0x19},
+ {0x3902, 0x05}, {0x3903, 0x00}, {0x3904, 0x00}, {0x3908, 0x00},
+ {0x3909, 0x18}, {0x390a, 0x00}, {0x390b, 0x11}, {0x390c, 0x15},
+ {0x390d, 0x84}, {0x390f, 0x88}, {0x3910, 0x00}, {0x3911, 0x00},
+ {0x3912, 0x03}, {0x3913, 0x62}, {0x3914, 0x00}, {0x3915, 0x06},
+ {0x3916, 0x0c}, {0x3917, 0x81}, {0x3918, 0xc8}, {0x3919, 0x94},
+ {0x391a, 0x17}, {0x391b, 0x05}, {0x391c, 0x81}, {0x391d, 0x05},
+ {0x391e, 0x81}, {0x391f, 0x05}, {0x3920, 0x81}, {0x3921, 0x14},
+ {0x3922, 0x0b}, {0x3929, 0x00}, {0x392a, 0x00}, {0x392b, 0xc8},
+ {0x392c, 0x81}, {0x392f, 0x00}, {0x3930, 0x00}, {0x3931, 0x00},
+ {0x3932, 0x00}, {0x3933, 0x00}, {0x3934, 0x1b}, {0x3935, 0xc0},
+ {0x3936, 0x1c}, {0x3937, 0x21}, {0x3938, 0x0d}, {0x3939, 0x92},
+ {0x393a, 0x85}, {0x393b, 0x8a}, {0x393c, 0x06}, {0x393d, 0x8b},
+ {0x393e, 0x0f}, {0x393f, 0x14}, {0x3940, 0x0f}, {0x3941, 0x14},
+ {0x3945, 0xc0}, {0x3946, 0x05}, {0x3947, 0xc0}, {0x3948, 0x01},
+ {0x3949, 0x00}, {0x394a, 0x00}, {0x394b, 0x0b}, {0x394c, 0x0c},
+ {0x394d, 0x0b}, {0x394e, 0x09}, {0x3951, 0xc7}, {0x3952, 0x0f},
+ {0x3953, 0x0f}, {0x3954, 0x0f}, {0x3955, 0x00}, {0x3956, 0x27},
+ {0x3957, 0x27}, {0x3958, 0x27}, {0x3959, 0x01}, {0x395a, 0x02},
+ {0x395b, 0x14}, {0x395c, 0x36}, {0x395e, 0xc0}, {0x3964, 0x55},
+ {0x3965, 0x55}, {0x3966, 0x88}, {0x3967, 0x88}, {0x3968, 0x66},
+ {0x3969, 0x66}, {0x396d, 0x80}, {0x396e, 0xff}, {0x396f, 0x10},
+ {0x3970, 0x80}, {0x3971, 0x80}, {0x3972, 0x00}, {0x397a, 0x55},
+ {0x397b, 0x10}, {0x397c, 0x10}, {0x397d, 0x10}, {0x397e, 0x10},
+ {0x3980, 0xfc}, {0x3981, 0xfc}, {0x3982, 0x66}, {0x3983, 0xfc},
+ {0x3984, 0xfc}, {0x3985, 0x66}, {0x3986, 0x00}, {0x3987, 0x00},
+ {0x3988, 0x00}, {0x3989, 0x00}, {0x398a, 0x00}, {0x398b, 0x00},
+ {0x398c, 0x00}, {0x398d, 0x00}, {0x398e, 0x00}, {0x398f, 0x00},
+ {0x3990, 0x00}, {0x3991, 0x00}, {0x3992, 0x00}, {0x3993, 0x00},
+ {0x3994, 0x00}, {0x3995, 0x00}, {0x3996, 0x00}, {0x3997, 0x0f},
+ {0x3998, 0x0c}, {0x3999, 0x0c}, {0x399a, 0x0c}, {0x399b, 0xf0},
+ {0x399c, 0x14}, {0x399d, 0x0d}, {0x399e, 0x00}, {0x399f, 0x0c},
+ {0x39a0, 0x0c}, {0x39a1, 0x0c}, {0x39a2, 0x00}, {0x39a3, 0x0f},
+ {0x39a4, 0x0c}, {0x39a5, 0x0c}, {0x39a6, 0x0c}, {0x39a7, 0x0c},
+ {0x39a8, 0x0f}, {0x39a9, 0xff}, {0x39aa, 0xbf}, {0x39ab, 0x3f},
+ {0x39ac, 0x7e}, {0x39ad, 0xff}, {0x39ae, 0x00}, {0x39af, 0x00},
+ {0x39b0, 0x00}, {0x39b1, 0x00}, {0x39b2, 0x00}, {0x39b3, 0x00},
+ {0x39b4, 0x00}, {0x39b5, 0x00}, {0x39b6, 0x00}, {0x39b7, 0x00},
+ {0x39b8, 0x00}, {0x39b9, 0x00}, {0x39ba, 0x00}, {0x39bb, 0x00},
+ {0x39bc, 0x00}, {0x39c2, 0x00}, {0x39c3, 0x00}, {0x39c4, 0x00},
+ {0x39c5, 0x00}, {0x39c7, 0x00}, {0x39c8, 0x00}, {0x39c9, 0x00},
+ {0x39ca, 0x01}, {0x39cb, 0x00}, {0x39cc, 0x85}, {0x39cd, 0x09},
+ {0x39cf, 0x04}, {0x39d0, 0x85}, {0x39d1, 0x09}, {0x39d2, 0x04},
+ {0x39d4, 0x02}, {0x39d5, 0x0e}, {0x39db, 0x00}, {0x39dc, 0x01},
+ {0x39dd, 0x0c}, {0x39e5, 0xff}, {0x39e6, 0xff}, {0x39fa, 0x38},
+ {0x39fb, 0x07}, {0x39ff, 0x00}, {0x3a05, 0x00}, {0x3a06, 0x07},
+ {0x3a07, 0x0d}, {0x3a08, 0x08}, {0x3a09, 0xb2}, {0x3a0a, 0x0a},
+ {0x3a0b, 0x3c}, {0x3a0c, 0x0b}, {0x3a0d, 0xe1}, {0x3a0e, 0x03},
+ {0x3a0f, 0x85}, {0x3a10, 0x0b}, {0x3a11, 0xff}, {0x3a12, 0x00},
+ {0x3a13, 0x01}, {0x3a14, 0x0c}, {0x3a15, 0x04}, {0x3a17, 0x09},
+ {0x3a18, 0x20}, {0x3a19, 0x09}, {0x3a1a, 0x9d}, {0x3a1b, 0x09},
+ {0x3a1e, 0x34}, {0x3a1f, 0x09}, {0x3a20, 0x89}, {0x3a21, 0x09},
+ {0x3a48, 0xbe}, {0x3a52, 0x00}, {0x3a53, 0x01}, {0x3a54, 0x0c},
+ {0x3a55, 0x04}, {0x3a58, 0x0c}, {0x3a59, 0x04}, {0x3a5a, 0x01},
+ {0x3a5b, 0x00}, {0x3a5c, 0x01}, {0x3a5d, 0xe8}, {0x3a62, 0x03},
+ {0x3a63, 0x86}, {0x3a64, 0x0b}, {0x3a65, 0xbe}, {0x3a6a, 0xdc},
+ {0x3a6b, 0x0b}, {0x3a6c, 0x1a}, {0x3a6d, 0x06}, {0x3a6e, 0x01},
+ {0x3a6f, 0x04}, {0x3a70, 0xdc}, {0x3a71, 0x0b}, {0x3a83, 0x10},
+ {0x3a84, 0x00}, {0x3a85, 0x08}, {0x3a87, 0x00}, {0x3a88, 0x6b},
+ {0x3a89, 0x01}, {0x3a8a, 0x53}, {0x3a8f, 0x00}, {0x3a90, 0x00},
+ {0x3a91, 0x00}, {0x3a92, 0x00}, {0x3a93, 0x60}, {0x3a94, 0xea},
+ {0x3a98, 0x00}, {0x3a99, 0x31}, {0x3a9a, 0x01}, {0x3a9b, 0x04},
+ {0x3a9c, 0xdc}, {0x3a9d, 0x0b}, {0x3aa4, 0x0f}, {0x3aad, 0x00},
+ {0x3aae, 0x3e}, {0x3aaf, 0x02}, {0x3ab0, 0x77}, {0x3ab2, 0x00},
+ {0x3ab3, 0x08}, {0x3ab6, 0x0b}, {0x3ab7, 0xff}, {0x3aba, 0x0b},
+ {0x3abb, 0xfa}, {0x3abd, 0x05}, {0x3abe, 0x09}, {0x3abf, 0x1e},
+ {0x3ac0, 0x00}, {0x3ac1, 0x63}, {0x3ac2, 0x01}, {0x3ac3, 0x55},
+ {0x3ac8, 0x00}, {0x3ac9, 0x2a}, {0x3aca, 0x01}, {0x3acb, 0x36},
+ {0x3acc, 0x00}, {0x3acd, 0x6f}, {0x3ad0, 0x00}, {0x3ad1, 0x79},
+ {0x3ad2, 0x02}, {0x3ad3, 0x59}, {0x3ad4, 0x06}, {0x3ad5, 0x5a},
+ {0x3ad6, 0x08}, {0x3ad7, 0x3a}, {0x3ad8, 0x00}, {0x3ad9, 0x79},
+ {0x3ada, 0x02}, {0x3adb, 0x59}, {0x3adc, 0x09}, {0x3add, 0x89},
+ {0x3ade, 0x0b}, {0x3adf, 0x69}, {0x3ae0, 0x03}, {0x3ae1, 0xc1},
+ {0x3ae2, 0x0b}, {0x3ae3, 0xaf}, {0x3ae4, 0x00}, {0x3ae5, 0x3e},
+ {0x3ae6, 0x02}, {0x3ae7, 0x77}, {0x3ae8, 0x00}, {0x3aea, 0x0b},
+ {0x3aeb, 0xbe}, {0x3aee, 0x08}, {0x3aef, 0x80}, {0x3af0, 0x09},
+ {0x3af1, 0x70}, {0x3af2, 0x08}, {0x3af3, 0x94}, {0x3af4, 0x09},
+ {0x3af5, 0x5c}, {0x3af6, 0x03}, {0x3af7, 0x85}, {0x3af8, 0x08},
+ {0x3af9, 0x80}, {0x3afa, 0x0b}, {0x3afb, 0xaf}, {0x3afc, 0x01},
+ {0x3afd, 0x5a}, {0x3b1e, 0x00}, {0x3b20, 0xa5}, {0x3b21, 0x00},
+ {0x3b22, 0x00}, {0x3b23, 0x00}, {0x3b24, 0x05}, {0x3b25, 0x00},
+ {0x3b26, 0x00}, {0x3b27, 0x00}, {0x3b28, 0x1a}, {0x3b2f, 0x40},
+ {0x3b40, 0x08}, {0x3b41, 0x70}, {0x3b42, 0x05}, {0x3b43, 0xf0},
+ {0x3b44, 0x01}, {0x3b45, 0x54}, {0x3b46, 0x01}, {0x3b47, 0x54},
+ {0x3b56, 0x08}, {0x3b80, 0x00}, {0x3b81, 0x00}, {0x3b82, 0x64},
+ {0x3b83, 0x00}, {0x3b84, 0x00}, {0x3b85, 0x64}, {0x3b9d, 0x61},
+ {0x3ba8, 0x38}, {0x3c11, 0x33}, {0x3c12, 0x3d}, {0x3c13, 0x00},
+ {0x3c14, 0xbe}, {0x3c15, 0x0b}, {0x3c16, 0xa8}, {0x3c17, 0x03},
+ {0x3c18, 0x9c}, {0x3c19, 0x0b}, {0x3c1a, 0x0f}, {0x3c1b, 0x97},
+ {0x3c1c, 0x00}, {0x3c1d, 0x3c}, {0x3c1e, 0x02}, {0x3c1f, 0x78},
+ {0x3c20, 0x06}, {0x3c21, 0x80}, {0x3c22, 0x08}, {0x3c23, 0x0f},
+ {0x3c24, 0x97}, {0x3c25, 0x00}, {0x3c26, 0x3c}, {0x3c27, 0x02},
+ {0x3c28, 0xa7}, {0x3c29, 0x09}, {0x3c2a, 0xaf}, {0x3c2b, 0x0b},
+ {0x3c2c, 0x38}, {0x3c2d, 0xf9}, {0x3c2e, 0x0b}, {0x3c2f, 0xfd},
+ {0x3c30, 0x05}, {0x3c35, 0x8c}, {0x3c3e, 0xc3}, {0x3c43, 0xcb},
+ {0x3c44, 0x00}, {0x3c45, 0xff}, {0x3c46, 0x0b}, {0x3c48, 0x3b},
+ {0x3c49, 0x40}, {0x3c4a, 0x00}, {0x3c4b, 0x5b}, {0x3c4c, 0x02},
+ {0x3c4d, 0x02}, {0x3c4e, 0x00}, {0x3c4f, 0x04}, {0x3c50, 0x0c},
+ {0x3c51, 0x00}, {0x3c52, 0x3b}, {0x3c53, 0x3a}, {0x3c54, 0x07},
+ {0x3c55, 0x9e}, {0x3c56, 0x07}, {0x3c57, 0x9e}, {0x3c58, 0x07},
+ {0x3c59, 0xe8}, {0x3c5a, 0x03}, {0x3c5b, 0x33}, {0x3c5c, 0xa8},
+ {0x3c5d, 0x07}, {0x3c5e, 0xd0}, {0x3c5f, 0x07}, {0x3c60, 0x32},
+ {0x3c61, 0x00}, {0x3c62, 0xd0}, {0x3c63, 0x07}, {0x3c64, 0x80},
+ {0x3c65, 0x80}, {0x3c66, 0x3f}, {0x3c67, 0x01}, {0x3c68, 0x00},
+ {0x3c69, 0xd0}, {0x3c6a, 0x07}, {0x3c6b, 0x01}, {0x3c6c, 0x00},
+ {0x3c6d, 0xcd}, {0x3c6e, 0x07}, {0x3c6f, 0xd1}, {0x3c70, 0x07},
+ {0x3c71, 0x01}, {0x3c72, 0x00}, {0x3c73, 0xc3}, {0x3c74, 0x01},
+ {0x3c75, 0x00}, {0x3c76, 0xcd}, {0x3c77, 0x07}, {0x3c78, 0xea},
+ {0x3c79, 0x03}, {0x3c7a, 0xcd}, {0x3c7b, 0x07}, {0x3c7c, 0x08},
+ {0x3c7d, 0x06}, {0x3c7e, 0x03}, {0x3c85, 0x3a}, {0x3c86, 0x08},
+ {0x3c87, 0x69}, {0x3c88, 0x0b}, {0x3c8f, 0xb2}, {0x3c90, 0x08},
+ {0x3c91, 0xe1}, {0x3c92, 0x0b}, {0x3c93, 0x06}, {0x3c94, 0x03},
+ {0x3c9b, 0x35}, {0x3c9c, 0x08}, {0x3c9d, 0x64}, {0x3c9e, 0x0b},
+ {0x3ca5, 0xb7}, {0x3ca6, 0x08}, {0x3ca7, 0xe6}, {0x3ca8, 0x0b},
+ {0x3ca9, 0x83}, {0x3caa, 0x3c}, {0x3cab, 0x01}, {0x3cac, 0x00},
+ {0x3cad, 0x9e}, {0x3cae, 0x07}, {0x3caf, 0x85}, {0x3cb0, 0x03},
+ {0x3cb1, 0xbc}, {0x3cb2, 0x0b}, {0x3cb7, 0x3c}, {0x3cb8, 0x01},
+ {0x3cb9, 0x00}, {0x3cba, 0xbc}, {0x3cbb, 0x07}, {0x3cbc, 0xa3},
+ {0x3cbd, 0x03}, {0x3cbe, 0x9e}, {0x3cbf, 0x0b}, {0x3cc4, 0x99},
+ {0x3cc5, 0xe9}, {0x3cc6, 0x99}, {0x3cc7, 0xe9}, {0x3cc8, 0x33},
+ {0x3cc9, 0x03}, {0x3cca, 0x33}, {0x3ccb, 0x03}, {0x3cce, 0x66},
+ {0x3ccf, 0x66}, {0x3cd0, 0x00}, {0x3cd1, 0x04}, {0x3cd2, 0xf4},
+ {0x3cd3, 0xb7}, {0x3cd4, 0x03}, {0x3cd5, 0x10}, {0x3cd6, 0x06},
+ {0x3cd7, 0x30}, {0x3cd8, 0x08}, {0x3cd9, 0x5f}, {0x3cda, 0x0b},
+ {0x3cdd, 0x44}, {0x3cde, 0x44}, {0x3cdf, 0x04}, {0x3ce0, 0x00},
+ {0x3ce1, 0x00}, {0x3ce3, 0x00}, {0x3ce4, 0x00}, {0x3ce5, 0x00},
+ {0x3ce6, 0x00}, {0x3ce7, 0x00}, {0x3ce8, 0x00}, {0x3ce9, 0x00},
+ {0x3cea, 0x00}, {0x3ceb, 0x00}, {0x3cec, 0x00}, {0x3ced, 0x00},
+ {0x3cee, 0x00}, {0x3cef, 0x85}, {0x3cf0, 0x03}, {0x3cf1, 0xaf},
+ {0x3cf2, 0x0b}, {0x3cf3, 0x03}, {0x3cf4, 0x2c}, {0x3cf5, 0x00},
+ {0x3cf6, 0x42}, {0x3cf7, 0x00}, {0x3cf8, 0x03}, {0x3cf9, 0x2c},
+ {0x3cfa, 0x00}, {0x3cfb, 0x42}, {0x3cfc, 0x00}, {0x3cfd, 0x03},
+ {0x3cfe, 0x01}, {0x3d81, 0x00}, {0x3e94, 0x0f}, {0x3e95, 0x5f},
+ {0x3e96, 0x02}, {0x3e97, 0x3c}, {0x3e98, 0x00}, {0x3e9f, 0x00},
+ {0x3f00, 0x00}, {0x3f05, 0x03}, {0x3f07, 0x01}, {0x3f08, 0x55},
+ {0x3f09, 0x25}, {0x3f0a, 0x35}, {0x3f0b, 0x20}, {0x3f11, 0x05},
+ {0x3f12, 0x05}, {0x3f40, 0x00}, {0x3f41, 0x03}, {0x3f43, 0x10},
+ {0x3f44, 0x02}, {0x3f45, 0xe6}, {0x4000, 0xf9}, {0x4001, 0x2b},
+ {0x4008, 0x04}, {0x4009, 0x1b}, {0x400a, 0x03}, {0x400e, 0x10},
+ {0x4010, 0x04}, {0x4011, 0xf7}, {0x4032, 0x3e}, {0x4033, 0x02},
+ {0x4050, 0x02}, {0x4051, 0x0d}, {0x40f9, 0x00}, {0x4200, 0x00},
+ {0x4204, 0x00}, {0x4205, 0x00}, {0x4206, 0x00}, {0x4207, 0x00},
+ {0x4208, 0x00}, {0x4244, 0x00}, {0x4300, 0x00}, {0x4301, 0xff},
+ {0x4302, 0xf0}, {0x4303, 0x00}, {0x4304, 0xff}, {0x4305, 0xf0},
+ {0x4306, 0x00}, {0x4308, 0x00}, {0x430a, 0x90}, {0x430b, 0x11},
+ {0x4310, 0x00}, {0x4316, 0x00}, {0x431c, 0x00}, {0x431e, 0x00},
+ {0x4410, 0x08}, {0x4433, 0x08}, {0x4434, 0xf8}, {0x4508, 0x80},
+ {0x4509, 0x10}, {0x450b, 0x83}, {0x4511, 0x00}, {0x4580, 0x09},
+ {0x4587, 0x00}, {0x458c, 0x00}, {0x4640, 0x00}, {0x4641, 0xc1},
+ {0x4642, 0x00}, {0x4643, 0x00}, {0x4649, 0x00}, {0x4681, 0x04},
+ {0x4682, 0x10}, {0x4683, 0xa0}, {0x4698, 0x07}, {0x4699, 0xf0},
+ {0x4710, 0x00}, {0x4802, 0x00}, {0x481b, 0x3c}, {0x4837, 0x10},
+ {0x4860, 0x00}, {0x4883, 0x00}, {0x4884, 0x09}, {0x4885, 0x80},
+ {0x4886, 0x00}, {0x4888, 0x10}, {0x488b, 0x00}, {0x488c, 0x10},
+ {0x4980, 0x03}, {0x4981, 0x06}, {0x4984, 0x00}, {0x4985, 0x00},
+ {0x4a14, 0x04}, {0x4b01, 0x44}, {0x4b03, 0x80}, {0x4d06, 0xc8},
+ {0x4d09, 0xdf}, {0x4d15, 0x7d}, {0x4d34, 0x7d}, {0x4d3c, 0x7d},
+ {0x4f00, 0x7f}, {0x4f01, 0xff}, {0x4f03, 0x00}, {0x4f04, 0x18},
+ {0x4f05, 0x13}, {0x5000, 0x6e}, {0x5001, 0x00}, {0x500a, 0x00},
+ {0x5080, 0x00}, {0x5081, 0x00}, {0x5082, 0x00}, {0x5083, 0x00},
+ {0x5100, 0x00}, {0x5103, 0x00}, {0x5180, 0x70}, {0x5181, 0x70},
+ {0x5182, 0x73}, {0x5183, 0xff}, {0x5249, 0x06}, {0x524f, 0x06},
+ {0x5281, 0x18}, {0x5282, 0x08}, {0x5283, 0x08}, {0x5284, 0x18},
+ {0x5285, 0x18}, {0x5286, 0x08}, {0x5287, 0x08}, {0x5288, 0x18},
+ {0x5289, 0x2d}, {0x6000, 0x40}, {0x6001, 0x40}, {0x6002, 0x00},
+ {0x6003, 0x00}, {0x6004, 0x00}, {0x6005, 0x00}, {0x6006, 0x00},
+ {0x6007, 0x00}, {0x6008, 0x00}, {0x6009, 0x00}, {0x600a, 0x00},
+ {0x600b, 0x00}, {0x600c, 0x02}, {0x600d, 0x00}, {0x600e, 0x04},
+ {0x600f, 0x00}, {0x6010, 0x06}, {0x6011, 0x00}, {0x6012, 0x00},
+ {0x6013, 0x00}, {0x6014, 0x02}, {0x6015, 0x00}, {0x6016, 0x04},
+ {0x6017, 0x00}, {0x6018, 0x06}, {0x6019, 0x00}, {0x601a, 0x01},
+ {0x601b, 0x00}, {0x601c, 0x01}, {0x601d, 0x00}, {0x601e, 0x01},
+ {0x601f, 0x00}, {0x6020, 0x01}, {0x6021, 0x00}, {0x6022, 0x01},
+ {0x6023, 0x00}, {0x6024, 0x01}, {0x6025, 0x00}, {0x6026, 0x01},
+ {0x6027, 0x00}, {0x6028, 0x01}, {0x6029, 0x00}, {0x3501, 0x08},
+ {0x3502, 0x32}, {0x320a, 0x01}, {0x320b, 0x01}, {0x320c, 0x00},
+ {0x320d, 0x00},
+};
+
+struct ox05b {
+ struct device *dev;
+ struct clk *clk;
+ unsigned long clk_rate;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct gpio_desc *pwdn_gpio;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_ctrl_handler handler;
+ /* Added for RGB dominant stream */
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *again;
+ struct v4l2_ctrl *dgain;
+ /* Added for IR dominant stream */
+ struct v4l2_ctrl *ir_exposure;
+ struct v4l2_ctrl *ir_again;
+ struct v4l2_ctrl *ir_dgain;
+ u32 fps;
+ struct mutex lock; /* For streaming status */
+ bool streaming;
+ struct v4l2_ctrl *link_freq;
+};
+
+static inline struct ox05b *to_ox05b(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ox05b, subdev);
+}
+
+static int ox05b_read(struct ox05b *ox05b, u16 addr, u32 *val, size_t nbytes)
+{
+ int ret;
+ __le32 val_le = 0;
+
+ ret = regmap_bulk_read(ox05b->regmap, addr, &val_le, nbytes);
+ if (ret < 0) {
+ dev_err(ox05b->dev, "%s: failed to read reg 0x%04x: %d\n",
+ __func__, addr, ret);
+ return ret;
+ }
+
+ *val = le32_to_cpu(val_le);
+ return 0;
+}
+
+static int ox05b_write(struct ox05b *ox05b, u16 addr, u32 val, size_t nbytes)
+{
+ int ret;
+ __le32 val_le = cpu_to_le32(val);
+
+ ret = regmap_bulk_write(ox05b->regmap, addr, &val_le, nbytes);
+ if (ret < 0) {
+ dev_err(ox05b->dev, "%s: failed to write reg 0x%04x: %d\n",
+ __func__, addr, ret);
+ }
+ return ret;
+}
+
+static int ox05b_write_table(struct ox05b *ox05b,
+ const struct reg_sequence *regs,
+ unsigned int nr_regs)
+{
+ int ret;
+
+ ret = regmap_multi_reg_write(ox05b->regmap, regs, nr_regs);
+ if (ret < 0) {
+ dev_err(ox05b->dev, "%s: failed to write reg table (%d)!\n",
+ __func__, ret);
+ }
+ return ret;
+}
+
+static void ox05b_init_formats(struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *format;
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ format = v4l2_subdev_state_get_stream_format(state, 0, i);
+ format->code = ox05b_mbus_formats[0];
+ format->width = ox05b_framesizes[0].width;
+ format->height = ox05b_framesizes[0].height;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+ }
+}
+
+static int ox05b_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ox05b *ox05b = to_ox05b(sd);
+ struct v4l2_mbus_framefmt *format;
+ const struct v4l2_area *fsize;
+ u32 code;
+ int ret = 0;
+
+ if (fmt->pad != 0)
+ return -EINVAL;
+
+ if (fmt->stream != 0)
+ return -EINVAL;
+
+ /* Sensor only supports a single format. */
+ code = ox05b_mbus_formats[0];
+
+ /* Find the nearest supported frame size. */
+ fsize = v4l2_find_nearest_size(ox05b_framesizes,
+ ARRAY_SIZE(ox05b_framesizes), width,
+ height, fmt->format.width,
+ fmt->format.height);
+
+ format = v4l2_subdev_state_get_stream_format(state, fmt->pad, fmt->stream);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && ox05b->streaming)
+ ret = -EBUSY;
+
+ format->code = code;
+ format->width = fsize->width;
+ format->height = fsize->height;
+
+ fmt->format = *format;
+
+ return ret;
+}
+
+static int _ox05b_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .source_pad = 0,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ {
+ .source_pad = 0,
+ .source_stream = 1,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ int ret;
+
+ ret = v4l2_subdev_set_routing(sd, state, &routing);
+ if (ret < 0)
+ return ret;
+
+ ox05b_init_formats(state);
+
+ return 0;
+}
+
+static int ox05b_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+ u32 bpp;
+ int ret = 0;
+ unsigned int i;
+
+ if (pad != 0)
+ return -EINVAL;
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ fmt = v4l2_subdev_state_get_stream_format(state, 0, 0);
+ if (!fmt) {
+ ret = -EPIPE;
+ goto out;
+ }
+ memset(fd, 0, sizeof(*fd));
+
+ fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+
+ /* pixel stream - 2 virtual channels*/
+
+ bpp = 10;
+
+ for (i = 0; i < 2; ++i) {
+ fd->entry[fd->num_entries].stream = i;
+
+ fd->entry[fd->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
+ fd->entry[fd->num_entries].length = fmt->width * fmt->height * bpp / 8;
+ fd->entry[fd->num_entries].pixelcode = fmt->code;
+ fd->entry[fd->num_entries].bus.csi2.vc = i;
+ fd->entry[fd->num_entries].bus.csi2.dt = 0x2b; /* RAW10 */
+
+ fd->num_entries++;
+ }
+
+out:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static int ox05b_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ if (routing->num_routes == 0 || routing->num_routes > 2)
+ return -EINVAL;
+
+ v4l2_subdev_lock_state(state);
+
+ ret = _ox05b_set_routing(sd, state);
+
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static int ox05b_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ int ret;
+
+ ret = _ox05b_set_routing(sd, state);
+
+ return ret;
+}
+
+static int ox05b_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(ox05b_mbus_formats))
+ return -EINVAL;
+
+ code->code = ox05b_mbus_formats[code->index];
+
+ return 0;
+}
+
+static int ox05b_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ox05b_mbus_formats); ++i) {
+ if (ox05b_mbus_formats[i] == fse->code)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(ox05b_mbus_formats))
+ return -EINVAL;
+
+ if (fse->index >= ARRAY_SIZE(ox05b_framesizes))
+ return -EINVAL;
+
+ fse->min_width = ox05b_framesizes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->max_height = ox05b_framesizes[fse->index].height;
+ fse->min_height = fse->max_height;
+
+ return 0;
+}
+
+static int ox05b_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct ox05b *ox05b = to_ox05b(sd);
+
+ fi->interval.numerator = 1;
+ fi->interval.denominator = ox05b->fps;
+ return 0;
+}
+
+static int ox05b_set_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct ox05b *ox05b = to_ox05b(sd);
+
+ dev_dbg(ox05b->dev, "%s: Set framerate %dfps\n", __func__,
+ fi->interval.denominator / fi->interval.numerator);
+ if ((fi->interval.denominator / fi->interval.numerator) != ox05b->fps) {
+ dev_err(ox05b->dev, "%s: Framerate can only be %dfps\n",
+ __func__, ox05b->fps);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ox05b_detect(struct ox05b *ox05b)
+{
+ int ret;
+ u32 id;
+
+ ret = ox05b_read(ox05b, OX05B_SC_CHIP_ID_HI, &id, 2);
+ if (ret < 0)
+ return ret;
+
+ if (id != OX05B_CHIP_ID) {
+ dev_err(ox05b->dev,
+ "%s: unknown chip ID 0x%04x\n", __func__, id);
+ return -ENODEV;
+ }
+
+ dev_info(ox05b->dev, "%s: detected chip ID 0x%04x\n", __func__, id);
+ return 0;
+}
+
+static int ox05b_set_groupA(struct ox05b *ox05b)
+{
+ int i, ret;
+ u32 exposure = ox05b->ir_exposure->val;
+ u32 again = ox05b->ir_again->val;
+ u32 dgain = ox05b->ir_dgain->val;
+ struct reg_sequence ox05b_groupA[] = {
+ {0x3208, 0x01}, /* Group 1 (IR Dominant VC0) hold start */
+ {OX05B_AEC_PK_EXPO_HI, (exposure >> 8) & 0xff}, /* Exposure time Hi */
+ {OX05B_AEC_PK_EXPO_LO, exposure & 0xff}, /* Exposure time Low */
+ {OX05B_AEC_PK_AGAIN_HI, (again >> 4) & 0xff}, /* Analog gain Hi */
+ {OX05B_AEC_PK_AGAIN_LO, (again & 0x0f) << 4}, /* Analog gain Low */
+ {OX05B_AEC_PK_DGAIN_HI, (dgain >> 8) & 0xff}, /* Digital gain Hi */
+ {OX05B_AEC_PK_DGAIN_LO, dgain & 0xff}, /* Digital gain Lo */
+ {0x4813, 0x01}, /* VC=1. This register takes effect from next frame. */
+ {0x3208, 0x11}, /* Group 1 (IR Dominant VC0) hold end*/
+ };
+
+ for (i = 0; i < ARRAY_SIZE(ox05b_groupA); i++) {
+ ret = regmap_write(ox05b->regmap, ox05b_groupA[i].reg, ox05b_groupA[i].def);
+ if (ret < 0) {
+ dev_err(ox05b->dev,
+ "%s: failed to write reg[%d] 0x%04x = 0x%02x (%d)!\n",
+ __func__, i, ox05b_groupA[i].reg, ox05b_groupA[i].def, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ox05b_set_groupB(struct ox05b *ox05b)
+{
+ int i, ret;
+ u32 exposure = ox05b->exposure->val;
+ u32 again = ox05b->again->val;
+ u32 dgain = ox05b->dgain->val;
+ struct reg_sequence ox05b_groupB[] = {
+ {0x3208, 0x00}, /* Group 0 (RGB Dominant VC1) hold start */
+ {OX05B_AEC_PK_EXPO_HI, (exposure >> 8) & 0xff}, /* Exposure time Hi */
+ {OX05B_AEC_PK_EXPO_LO, exposure & 0xff}, /* Exposure time Low */
+ {OX05B_AEC_PK_AGAIN_HI, (again >> 4) & 0xff}, /* Analog gain Hi */
+ {OX05B_AEC_PK_AGAIN_LO, (again & 0x0f) << 4}, /* Analog gain Low */
+ {OX05B_AEC_PK_DGAIN_HI, (dgain >> 8) & 0xff}, /* Digital gain Hi */
+ {OX05B_AEC_PK_DGAIN_LO, dgain & 0xff}, /* Digital gain Lo */
+ {0x4813, 0x00}, /* VC=0. This register takes effect from next frame. */
+ {0x3208, 0x10}, /* Group 0 (RGB Dominant VC1) hold end*/
+ };
+
+ for (i = 0; i < ARRAY_SIZE(ox05b_groupB); i++) {
+ ret = regmap_write(ox05b->regmap, ox05b_groupB[i].reg, ox05b_groupB[i].def);
+ if (ret < 0) {
+ dev_err(ox05b->dev,
+ "%s: failed to write reg[%d] 0x%04x = 0x%02x (%d)!\n",
+ __func__, i, ox05b_groupB[i].reg, ox05b_groupB[i].def, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ox05b_set_AB_mode_regs(struct ox05b *ox05b)
+{
+ int i, ret;
+ struct reg_sequence ox5b_AB_mode_regs[] = {
+ {0x3211, 0xF1}, /* AB mode enable */
+ {0x3212, 0x21}, /* Enable sync between holds of group 0 and group 1*/
+ {0x3208, 0xA0}, /* Always use for repeat launch */
+ };
+
+ for (i = 0; i < ARRAY_SIZE(ox5b_AB_mode_regs); i++) {
+ ret = regmap_write(ox05b->regmap, ox5b_AB_mode_regs[i].reg,
+ ox5b_AB_mode_regs[i].def);
+ if (ret < 0) {
+ dev_err(ox05b->dev,
+ "%s: failed to write reg[%d] 0x%04x = 0x%02x (%d)!\n",
+ __func__, i, ox5b_AB_mode_regs[i].reg,
+ ox5b_AB_mode_regs[i].def, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ox05b_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ox05b *ox05b = container_of(ctrl->handler, struct ox05b, handler);
+ int ret;
+
+ dev_dbg(ox05b->dev, "%s: %s, value: %d\n", __func__,
+ ctrl->name, ctrl->val);
+
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (pm_runtime_suspended(ox05b->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ case V4L2_CID_ANALOGUE_GAIN:
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = ox05b_set_groupB(ox05b);
+ break;
+ case V4L2_CID_IR_EXPOSURE:
+ case V4L2_CID_IR_ANALOGUE_GAIN:
+ case V4L2_CID_IR_DIGITAL_GAIN:
+ ret = ox05b_set_groupA(ox05b);
+ break;
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ox05b_power_on(struct ox05b *ox05b)
+{
+ int ret;
+
+ ret = clk_prepare_enable(ox05b->clk);
+ if (ret < 0)
+ return ret;
+
+ if (ox05b->pwdn_gpio) {
+ gpiod_set_value_cansleep(ox05b->pwdn_gpio, 1);
+ usleep_range(100, 1000);
+ gpiod_set_value_cansleep(ox05b->pwdn_gpio, 0);
+ msleep(30);
+ }
+ return 0;
+}
+
+static int ox05b_power_off(struct ox05b *ox05b)
+{
+ if (ox05b->pwdn_gpio) {
+ gpiod_set_value_cansleep(ox05b->pwdn_gpio, 1);
+ usleep_range(1, 10);
+ }
+
+ clk_disable_unprepare(ox05b->clk);
+
+ return 0;
+}
+
+static int ox05b_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ox05b *ox05b = to_ox05b(sd);
+
+ return ox05b_power_on(ox05b);
+}
+
+static int ox05b_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ox05b *ox05b = to_ox05b(sd);
+
+ return ox05b_power_off(ox05b);
+}
+
+static int ox05b_start_stream(struct ox05b *ox05b)
+{
+ int ret;
+
+ ret = ox05b_write_table(ox05b, ox05b_linear_2592x1944,
+ ARRAY_SIZE(ox05b_linear_2592x1944));
+ if (ret < 0)
+ return ret;
+
+ msleep(20);
+
+ /* set registers for IR frame */
+ ret = ox05b_set_groupA(ox05b);
+ if (ret < 0)
+ return ret;
+
+ /* set registers for RGB frame */
+ ret = ox05b_set_groupB(ox05b);
+ if (ret < 0)
+ return ret;
+
+ /* set registers specific to AB mode */
+ ret = ox05b_set_AB_mode_regs(ox05b);
+
+ /* Set active */
+ ret = ox05b_write(ox05b, OX05B_SYS_MODE_SEL, 0x01, 1);
+ if (ret < 0)
+ return ret;
+
+ /* No communication is possible for a while after exiting standby.
+ * we want the sensor to have sufficient time to process
+ * the new configurations. The same type of delays have been programmed
+ * in the OV2312 driver.
+ * TODO: check if there is a status register to poll for sensor readiness.
+ */
+ msleep(20);
+
+ return 0;
+}
+
+static int ox05b_stop_stream(struct ox05b *ox05b)
+{
+ int ret;
+
+ /* Set standby */
+ ret = ox05b_write(ox05b, OX05B_SYS_MODE_SEL, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ /* No communication is possible for a while after entering standby */
+ usleep_range(10000, 20000);
+ return 0;
+}
+
+static int ox05b_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ox05b *ox05b = to_ox05b(sd);
+ int ret;
+
+ mutex_lock(&ox05b->lock);
+ if (ox05b->streaming == enable) {
+ mutex_unlock(&ox05b->lock);
+ return 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(ox05b->dev);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = ox05b_start_stream(ox05b);
+ if (ret < 0)
+ goto err_runtime_put;
+
+ } else {
+ ret = ox05b_stop_stream(ox05b);
+ if (ret < 0)
+ goto err_runtime_put;
+ pm_runtime_put(ox05b->dev);
+ }
+
+ ox05b->streaming = enable;
+
+ mutex_unlock(&ox05b->lock);
+ return 0;
+
+err_runtime_put:
+ pm_runtime_put(ox05b->dev);
+
+err_unlock:
+ mutex_unlock(&ox05b->lock);
+ dev_err(ox05b->dev,
+ "%s: failed to setup streaming %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops ox05b_subdev_video_ops = {
+ .g_frame_interval = ox05b_get_frame_interval,
+ .s_frame_interval = ox05b_set_frame_interval,
+ .s_stream = ox05b_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ox05b_subdev_pad_ops = {
+ .init_cfg = ox05b_init_cfg,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ox05b_set_fmt,
+ .enum_mbus_code = ox05b_enum_mbus_code,
+ .enum_frame_size = ox05b_enum_frame_sizes,
+ .set_routing = ox05b_set_routing,
+ .get_frame_desc = ox05b_get_frame_desc,
+};
+
+static const struct v4l2_subdev_ops ox05b_subdev_ops = {
+ .video = &ox05b_subdev_video_ops,
+ .pad = &ox05b_subdev_pad_ops,
+};
+
+static const struct v4l2_ctrl_ops ox05b_ctrl_ops = {
+ .s_ctrl = ox05b_set_ctrl,
+};
+
+static const struct dev_pm_ops ox05b_pm_ops = {
+ SET_RUNTIME_PM_OPS(ox05b_suspend, ox05b_resume, NULL)
+};
+
+static int ox05b_probe(struct i2c_client *client)
+{
+ struct ox05b *ox05b;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *ctrl_hdr;
+ int ret;
+ /* Allocate internal struct */
+ ox05b = devm_kzalloc(&client->dev, sizeof(*ox05b), GFP_KERNEL);
+ if (!ox05b)
+ return -ENOMEM;
+ ox05b->dev = &client->dev;
+
+ /* Initialize I2C Regmap */
+ ox05b->regmap = devm_regmap_init_i2c(client, &ox05b_regmap_config);
+ if (IS_ERR(ox05b->regmap))
+ return PTR_ERR(ox05b->regmap);
+
+ /* Initialize Powerdown GPIO */
+ ox05b->pwdn_gpio = devm_gpiod_get_optional(ox05b->dev, "pwdn", GPIOD_OUT_LOW);
+ if (IS_ERR(ox05b->pwdn_gpio))
+ return PTR_ERR(ox05b->pwdn_gpio);
+
+ ox05b->clk = devm_clk_get(ox05b->dev, "inck");
+ if (IS_ERR(ox05b->clk))
+ return PTR_ERR(ox05b->clk);
+
+ ox05b->clk_rate = clk_get_rate(ox05b->clk);
+
+ if (ox05b->clk_rate < 6000000 || ox05b->clk_rate > 27000000)
+ return -EINVAL;
+
+ /* Power on */
+ ret = ox05b_power_on(ox05b);
+ if (ret < 0)
+ return ret;
+
+ /* Detect sensor */
+ ret = ox05b_detect(ox05b);
+ if (ret < 0)
+ return ret;
+
+ /* Initialize the subdev and its controls. */
+ sd = &ox05b->subdev;
+ v4l2_i2c_subdev_init(sd, client, &ox05b_subdev_ops);
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS |
+ V4L2_SUBDEV_FL_STREAMS;
+
+ /* Initialize the media entity. */
+ ox05b->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ox05b->pad);
+ if (ret < 0) {
+ dev_err(ox05b->dev,
+ "%s: media entity init failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ ox05b->fps = OX05B_FRAMERATE_DEFAULT;
+ mutex_init(&ox05b->lock);
+ /* Initialize controls */
+ ctrl_hdr = &ox05b->handler;
+ ret = v4l2_ctrl_handler_init(ctrl_hdr, 7);
+ if (ret < 0) {
+ dev_err(ox05b->dev,
+ "%s: ctrl handler init failed: %d\n", __func__, ret);
+ goto err_media_cleanup;
+ }
+
+ ox05b->handler.lock = &ox05b->lock;
+
+ /* Add new controls */
+ ox05b->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdr, &ox05b_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(ox05b_link_freq_menu) - 1, 0,
+ ox05b_link_freq_menu);
+ if (ox05b->link_freq)
+ ox05b->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ox05b->exposure = v4l2_ctrl_new_std(ctrl_hdr, &ox05b_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0,
+ OX05B_EXPOSURE_MAX,
+ 1, OX05B_EXPOSURE_DEFAULT);
+
+ ox05b->again = v4l2_ctrl_new_std(ctrl_hdr, &ox05b_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, 0,
+ OX05B_AGAIN_MAX, 1,
+ OX05B_AGAIN_DEFAULT);
+ ox05b->dgain = v4l2_ctrl_new_std(ctrl_hdr, &ox05b_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN, 0,
+ OX05B_DGAIN_MAX, 1,
+ OX05B_DGAIN_DEFAULT);
+
+ /* Added new control for IR frames. */
+ ox05b->ir_exposure = v4l2_ctrl_new_std(ctrl_hdr, &ox05b_ctrl_ops,
+ V4L2_CID_IR_EXPOSURE, 0,
+ OX05B_EXPOSURE_MAX,
+ 1, OX05B_EXPOSURE_DEFAULT);
+
+ ox05b->ir_again = v4l2_ctrl_new_std(ctrl_hdr, &ox05b_ctrl_ops,
+ V4L2_CID_IR_ANALOGUE_GAIN, 0,
+ OX05B_AGAIN_MAX, 1,
+ OX05B_AGAIN_DEFAULT);
+
+ ox05b->ir_dgain = v4l2_ctrl_new_std(ctrl_hdr, &ox05b_ctrl_ops,
+ V4L2_CID_IR_DIGITAL_GAIN, 0,
+ OX05B_DGAIN_MAX, 1,
+ OX05B_DGAIN_DEFAULT);
+
+ ox05b->subdev.ctrl_handler = ctrl_hdr;
+ if (ox05b->handler.error) {
+ ret = ox05b->handler.error;
+ dev_err(ox05b->dev,
+ "%s: failed to add the ctrls: %d\n", __func__, ret);
+ goto err_ctrl_free;
+ }
+
+ /* PM Runtime */
+ pm_runtime_enable(ox05b->dev);
+ pm_runtime_set_suspended(ox05b->dev);
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret < 0) {
+ dev_err(ox05b->dev, "%s: failed to init subdev: %d\n", __func__, ret);
+ goto err_pm_disable;
+ }
+
+ /* Finally, register the subdev. */
+ ret = v4l2_async_register_subdev(sd);
+ if (ret < 0) {
+ dev_err(ox05b->dev,
+ "%s: v4l2 subdev register failed %d\n", __func__, ret);
+ goto err_subdev_cleanup;
+ }
+
+ dev_info(ox05b->dev, "ox05b1s probed!\n");
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(&ox05b->subdev);
+
+err_pm_disable:
+ pm_runtime_disable(ox05b->dev);
+
+err_ctrl_free:
+ v4l2_ctrl_handler_free(ctrl_hdr);
+ mutex_destroy(&ox05b->lock);
+
+err_media_cleanup:
+ media_entity_cleanup(&ox05b->subdev.entity);
+
+ return ret;
+}
+
+static void ox05b_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ox05b *ox05b = to_ox05b(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&ox05b->handler);
+ v4l2_subdev_cleanup(&ox05b->subdev);
+ media_entity_cleanup(&sd->entity);
+ mutex_destroy(&ox05b->lock);
+
+ pm_runtime_disable(ox05b->dev);
+}
+
+static const struct of_device_id ox05b_of_match[] = {
+ { .compatible = "ovti,ox05b" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, ox05b_of_match);
+
+static struct i2c_driver ox05b_i2c_driver = {
+ .driver = {
+ .name = "ox05b",
+ .of_match_table = ox05b_of_match,
+ .pm = &ox05b_pm_ops,
+ },
+ .probe_new = ox05b_probe,
+ .remove = ox05b_remove,
+};
+
+module_i2c_driver(ox05b_i2c_driver);
+
+MODULE_AUTHOR("Abhishek Sharma <abhishek.sharma@ti.com>");
+MODULE_DESCRIPTION("OX05B1S RGB-IR Image Sensor driver");
+MODULE_LICENSE("GPL");