diff options
Diffstat (limited to 'drivers/media/i2c/ox05b1s.c')
-rw-r--r-- | drivers/media/i2c/ox05b1s.c | 1139 |
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"); |