summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ox05b1s.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ox05b1s.c')
-rw-r--r--drivers/media/i2c/ox05b1s.c1139
1 files changed, 1139 insertions, 0 deletions
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");