xref: /linux/drivers/media/i2c/os05b10.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
13aa9296aSHimanshu Bhavani // SPDX-License-Identifier: GPL-2.0
23aa9296aSHimanshu Bhavani /*
33aa9296aSHimanshu Bhavani  * V4L2 Support for the os05b10
43aa9296aSHimanshu Bhavani  *
53aa9296aSHimanshu Bhavani  * Copyright (C) 2025 Silicon Signals Pvt. Ltd.
63aa9296aSHimanshu Bhavani  *
73aa9296aSHimanshu Bhavani  * Inspired from imx219, ov2735 camera drivers.
83aa9296aSHimanshu Bhavani  */
93aa9296aSHimanshu Bhavani 
103aa9296aSHimanshu Bhavani #include <linux/array_size.h>
113aa9296aSHimanshu Bhavani #include <linux/bitops.h>
123aa9296aSHimanshu Bhavani #include <linux/clk.h>
133aa9296aSHimanshu Bhavani #include <linux/container_of.h>
143aa9296aSHimanshu Bhavani #include <linux/delay.h>
153aa9296aSHimanshu Bhavani #include <linux/device/devres.h>
163aa9296aSHimanshu Bhavani #include <linux/err.h>
173aa9296aSHimanshu Bhavani #include <linux/gpio/consumer.h>
183aa9296aSHimanshu Bhavani #include <linux/i2c.h>
193aa9296aSHimanshu Bhavani #include <linux/module.h>
203aa9296aSHimanshu Bhavani #include <linux/pm_runtime.h>
213aa9296aSHimanshu Bhavani #include <linux/regulator/consumer.h>
223aa9296aSHimanshu Bhavani #include <linux/types.h>
233aa9296aSHimanshu Bhavani #include <linux/time.h>
243aa9296aSHimanshu Bhavani #include <linux/units.h>
253aa9296aSHimanshu Bhavani 
263aa9296aSHimanshu Bhavani #include <media/v4l2-cci.h>
273aa9296aSHimanshu Bhavani #include <media/v4l2-ctrls.h>
283aa9296aSHimanshu Bhavani #include <media/v4l2-device.h>
293aa9296aSHimanshu Bhavani #include <media/v4l2-fwnode.h>
303aa9296aSHimanshu Bhavani #include <media/v4l2-mediabus.h>
313aa9296aSHimanshu Bhavani 
323aa9296aSHimanshu Bhavani #define OS05B10_XCLK_FREQ		(24 * HZ_PER_MHZ)
333aa9296aSHimanshu Bhavani 
343aa9296aSHimanshu Bhavani #define OS05B10_REG_CHIP_ID		CCI_REG24(0x300a)
353aa9296aSHimanshu Bhavani #define OS05B10_CHIP_ID			0x530641
363aa9296aSHimanshu Bhavani 
373aa9296aSHimanshu Bhavani #define OS05B10_REG_CTRL_MODE		CCI_REG8(0x0100)
383aa9296aSHimanshu Bhavani #define OS05B10_MODE_STANDBY		0x00
393aa9296aSHimanshu Bhavani #define OS05B10_MODE_STREAMING		0x01
403aa9296aSHimanshu Bhavani 
413aa9296aSHimanshu Bhavani #define OS05B10_REG_EXPOSURE		CCI_REG24(0x3500)
423aa9296aSHimanshu Bhavani #define OS05B10_EXPOSURE_MIN		2
433aa9296aSHimanshu Bhavani #define OS05B10_EXPOSURE_STEP		1
443aa9296aSHimanshu Bhavani #define OS05B10_EXPOSURE_MARGIN		8
453aa9296aSHimanshu Bhavani 
463aa9296aSHimanshu Bhavani #define OS05B10_REG_ANALOG_GAIN		CCI_REG16(0x3508)
473aa9296aSHimanshu Bhavani #define OS05B10_ANALOG_GAIN_MIN		0x80
483aa9296aSHimanshu Bhavani #define OS05B10_ANALOG_GAIN_MAX		0x7C0
493aa9296aSHimanshu Bhavani #define OS05B10_ANALOG_GAIN_STEP	1
503aa9296aSHimanshu Bhavani #define OS05B10_ANALOG_GAIN_DEFAULT	0x80
513aa9296aSHimanshu Bhavani 
523aa9296aSHimanshu Bhavani #define OS05B10_REG_HTS			CCI_REG16(0x380c)
533aa9296aSHimanshu Bhavani 
543aa9296aSHimanshu Bhavani #define OS05B10_REG_VTS			CCI_REG16(0x380e)
553aa9296aSHimanshu Bhavani #define OS05B10_VTS_MAX			0x7fff
563aa9296aSHimanshu Bhavani 
573aa9296aSHimanshu Bhavani #define OS05B10_LINK_FREQ_600MHZ	(600 * HZ_PER_MHZ)
583aa9296aSHimanshu Bhavani 
593aa9296aSHimanshu Bhavani static const struct v4l2_rect os05b10_native_area = {
603aa9296aSHimanshu Bhavani 	.top = 0,
613aa9296aSHimanshu Bhavani 	.left = 0,
623aa9296aSHimanshu Bhavani 	.width = 2608,
633aa9296aSHimanshu Bhavani 	.height = 1960,
643aa9296aSHimanshu Bhavani };
653aa9296aSHimanshu Bhavani 
663aa9296aSHimanshu Bhavani static const struct v4l2_rect os05b10_active_area = {
673aa9296aSHimanshu Bhavani 	.top = 8,
683aa9296aSHimanshu Bhavani 	.left = 8,
693aa9296aSHimanshu Bhavani 	.width = 2592,
703aa9296aSHimanshu Bhavani 	.height = 1944,
713aa9296aSHimanshu Bhavani };
723aa9296aSHimanshu Bhavani 
733aa9296aSHimanshu Bhavani static const char * const os05b10_supply_name[] = {
743aa9296aSHimanshu Bhavani 	"avdd",		/* Analog supply */
753aa9296aSHimanshu Bhavani 	"dovdd",	/* Digital IO */
763aa9296aSHimanshu Bhavani 	"dvdd",		/* Digital core */
773aa9296aSHimanshu Bhavani };
783aa9296aSHimanshu Bhavani 
793aa9296aSHimanshu Bhavani static const struct cci_reg_sequence os05b10_common_regs[] = {
803aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x0301), 0x44 },
813aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x0303), 0x02 },
823aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x0305), 0x32 },
833aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x0306), 0x00 },
843aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x0325), 0x3b },
853aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3002), 0x21 },
863aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3016), 0x72 },
873aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x301e), 0xb4 },
883aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x301f), 0xd0 },
893aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3021), 0x03 },
903aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3022), 0x01 },
913aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3107), 0xa1 },
923aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3108), 0x7d },
933aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3109), 0xfc },
943aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3503), 0x88 },
953aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x350a), 0x04 },
963aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x350b), 0x00 },
973aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x350c), 0x00 },
983aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x350d), 0x80 },
993aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x350e), 0x04 },
1003aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x350f), 0x00 },
1013aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3510), 0x00 },
1023aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3511), 0x00 },
1033aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3512), 0x20 },
1043aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3600), 0x4d },
1053aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3601), 0x08 },
1063aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3610), 0x87 },
1073aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3611), 0x24 },
1083aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3614), 0x4c },
1093aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3620), 0x0c },
1103aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3632), 0x80 },
1113aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3633), 0x00 },
1123aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3636), 0xcc },
1133aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3637), 0x27 },
1143aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3660), 0x00 },
1153aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3662), 0x10 },
1163aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3665), 0x00 },
1173aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3666), 0x00 },
1183aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x366a), 0x14 },
1193aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3670), 0x0b },
1203aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3671), 0x0b },
1213aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3672), 0x0b },
1223aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3673), 0x0b },
1233aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3678), 0x2b },
1243aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x367a), 0x11 },
1253aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x367b), 0x11 },
1263aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x367c), 0x11 },
1273aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x367d), 0x11 },
1283aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3681), 0xff },
1293aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3682), 0x86 },
1303aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3683), 0x44 },
1313aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3684), 0x24 },
1323aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3685), 0x00 },
1333aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x368a), 0x00 },
1343aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x368d), 0x2b },
1353aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x368e), 0x2b },
1363aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3690), 0x00 },
1373aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3691), 0x0b },
1383aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3692), 0x0b },
1393aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3693), 0x0b },
1403aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3694), 0x0b },
1413aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x369d), 0x68 },
1423aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x369e), 0x34 },
1433aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x369f), 0x1b },
1443aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36a0), 0x0f },
1453aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36a1), 0x77 },
1463aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b0), 0x30 },
1473aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b2), 0x00 },
1483aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b3), 0x00 },
1493aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b4), 0x00 },
1503aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b5), 0x00 },
1513aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b6), 0x00 },
1523aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b7), 0x00 },
1533aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b8), 0x00 },
1543aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36b9), 0x00 },
1553aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36ba), 0x00 },
1563aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36bb), 0x00 },
1573aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36bc), 0x00 },
1583aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36bd), 0x00 },
1593aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36be), 0x00 },
1603aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36bf), 0x00 },
1613aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c0), 0x01 },
1623aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c1), 0x00 },
1633aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c2), 0x00 },
1643aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c3), 0x00 },
1653aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c4), 0x00 },
1663aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c5), 0x00 },
1673aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c6), 0x00 },
1683aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c7), 0x00 },
1693aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c8), 0x00 },
1703aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36c9), 0x00 },
1713aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36ca), 0x0e },
1723aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36cb), 0x0e },
1733aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36cc), 0x0e },
1743aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36cd), 0x0e },
1753aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36ce), 0x0c },
1763aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36cf), 0x0c },
1773aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d0), 0x0c },
1783aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d1), 0x0c },
1793aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d2), 0x00 },
1803aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d3), 0x08 },
1813aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d4), 0x10 },
1823aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d5), 0x10 },
1833aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d6), 0x00 },
1843aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d7), 0x08 },
1853aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d8), 0x10 },
1863aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x36d9), 0x10 },
1873aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3701), 0x1d },
1883aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3703), 0x2a },
1893aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3704), 0x05 },
1903aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3709), 0x57 },
1913aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x370b), 0x63 },
1923aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3706), 0x28 },
1933aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x370a), 0x00 },
1943aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x370b), 0x63 },
1953aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x370e), 0x0c },
1963aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x370f), 0x1c },
1973aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3710), 0x00 },
1983aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3713), 0x00 },
1993aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3714), 0x24 },
2003aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3716), 0x24 },
2013aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x371a), 0x1e },
2023aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3724), 0x09 },
2033aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3725), 0xb2 },
2043aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x372b), 0x54 },
2053aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3730), 0xe1 },
2063aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3735), 0x80 },
2073aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3739), 0x10 },
2083aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x373f), 0xb0 },
2093aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3740), 0x28 },
2103aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3741), 0x21 },
2113aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3742), 0x21 },
2123aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3743), 0x21 },
2133aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3744), 0x63 },
2143aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3745), 0x5a },
2153aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3746), 0x5a },
2163aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3747), 0x5a },
2173aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3748), 0x00 },
2183aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3749), 0x00 },
2193aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x374a), 0x00 },
2203aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x374b), 0x00 },
2213aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3756), 0x00 },
2223aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3757), 0x0e },
2233aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x375d), 0x84 },
2243aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3760), 0x11 },
2253aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3767), 0x08 },
2263aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x376f), 0x42 },
2273aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3771), 0x00 },
2283aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3773), 0x01 },
2293aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3774), 0x02 },
2303aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3775), 0x12 },
2313aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3776), 0x02 },
2323aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x377b), 0x40 },
2333aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x377c), 0x00 },
2343aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x377d), 0x0c },
2353aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3782), 0x02 },
2363aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3787), 0x24 },
2373aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x378a), 0x01 },
2383aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x378d), 0x00 },
2393aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3790), 0x1f },
2403aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3791), 0x58 },
2413aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3795), 0x24 },
2423aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3796), 0x01 },
2433aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3798), 0x40 },
2443aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x379c), 0x00 },
2453aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x379d), 0x00 },
2463aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x379e), 0x00 },
2473aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x379f), 0x01 },
2483aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37a1), 0x10 },
2493aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37a6), 0x00 },
2503aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ab), 0x0e },
2513aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ac), 0xa0 },
2523aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37be), 0x0a },
2533aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37bf), 0x05 },
2543aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37bb), 0x02 },
2553aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37bf), 0x05 },
2563aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37c2), 0x04 },
2573aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37c4), 0x11 },
2583aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37c5), 0x80 },
2593aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37c6), 0x14 },
2603aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37c7), 0x08 },
2613aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37c8), 0x42 },
2623aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37cd), 0x17 },
2633aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ce), 0x01 },
2643aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37d8), 0x02 },
2653aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37d9), 0x08 },
2663aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37dc), 0x01 },
2673aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37e0), 0x0c },
2683aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37e1), 0x20 },
2693aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37e2), 0x10 },
2703aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37e3), 0x04 },
2713aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37e4), 0x28 },
2723aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37e5), 0x02 },
2733aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ef), 0x00 },
2743aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37f4), 0x00 },
2753aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37f5), 0x00 },
2763aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37f6), 0x00 },
2773aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37f7), 0x00 },
2783aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3800), 0x01 },
2793aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3801), 0x30 },
2803aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3802), 0x00 },
2813aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3803), 0x00 },
2823aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3804), 0x0b },
2833aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3805), 0x5f },
2843aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3806), 0x07 },
2853aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3807), 0xa7 },
2863aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3808), 0x0a },
2873aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3809), 0x20 },
2883aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x380a), 0x07 },
2893aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x380b), 0x98 },
2903aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x380c), 0x06 },
2913aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x380d), 0xd0 },
2923aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3810), 0x00 },
2933aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3811), 0x08 },
2943aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3812), 0x00 },
2953aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3813), 0x08 },
2963aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3814), 0x01 },
2973aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3815), 0x01 },
2983aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3816), 0x01 },
2993aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3817), 0x01 },
3003aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3818), 0x00 },
3013aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3819), 0x00 },
3023aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x381a), 0x00 },
3033aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x381b), 0x01 },
3043aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3820), 0x88 },
3053aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3821), 0x00 },
3063aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3822), 0x12 },
3073aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3823), 0x08 },
3083aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3824), 0x00 },
3093aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3825), 0x20 },
3103aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3826), 0x00 },
3113aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3827), 0x08 },
3123aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3829), 0x03 },
3133aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x382a), 0x00 },
3143aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x382b), 0x00 },
3153aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3832), 0x08 },
3163aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3838), 0x00 },
3173aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3839), 0x00 },
3183aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x383a), 0x00 },
3193aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x383b), 0x00 },
3203aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x383d), 0x01 },
3213aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x383e), 0x00 },
3223aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x383f), 0x00 },
3233aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3843), 0x00 },
3243aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3880), 0x16 },
3253aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3881), 0x00 },
3263aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3882), 0x08 },
3273aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x389a), 0x00 },
3283aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x389b), 0x00 },
3293aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38a2), 0x02 },
3303aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38a3), 0x02 },
3313aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38a4), 0x02 },
3323aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38a5), 0x02 },
3333aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38a7), 0x04 },
3343aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38b8), 0x02 },
3353aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3c80), 0x3e },
3363aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3c86), 0x01 },
3373aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3c87), 0x02 },
3383aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x389c), 0x00 },
3393aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3ca2), 0x0c },
3403aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3d85), 0x1b },
3413aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3d8c), 0x01 },
3423aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3d8d), 0xe2 },
3433aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3f00), 0xcb },
3443aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3f03), 0x08 },
3453aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3f9e), 0x07 },
3463aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3f9f), 0x04 },
3473aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4000), 0xf3 },
3483aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4002), 0x00 },
3493aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4003), 0x40 },
3503aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4008), 0x02 },
3513aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4009), 0x0d },
3523aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x400a), 0x01 },
3533aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x400b), 0x00 },
3543aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4040), 0x00 },
3553aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4041), 0x07 },
3563aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4090), 0x14 },
3573aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b0), 0x01 },
3583aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b1), 0x01 },
3593aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b2), 0x30 },
3603aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b3), 0x04 },
3613aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b4), 0xe8 },
3623aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b5), 0x01 },
3633aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b7), 0x07 },
3643aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b8), 0xff },
3653aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40b9), 0x00 },
3663aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x40ba), 0x00 },
3673aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4300), 0xff },
3683aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4301), 0x00 },
3693aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4302), 0x0f },
3703aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4303), 0x20 },
3713aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4304), 0x20 },
3723aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4305), 0x83 },
3733aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4306), 0x21 },
3743aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x430d), 0x00 },
3753aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4505), 0xc4 },
3763aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4506), 0x00 },
3773aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4507), 0x60 },
3783aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4803), 0x00 },
3793aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4809), 0x8e },
3803aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x480e), 0x00 },
3813aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4813), 0x00 },
3823aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4814), 0x2a },
3833aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x481b), 0x40 },
3843aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x481f), 0x30 },
3853aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4825), 0x34 },
3863aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4829), 0x64 },
3873aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4837), 0x12 },
3883aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x484b), 0x07 },
3893aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4883), 0x36 },
3903aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4885), 0x03 },
3913aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x488b), 0x00 },
3923aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4d06), 0x01 },
3933aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4e00), 0x2a },
3943aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x4e0d), 0x00 },
3953aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5000), 0xf9 },
3963aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5001), 0x09 },
3973aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5004), 0x00 },
3983aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5005), 0x0e },
3993aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5036), 0x00 },
4003aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5080), 0x04 },
4013aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5082), 0x00 },
4023aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5180), 0x00 },
4033aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5181), 0x10 },
4043aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5182), 0x01 },
4053aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5183), 0xdf },
4063aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5184), 0x02 },
4073aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5185), 0x6c },
4083aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5189), 0x48 },
4093aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x520a), 0x03 },
4103aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x520b), 0x0f },
4113aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x520c), 0x3f },
4123aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x580b), 0x03 },
4133aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x580d), 0x00 },
4143aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x580f), 0x00 },
4153aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5820), 0x00 },
4163aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x5821), 0x00 },
4173aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3222), 0x03 },
4183aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x06 },
4193aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3701), 0x1d },
4203aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ab), 0x01 },
4213aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3790), 0x21 },
4223aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38be), 0x00 },
4233aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3791), 0x5a },
4243aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37bf), 0x1c },
4253aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3610), 0x37 },
4263aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x16 },
4273aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x07 },
4283aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3701), 0x1d },
4293aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ab), 0x0e },
4303aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3790), 0x21 },
4313aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38be), 0x00 },
4323aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3791), 0x5a },
4333aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37bf), 0x0a },
4343aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3610), 0x87 },
4353aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x17 },
4363aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x08 },
4373aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3701), 0x1d },
4383aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ab), 0x0e },
4393aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3790), 0x21 },
4403aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38be), 0x00 },
4413aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3791), 0x5a },
4423aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37bf), 0x0a },
4433aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3610), 0x87 },
4443aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x18 },
4453aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x09 },
4463aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3701), 0x1d },
4473aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37ab), 0x0e },
4483aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3790), 0x28 },
4493aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x38be), 0x00 },
4503aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3791), 0x63 },
4513aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x37bf), 0x0a },
4523aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3610), 0x87 },
4533aa9296aSHimanshu Bhavani 	{ CCI_REG8(0x3208), 0x19 },
4543aa9296aSHimanshu Bhavani };
4553aa9296aSHimanshu Bhavani 
4563aa9296aSHimanshu Bhavani struct os05b10 {
4573aa9296aSHimanshu Bhavani 	struct device *dev;
4583aa9296aSHimanshu Bhavani 	struct regmap *cci;
4593aa9296aSHimanshu Bhavani 	struct v4l2_subdev sd;
4603aa9296aSHimanshu Bhavani 	struct media_pad pad;
4613aa9296aSHimanshu Bhavani 	struct clk *xclk;
4623aa9296aSHimanshu Bhavani 	struct i2c_client *client;
4633aa9296aSHimanshu Bhavani 	struct gpio_desc *reset_gpio;
4643aa9296aSHimanshu Bhavani 	struct regulator_bulk_data supplies[ARRAY_SIZE(os05b10_supply_name)];
4653aa9296aSHimanshu Bhavani 
4663aa9296aSHimanshu Bhavani 	/* V4L2 Controls */
4673aa9296aSHimanshu Bhavani 	struct v4l2_ctrl_handler handler;
4683aa9296aSHimanshu Bhavani 	struct v4l2_ctrl *link_freq;
4693aa9296aSHimanshu Bhavani 	struct v4l2_ctrl *hblank;
4703aa9296aSHimanshu Bhavani 	struct v4l2_ctrl *vblank;
4713aa9296aSHimanshu Bhavani 	struct v4l2_ctrl *gain;
4723aa9296aSHimanshu Bhavani 	struct v4l2_ctrl *exposure;
4733aa9296aSHimanshu Bhavani 
4743aa9296aSHimanshu Bhavani 	u32 link_freq_index;
4753aa9296aSHimanshu Bhavani 	u32 data_lanes;
4763aa9296aSHimanshu Bhavani };
4773aa9296aSHimanshu Bhavani 
4783aa9296aSHimanshu Bhavani struct os05b10_mode {
4793aa9296aSHimanshu Bhavani 	u32 width;
4803aa9296aSHimanshu Bhavani 	u32 height;
4813aa9296aSHimanshu Bhavani 	u32 vts;
4823aa9296aSHimanshu Bhavani 	u32 hts;
4833aa9296aSHimanshu Bhavani 	u32 exp;
4843aa9296aSHimanshu Bhavani 	u8 bpp;
4853aa9296aSHimanshu Bhavani };
4863aa9296aSHimanshu Bhavani 
4873aa9296aSHimanshu Bhavani static const struct os05b10_mode supported_modes_10bit[] = {
4883aa9296aSHimanshu Bhavani 	{
4893aa9296aSHimanshu Bhavani 		.width = 2592,
4903aa9296aSHimanshu Bhavani 		.height = 1944,
4913aa9296aSHimanshu Bhavani 		.vts = 2006,
4923aa9296aSHimanshu Bhavani 		.hts = 1744,
4933aa9296aSHimanshu Bhavani 		.exp = 1944,
4943aa9296aSHimanshu Bhavani 		.bpp = 10,
4953aa9296aSHimanshu Bhavani 	},
4963aa9296aSHimanshu Bhavani };
4973aa9296aSHimanshu Bhavani 
4983aa9296aSHimanshu Bhavani static const s64 link_frequencies[] = {
4993aa9296aSHimanshu Bhavani 	OS05B10_LINK_FREQ_600MHZ,
5003aa9296aSHimanshu Bhavani };
5013aa9296aSHimanshu Bhavani 
5023aa9296aSHimanshu Bhavani static const u32 os05b10_mbus_codes[] = {
5033aa9296aSHimanshu Bhavani 	MEDIA_BUS_FMT_SBGGR10_1X10,
5043aa9296aSHimanshu Bhavani };
5053aa9296aSHimanshu Bhavani 
5063aa9296aSHimanshu Bhavani static inline struct os05b10 *to_os05b10(struct v4l2_subdev *sd)
5073aa9296aSHimanshu Bhavani {
5083aa9296aSHimanshu Bhavani 	return container_of_const(sd, struct os05b10, sd);
5093aa9296aSHimanshu Bhavani };
5103aa9296aSHimanshu Bhavani 
5113aa9296aSHimanshu Bhavani static int os05b10_set_ctrl(struct v4l2_ctrl *ctrl)
5123aa9296aSHimanshu Bhavani {
5133aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10 = container_of_const(ctrl->handler,
5143aa9296aSHimanshu Bhavani 						     struct os05b10, handler);
5153aa9296aSHimanshu Bhavani 	struct v4l2_subdev_state *state;
5163aa9296aSHimanshu Bhavani 	struct v4l2_mbus_framefmt *fmt;
5173aa9296aSHimanshu Bhavani 	int vmax, ret;
5183aa9296aSHimanshu Bhavani 
5193aa9296aSHimanshu Bhavani 	state = v4l2_subdev_get_locked_active_state(&os05b10->sd);
5203aa9296aSHimanshu Bhavani 	fmt = v4l2_subdev_state_get_format(state, 0);
5213aa9296aSHimanshu Bhavani 
5223aa9296aSHimanshu Bhavani 	if (ctrl->id == V4L2_CID_VBLANK) {
5233aa9296aSHimanshu Bhavani 		/* Honour the VBLANK limits when setting exposure. */
5243aa9296aSHimanshu Bhavani 		s64 max = fmt->height + ctrl->val - OS05B10_EXPOSURE_MARGIN;
5253aa9296aSHimanshu Bhavani 
5263aa9296aSHimanshu Bhavani 		ret = __v4l2_ctrl_modify_range(os05b10->exposure,
5273aa9296aSHimanshu Bhavani 					       os05b10->exposure->minimum, max,
5283aa9296aSHimanshu Bhavani 					       os05b10->exposure->step,
5293aa9296aSHimanshu Bhavani 					       os05b10->exposure->default_value);
5303aa9296aSHimanshu Bhavani 		if (ret)
5313aa9296aSHimanshu Bhavani 			return ret;
5323aa9296aSHimanshu Bhavani 	}
5333aa9296aSHimanshu Bhavani 
5343aa9296aSHimanshu Bhavani 	if (pm_runtime_get_if_in_use(os05b10->dev) == 0)
5353aa9296aSHimanshu Bhavani 		return 0;
5363aa9296aSHimanshu Bhavani 
5373aa9296aSHimanshu Bhavani 	switch (ctrl->id) {
5383aa9296aSHimanshu Bhavani 	case V4L2_CID_VBLANK:
5393aa9296aSHimanshu Bhavani 		vmax = fmt->height + ctrl->val;
5403aa9296aSHimanshu Bhavani 		ret = cci_write(os05b10->cci, OS05B10_REG_VTS, vmax, NULL);
5413aa9296aSHimanshu Bhavani 		break;
5423aa9296aSHimanshu Bhavani 	case V4L2_CID_ANALOGUE_GAIN:
5433aa9296aSHimanshu Bhavani 		ret = cci_write(os05b10->cci, OS05B10_REG_ANALOG_GAIN,
5443aa9296aSHimanshu Bhavani 				ctrl->val, NULL);
5453aa9296aSHimanshu Bhavani 		break;
5463aa9296aSHimanshu Bhavani 	case V4L2_CID_EXPOSURE:
5473aa9296aSHimanshu Bhavani 		ret = cci_write(os05b10->cci, OS05B10_REG_EXPOSURE,
5483aa9296aSHimanshu Bhavani 				ctrl->val, NULL);
5493aa9296aSHimanshu Bhavani 		break;
5503aa9296aSHimanshu Bhavani 	default:
5513aa9296aSHimanshu Bhavani 		ret = -EINVAL;
5523aa9296aSHimanshu Bhavani 		break;
5533aa9296aSHimanshu Bhavani 	}
5543aa9296aSHimanshu Bhavani 
5553aa9296aSHimanshu Bhavani 	pm_runtime_put(os05b10->dev);
5563aa9296aSHimanshu Bhavani 
5573aa9296aSHimanshu Bhavani 	return ret;
5583aa9296aSHimanshu Bhavani }
5593aa9296aSHimanshu Bhavani 
5603aa9296aSHimanshu Bhavani static int os05b10_enum_mbus_code(struct v4l2_subdev *sd,
5613aa9296aSHimanshu Bhavani 				  struct v4l2_subdev_state *sd_state,
5623aa9296aSHimanshu Bhavani 				  struct v4l2_subdev_mbus_code_enum *code)
5633aa9296aSHimanshu Bhavani {
5643aa9296aSHimanshu Bhavani 	if (code->index >= ARRAY_SIZE(os05b10_mbus_codes))
5653aa9296aSHimanshu Bhavani 		return -EINVAL;
5663aa9296aSHimanshu Bhavani 
5673aa9296aSHimanshu Bhavani 	code->code = os05b10_mbus_codes[code->index];
5683aa9296aSHimanshu Bhavani 
5693aa9296aSHimanshu Bhavani 	return 0;
5703aa9296aSHimanshu Bhavani }
5713aa9296aSHimanshu Bhavani 
5723aa9296aSHimanshu Bhavani static int os05b10_set_framing_limits(struct os05b10 *os05b10,
5733aa9296aSHimanshu Bhavani 				      const struct os05b10_mode *mode)
5743aa9296aSHimanshu Bhavani {
5753aa9296aSHimanshu Bhavani 	u32 hblank, vblank, vblank_max, max_exp;
5763aa9296aSHimanshu Bhavani 	int ret;
5773aa9296aSHimanshu Bhavani 
5783aa9296aSHimanshu Bhavani 	hblank = mode->hts - mode->width;
5793aa9296aSHimanshu Bhavani 	ret = __v4l2_ctrl_modify_range(os05b10->hblank, hblank, hblank, 1,
5803aa9296aSHimanshu Bhavani 				       hblank);
5813aa9296aSHimanshu Bhavani 	if (ret)
5823aa9296aSHimanshu Bhavani 		return ret;
5833aa9296aSHimanshu Bhavani 
5843aa9296aSHimanshu Bhavani 	vblank = mode->vts - mode->height;
5853aa9296aSHimanshu Bhavani 	vblank_max = OS05B10_VTS_MAX - mode->height;
5863aa9296aSHimanshu Bhavani 	ret = __v4l2_ctrl_modify_range(os05b10->vblank, 0, vblank_max, 1,
5873aa9296aSHimanshu Bhavani 				       vblank);
5883aa9296aSHimanshu Bhavani 	if (ret)
5893aa9296aSHimanshu Bhavani 		return ret;
5903aa9296aSHimanshu Bhavani 
5913aa9296aSHimanshu Bhavani 	max_exp = mode->vts - OS05B10_EXPOSURE_MARGIN;
5923aa9296aSHimanshu Bhavani 	return __v4l2_ctrl_modify_range(os05b10->exposure,
5933aa9296aSHimanshu Bhavani 					OS05B10_EXPOSURE_MIN, max_exp,
5943aa9296aSHimanshu Bhavani 					OS05B10_EXPOSURE_STEP, mode->exp);
5953aa9296aSHimanshu Bhavani }
5963aa9296aSHimanshu Bhavani 
5973aa9296aSHimanshu Bhavani static int os05b10_set_pad_format(struct v4l2_subdev *sd,
5983aa9296aSHimanshu Bhavani 				  struct v4l2_subdev_state *sd_state,
5993aa9296aSHimanshu Bhavani 				  struct v4l2_subdev_format *fmt)
6003aa9296aSHimanshu Bhavani {
6013aa9296aSHimanshu Bhavani 	const struct os05b10_mode *mode = &supported_modes_10bit[0];
6023aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10 = to_os05b10(sd);
6033aa9296aSHimanshu Bhavani 	struct v4l2_mbus_framefmt *format;
6043aa9296aSHimanshu Bhavani 	int ret;
6053aa9296aSHimanshu Bhavani 
6063aa9296aSHimanshu Bhavani 	fmt->format.width = mode->width;
6073aa9296aSHimanshu Bhavani 	fmt->format.height = mode->height;
6083aa9296aSHimanshu Bhavani 	fmt->format.field = V4L2_FIELD_NONE;
6093aa9296aSHimanshu Bhavani 	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
6103aa9296aSHimanshu Bhavani 	fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
6113aa9296aSHimanshu Bhavani 	fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
6123aa9296aSHimanshu Bhavani 
6133aa9296aSHimanshu Bhavani 	format = v4l2_subdev_state_get_format(sd_state, 0);
6143aa9296aSHimanshu Bhavani 
6153aa9296aSHimanshu Bhavani 	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
6163aa9296aSHimanshu Bhavani 		ret = os05b10_set_framing_limits(os05b10, mode);
6173aa9296aSHimanshu Bhavani 		if (ret)
6183aa9296aSHimanshu Bhavani 			return ret;
6193aa9296aSHimanshu Bhavani 	}
6203aa9296aSHimanshu Bhavani 
6213aa9296aSHimanshu Bhavani 	*format = fmt->format;
6223aa9296aSHimanshu Bhavani 
6233aa9296aSHimanshu Bhavani 	return 0;
6243aa9296aSHimanshu Bhavani }
6253aa9296aSHimanshu Bhavani 
6263aa9296aSHimanshu Bhavani static int os05b10_get_selection(struct v4l2_subdev *sd,
6273aa9296aSHimanshu Bhavani 				 struct v4l2_subdev_state *sd_state,
6283aa9296aSHimanshu Bhavani 				 struct v4l2_subdev_selection *sel)
6293aa9296aSHimanshu Bhavani {
6303aa9296aSHimanshu Bhavani 	switch (sel->target) {
6313aa9296aSHimanshu Bhavani 	case V4L2_SEL_TGT_NATIVE_SIZE:
6323aa9296aSHimanshu Bhavani 	case V4L2_SEL_TGT_CROP_BOUNDS:
6333aa9296aSHimanshu Bhavani 		sel->r = os05b10_native_area;
6343aa9296aSHimanshu Bhavani 		return 0;
6353aa9296aSHimanshu Bhavani 	case V4L2_SEL_TGT_CROP:
6363aa9296aSHimanshu Bhavani 	case V4L2_SEL_TGT_CROP_DEFAULT:
6373aa9296aSHimanshu Bhavani 		sel->r = os05b10_active_area;
6383aa9296aSHimanshu Bhavani 		return 0;
6393aa9296aSHimanshu Bhavani 	default:
6403aa9296aSHimanshu Bhavani 		return -EINVAL;
6413aa9296aSHimanshu Bhavani 	}
6423aa9296aSHimanshu Bhavani }
6433aa9296aSHimanshu Bhavani 
6443aa9296aSHimanshu Bhavani static int os05b10_enum_frame_size(struct v4l2_subdev *sd,
6453aa9296aSHimanshu Bhavani 				   struct v4l2_subdev_state *sd_state,
6463aa9296aSHimanshu Bhavani 				   struct v4l2_subdev_frame_size_enum *fse)
6473aa9296aSHimanshu Bhavani {
6483aa9296aSHimanshu Bhavani 	if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
6493aa9296aSHimanshu Bhavani 		return -EINVAL;
6503aa9296aSHimanshu Bhavani 
6513aa9296aSHimanshu Bhavani 	fse->min_width = supported_modes_10bit[fse->index].width;
6523aa9296aSHimanshu Bhavani 	fse->max_width = fse->min_width;
6533aa9296aSHimanshu Bhavani 	fse->min_height = supported_modes_10bit[fse->index].height;
6543aa9296aSHimanshu Bhavani 	fse->max_height = fse->min_height;
6553aa9296aSHimanshu Bhavani 
6563aa9296aSHimanshu Bhavani 	return 0;
6573aa9296aSHimanshu Bhavani }
6583aa9296aSHimanshu Bhavani 
6593aa9296aSHimanshu Bhavani static int os05b10_enable_streams(struct v4l2_subdev *sd,
6603aa9296aSHimanshu Bhavani 				  struct v4l2_subdev_state *state,
6613aa9296aSHimanshu Bhavani 				  u32 pad, u64 streams_mask)
6623aa9296aSHimanshu Bhavani {
6633aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10 = to_os05b10(sd);
6643aa9296aSHimanshu Bhavani 	int ret;
6653aa9296aSHimanshu Bhavani 
6663aa9296aSHimanshu Bhavani 	ret = pm_runtime_resume_and_get(os05b10->dev);
6673aa9296aSHimanshu Bhavani 	if (ret < 0)
6683aa9296aSHimanshu Bhavani 		return ret;
6693aa9296aSHimanshu Bhavani 
6703aa9296aSHimanshu Bhavani 	/* Write common registers */
6713aa9296aSHimanshu Bhavani 	ret = cci_multi_reg_write(os05b10->cci, os05b10_common_regs,
6723aa9296aSHimanshu Bhavani 				  ARRAY_SIZE(os05b10_common_regs), NULL);
6733aa9296aSHimanshu Bhavani 	if (ret) {
6743aa9296aSHimanshu Bhavani 		dev_err(os05b10->dev, "failed to write common registers\n");
6753aa9296aSHimanshu Bhavani 		goto err_rpm_put;
6763aa9296aSHimanshu Bhavani 	}
6773aa9296aSHimanshu Bhavani 
6783aa9296aSHimanshu Bhavani 	/* Apply customized user controls */
6793aa9296aSHimanshu Bhavani 	ret = __v4l2_ctrl_handler_setup(os05b10->sd.ctrl_handler);
6803aa9296aSHimanshu Bhavani 	if (ret)
6813aa9296aSHimanshu Bhavani 		goto err_rpm_put;
6823aa9296aSHimanshu Bhavani 
6833aa9296aSHimanshu Bhavani 	/* Stream ON */
6843aa9296aSHimanshu Bhavani 	ret = cci_write(os05b10->cci, OS05B10_REG_CTRL_MODE,
6853aa9296aSHimanshu Bhavani 			OS05B10_MODE_STREAMING, NULL);
6863aa9296aSHimanshu Bhavani 	if (ret)
6873aa9296aSHimanshu Bhavani 		goto err_rpm_put;
6883aa9296aSHimanshu Bhavani 
6893aa9296aSHimanshu Bhavani 	return 0;
6903aa9296aSHimanshu Bhavani 
6913aa9296aSHimanshu Bhavani err_rpm_put:
6923aa9296aSHimanshu Bhavani 	pm_runtime_put(os05b10->dev);
6933aa9296aSHimanshu Bhavani 
6943aa9296aSHimanshu Bhavani 	return ret;
6953aa9296aSHimanshu Bhavani }
6963aa9296aSHimanshu Bhavani 
6973aa9296aSHimanshu Bhavani static int os05b10_disable_streams(struct v4l2_subdev *sd,
6983aa9296aSHimanshu Bhavani 				   struct v4l2_subdev_state *state,
6993aa9296aSHimanshu Bhavani 				   u32 pad, u64 streams_mask)
7003aa9296aSHimanshu Bhavani {
7013aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10 = to_os05b10(sd);
7023aa9296aSHimanshu Bhavani 	int ret;
7033aa9296aSHimanshu Bhavani 
7043aa9296aSHimanshu Bhavani 	ret = cci_write(os05b10->cci, OS05B10_REG_CTRL_MODE,
7053aa9296aSHimanshu Bhavani 			OS05B10_MODE_STANDBY, NULL);
7063aa9296aSHimanshu Bhavani 	if (ret)
7073aa9296aSHimanshu Bhavani 		dev_err(os05b10->dev, "failed to set stream off\n");
7083aa9296aSHimanshu Bhavani 
7093aa9296aSHimanshu Bhavani 	pm_runtime_put(os05b10->dev);
7103aa9296aSHimanshu Bhavani 
711*f422140eSHimanshu Bhavani 	return 0;
7123aa9296aSHimanshu Bhavani }
7133aa9296aSHimanshu Bhavani 
7143aa9296aSHimanshu Bhavani static int os05b10_init_state(struct v4l2_subdev *sd,
7153aa9296aSHimanshu Bhavani 			      struct v4l2_subdev_state *state)
7163aa9296aSHimanshu Bhavani {
7173aa9296aSHimanshu Bhavani 	struct v4l2_mbus_framefmt *format;
7183aa9296aSHimanshu Bhavani 	const struct os05b10_mode *mode;
7193aa9296aSHimanshu Bhavani 
7203aa9296aSHimanshu Bhavani 	/* Initialize try_fmt */
7213aa9296aSHimanshu Bhavani 	format = v4l2_subdev_state_get_format(state, 0);
7223aa9296aSHimanshu Bhavani 
7233aa9296aSHimanshu Bhavani 	mode = &supported_modes_10bit[0];
7243aa9296aSHimanshu Bhavani 	format->code = MEDIA_BUS_FMT_SBGGR10_1X10;
7253aa9296aSHimanshu Bhavani 
7263aa9296aSHimanshu Bhavani 	/* Update image pad formate */
7273aa9296aSHimanshu Bhavani 	format->width = mode->width;
7283aa9296aSHimanshu Bhavani 	format->height = mode->height;
7293aa9296aSHimanshu Bhavani 	format->field = V4L2_FIELD_NONE;
7303aa9296aSHimanshu Bhavani 	format->colorspace = V4L2_COLORSPACE_RAW;
7313aa9296aSHimanshu Bhavani 	format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
7323aa9296aSHimanshu Bhavani 	format->xfer_func = V4L2_XFER_FUNC_NONE;
7333aa9296aSHimanshu Bhavani 
7343aa9296aSHimanshu Bhavani 	return 0;
7353aa9296aSHimanshu Bhavani }
7363aa9296aSHimanshu Bhavani 
7373aa9296aSHimanshu Bhavani static const struct v4l2_subdev_video_ops os05b10_video_ops = {
7383aa9296aSHimanshu Bhavani 	.s_stream = v4l2_subdev_s_stream_helper,
7393aa9296aSHimanshu Bhavani };
7403aa9296aSHimanshu Bhavani 
7413aa9296aSHimanshu Bhavani static const struct v4l2_subdev_pad_ops os05b10_pad_ops = {
7423aa9296aSHimanshu Bhavani 	.enum_mbus_code = os05b10_enum_mbus_code,
7433aa9296aSHimanshu Bhavani 	.get_fmt = v4l2_subdev_get_fmt,
7443aa9296aSHimanshu Bhavani 	.set_fmt = os05b10_set_pad_format,
7453aa9296aSHimanshu Bhavani 	.get_selection = os05b10_get_selection,
7463aa9296aSHimanshu Bhavani 	.enum_frame_size = os05b10_enum_frame_size,
7473aa9296aSHimanshu Bhavani 	.enable_streams = os05b10_enable_streams,
7483aa9296aSHimanshu Bhavani 	.disable_streams = os05b10_disable_streams,
7493aa9296aSHimanshu Bhavani };
7503aa9296aSHimanshu Bhavani 
7513aa9296aSHimanshu Bhavani static const struct v4l2_subdev_internal_ops os05b10_internal_ops = {
7523aa9296aSHimanshu Bhavani 	.init_state = os05b10_init_state,
7533aa9296aSHimanshu Bhavani };
7543aa9296aSHimanshu Bhavani 
7553aa9296aSHimanshu Bhavani static const struct v4l2_subdev_ops os05b10_subdev_ops = {
7563aa9296aSHimanshu Bhavani 	.video = &os05b10_video_ops,
7573aa9296aSHimanshu Bhavani 	.pad = &os05b10_pad_ops,
7583aa9296aSHimanshu Bhavani };
7593aa9296aSHimanshu Bhavani 
7603aa9296aSHimanshu Bhavani static const struct v4l2_ctrl_ops os05b10_ctrl_ops = {
7613aa9296aSHimanshu Bhavani 	.s_ctrl = os05b10_set_ctrl,
7623aa9296aSHimanshu Bhavani };
7633aa9296aSHimanshu Bhavani 
7643aa9296aSHimanshu Bhavani static int os05b10_identify_module(struct os05b10 *os05b10)
7653aa9296aSHimanshu Bhavani {
7663aa9296aSHimanshu Bhavani 	int ret;
7673aa9296aSHimanshu Bhavani 	u64 val;
7683aa9296aSHimanshu Bhavani 
7693aa9296aSHimanshu Bhavani 	ret = cci_read(os05b10->cci, OS05B10_REG_CHIP_ID, &val, NULL);
7703aa9296aSHimanshu Bhavani 	if (ret)
7713aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, ret,
7723aa9296aSHimanshu Bhavani 				     "failed to read chip id %x\n",
7733aa9296aSHimanshu Bhavani 				     OS05B10_CHIP_ID);
7743aa9296aSHimanshu Bhavani 
7753aa9296aSHimanshu Bhavani 	if (val != OS05B10_CHIP_ID)
7763aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, -ENODEV,
7773aa9296aSHimanshu Bhavani 				     "chip id mismatch: %x!=%llx\n",
7783aa9296aSHimanshu Bhavani 				     OS05B10_CHIP_ID, val);
7793aa9296aSHimanshu Bhavani 
7803aa9296aSHimanshu Bhavani 	return 0;
7813aa9296aSHimanshu Bhavani }
7823aa9296aSHimanshu Bhavani 
7833aa9296aSHimanshu Bhavani static int os05b10_power_on(struct device *dev)
7843aa9296aSHimanshu Bhavani {
7853aa9296aSHimanshu Bhavani 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
7863aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10 = to_os05b10(sd);
7873aa9296aSHimanshu Bhavani 	unsigned long delay_us;
7883aa9296aSHimanshu Bhavani 	int ret;
7893aa9296aSHimanshu Bhavani 
7903aa9296aSHimanshu Bhavani 	/* Enable power rails */
7913aa9296aSHimanshu Bhavani 	ret = regulator_bulk_enable(ARRAY_SIZE(os05b10_supply_name),
7923aa9296aSHimanshu Bhavani 				    os05b10->supplies);
7933aa9296aSHimanshu Bhavani 	if (ret) {
7943aa9296aSHimanshu Bhavani 		dev_err(os05b10->dev, "failed to enable regulators\n");
7953aa9296aSHimanshu Bhavani 		return ret;
7963aa9296aSHimanshu Bhavani 	}
7973aa9296aSHimanshu Bhavani 
7983aa9296aSHimanshu Bhavani 	/* Enable xclk */
7993aa9296aSHimanshu Bhavani 	ret = clk_prepare_enable(os05b10->xclk);
8003aa9296aSHimanshu Bhavani 	if (ret) {
8013aa9296aSHimanshu Bhavani 		dev_err(os05b10->dev, "failed to enable clock\n");
8023aa9296aSHimanshu Bhavani 		goto err_regulator_off;
8033aa9296aSHimanshu Bhavani 	}
8043aa9296aSHimanshu Bhavani 
8053aa9296aSHimanshu Bhavani 	gpiod_set_value_cansleep(os05b10->reset_gpio, 0);
8063aa9296aSHimanshu Bhavani 
8073aa9296aSHimanshu Bhavani 	/* Delay T1 */
8083aa9296aSHimanshu Bhavani 	fsleep(5 * USEC_PER_MSEC);
8093aa9296aSHimanshu Bhavani 
8103aa9296aSHimanshu Bhavani 	/* Delay T2 (8192 cycles before SCCB/I2C access) */
8113aa9296aSHimanshu Bhavani 	delay_us = DIV_ROUND_UP(8192, OS05B10_XCLK_FREQ / 1000 / 1000);
8123aa9296aSHimanshu Bhavani 	usleep_range(delay_us, delay_us * 2);
8133aa9296aSHimanshu Bhavani 
8143aa9296aSHimanshu Bhavani 	return 0;
8153aa9296aSHimanshu Bhavani 
8163aa9296aSHimanshu Bhavani err_regulator_off:
8173aa9296aSHimanshu Bhavani 	regulator_bulk_disable(ARRAY_SIZE(os05b10_supply_name),
8183aa9296aSHimanshu Bhavani 			       os05b10->supplies);
8193aa9296aSHimanshu Bhavani 
8203aa9296aSHimanshu Bhavani 	return ret;
8213aa9296aSHimanshu Bhavani }
8223aa9296aSHimanshu Bhavani 
8233aa9296aSHimanshu Bhavani static int os05b10_power_off(struct device *dev)
8243aa9296aSHimanshu Bhavani {
8253aa9296aSHimanshu Bhavani 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
8263aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10 = to_os05b10(sd);
8273aa9296aSHimanshu Bhavani 
8283aa9296aSHimanshu Bhavani 	gpiod_set_value_cansleep(os05b10->reset_gpio, 1);
8293aa9296aSHimanshu Bhavani 
8303aa9296aSHimanshu Bhavani 	regulator_bulk_disable(ARRAY_SIZE(os05b10_supply_name),
8313aa9296aSHimanshu Bhavani 			       os05b10->supplies);
8323aa9296aSHimanshu Bhavani 	clk_disable_unprepare(os05b10->xclk);
8333aa9296aSHimanshu Bhavani 
8343aa9296aSHimanshu Bhavani 	return 0;
8353aa9296aSHimanshu Bhavani }
8363aa9296aSHimanshu Bhavani 
8373aa9296aSHimanshu Bhavani static int os05b10_parse_endpoint(struct os05b10 *os05b10)
8383aa9296aSHimanshu Bhavani {
8393aa9296aSHimanshu Bhavani 	struct v4l2_fwnode_endpoint bus_cfg = {
8403aa9296aSHimanshu Bhavani 		.bus_type = V4L2_MBUS_CSI2_DPHY
8413aa9296aSHimanshu Bhavani 	};
8423aa9296aSHimanshu Bhavani 	unsigned long link_freq_bitmap;
8433aa9296aSHimanshu Bhavani 	struct fwnode_handle *ep;
8443aa9296aSHimanshu Bhavani 	int ret;
8453aa9296aSHimanshu Bhavani 
8463aa9296aSHimanshu Bhavani 	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(os05b10->dev), 0, 0, 0);
8473aa9296aSHimanshu Bhavani 	if (!ep) {
8483aa9296aSHimanshu Bhavani 		dev_err(os05b10->dev, "Failed to get next endpoint\n");
8493aa9296aSHimanshu Bhavani 		return -EINVAL;
8503aa9296aSHimanshu Bhavani 	}
8513aa9296aSHimanshu Bhavani 
8523aa9296aSHimanshu Bhavani 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
8533aa9296aSHimanshu Bhavani 	fwnode_handle_put(ep);
8543aa9296aSHimanshu Bhavani 	if (ret)
8553aa9296aSHimanshu Bhavani 		return ret;
8563aa9296aSHimanshu Bhavani 
8573aa9296aSHimanshu Bhavani 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
8583aa9296aSHimanshu Bhavani 		ret = dev_err_probe(os05b10->dev, -EINVAL,
8593aa9296aSHimanshu Bhavani 				    "only 4 data lanes are supported\n");
8603aa9296aSHimanshu Bhavani 		goto error_out;
8613aa9296aSHimanshu Bhavani 	}
8623aa9296aSHimanshu Bhavani 
8633aa9296aSHimanshu Bhavani 	os05b10->data_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
8643aa9296aSHimanshu Bhavani 
8653aa9296aSHimanshu Bhavani 	ret = v4l2_link_freq_to_bitmap(os05b10->dev, bus_cfg.link_frequencies,
8663aa9296aSHimanshu Bhavani 				       bus_cfg.nr_of_link_frequencies,
8673aa9296aSHimanshu Bhavani 				       link_frequencies,
8683aa9296aSHimanshu Bhavani 				       ARRAY_SIZE(link_frequencies),
8693aa9296aSHimanshu Bhavani 				       &link_freq_bitmap);
8703aa9296aSHimanshu Bhavani 	if (ret) {
8713aa9296aSHimanshu Bhavani 		dev_err(os05b10->dev, "only 600MHz frequency is available\n");
8723aa9296aSHimanshu Bhavani 		goto error_out;
8733aa9296aSHimanshu Bhavani 	}
8743aa9296aSHimanshu Bhavani 
8753aa9296aSHimanshu Bhavani 	os05b10->link_freq_index = __ffs(link_freq_bitmap);
8763aa9296aSHimanshu Bhavani 
8773aa9296aSHimanshu Bhavani error_out:
8783aa9296aSHimanshu Bhavani 	v4l2_fwnode_endpoint_free(&bus_cfg);
8793aa9296aSHimanshu Bhavani 
8803aa9296aSHimanshu Bhavani 	return ret;
8813aa9296aSHimanshu Bhavani }
8823aa9296aSHimanshu Bhavani 
8833aa9296aSHimanshu Bhavani static u64 os05b10_pixel_rate(struct os05b10 *os05b10,
8843aa9296aSHimanshu Bhavani 			      const struct os05b10_mode *mode)
8853aa9296aSHimanshu Bhavani {
8863aa9296aSHimanshu Bhavani 	u64 link_freq = link_frequencies[os05b10->link_freq_index];
887*f422140eSHimanshu Bhavani 	u64 pixel_rate = div_u64(link_freq * 2 * os05b10->data_lanes, mode->bpp);
8883aa9296aSHimanshu Bhavani 
889*f422140eSHimanshu Bhavani 	dev_dbg(os05b10->dev,
8903aa9296aSHimanshu Bhavani 		"link_freq=%llu bpp=%u lanes=%u pixel_rate=%llu\n",
891*f422140eSHimanshu Bhavani 		link_freq, mode->bpp, os05b10->data_lanes, pixel_rate);
8923aa9296aSHimanshu Bhavani 
893*f422140eSHimanshu Bhavani 	return pixel_rate;
8943aa9296aSHimanshu Bhavani }
8953aa9296aSHimanshu Bhavani 
8963aa9296aSHimanshu Bhavani static int os05b10_init_controls(struct os05b10 *os05b10)
8973aa9296aSHimanshu Bhavani {
8983aa9296aSHimanshu Bhavani 	const struct os05b10_mode *mode = &supported_modes_10bit[0];
8993aa9296aSHimanshu Bhavani 	u64 hblank_def, vblank_def, exp_max, pixel_rate;
9003aa9296aSHimanshu Bhavani 	struct v4l2_fwnode_device_properties props;
9013aa9296aSHimanshu Bhavani 	struct v4l2_ctrl_handler *ctrl_hdlr;
9023aa9296aSHimanshu Bhavani 	int ret;
9033aa9296aSHimanshu Bhavani 
9043aa9296aSHimanshu Bhavani 	ctrl_hdlr = &os05b10->handler;
9053aa9296aSHimanshu Bhavani 	v4l2_ctrl_handler_init(ctrl_hdlr, 8);
9063aa9296aSHimanshu Bhavani 
9073aa9296aSHimanshu Bhavani 	pixel_rate = os05b10_pixel_rate(os05b10, mode);
9083aa9296aSHimanshu Bhavani 	v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops, V4L2_CID_PIXEL_RATE,
9093aa9296aSHimanshu Bhavani 			  pixel_rate, pixel_rate, 1, pixel_rate);
9103aa9296aSHimanshu Bhavani 
9113aa9296aSHimanshu Bhavani 	os05b10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &os05b10_ctrl_ops,
9123aa9296aSHimanshu Bhavani 						    V4L2_CID_LINK_FREQ,
9133aa9296aSHimanshu Bhavani 						    ARRAY_SIZE(link_frequencies) - 1,
9143aa9296aSHimanshu Bhavani 						    os05b10->link_freq_index,
9153aa9296aSHimanshu Bhavani 						    link_frequencies);
9163aa9296aSHimanshu Bhavani 
9173aa9296aSHimanshu Bhavani 	if (os05b10->link_freq)
9183aa9296aSHimanshu Bhavani 		os05b10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
9193aa9296aSHimanshu Bhavani 
9203aa9296aSHimanshu Bhavani 	hblank_def = mode->hts - mode->width;
9213aa9296aSHimanshu Bhavani 	os05b10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK,
9223aa9296aSHimanshu Bhavani 					    hblank_def, hblank_def,
9233aa9296aSHimanshu Bhavani 					    1, hblank_def);
9243aa9296aSHimanshu Bhavani 	if (os05b10->hblank)
9253aa9296aSHimanshu Bhavani 		os05b10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
9263aa9296aSHimanshu Bhavani 
9273aa9296aSHimanshu Bhavani 	vblank_def = mode->vts - mode->height;
9283aa9296aSHimanshu Bhavani 	os05b10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops,
9293aa9296aSHimanshu Bhavani 					    V4L2_CID_VBLANK, vblank_def,
9303aa9296aSHimanshu Bhavani 					    OS05B10_VTS_MAX - mode->height,
9313aa9296aSHimanshu Bhavani 					    1, vblank_def);
9323aa9296aSHimanshu Bhavani 
9333aa9296aSHimanshu Bhavani 	exp_max = mode->vts - OS05B10_EXPOSURE_MARGIN;
9343aa9296aSHimanshu Bhavani 	os05b10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops,
9353aa9296aSHimanshu Bhavani 					      V4L2_CID_EXPOSURE,
9363aa9296aSHimanshu Bhavani 					      OS05B10_EXPOSURE_MIN,
9373aa9296aSHimanshu Bhavani 					      exp_max, OS05B10_EXPOSURE_STEP,
9383aa9296aSHimanshu Bhavani 					      mode->exp);
9393aa9296aSHimanshu Bhavani 
9403aa9296aSHimanshu Bhavani 	os05b10->gain = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops,
9413aa9296aSHimanshu Bhavani 					  V4L2_CID_ANALOGUE_GAIN,
9423aa9296aSHimanshu Bhavani 					  OS05B10_ANALOG_GAIN_MIN,
9433aa9296aSHimanshu Bhavani 					  OS05B10_ANALOG_GAIN_MAX,
9443aa9296aSHimanshu Bhavani 					  OS05B10_ANALOG_GAIN_STEP,
9453aa9296aSHimanshu Bhavani 					  OS05B10_ANALOG_GAIN_DEFAULT);
9463aa9296aSHimanshu Bhavani 
9473aa9296aSHimanshu Bhavani 	if (ctrl_hdlr->error) {
9483aa9296aSHimanshu Bhavani 		ret = ctrl_hdlr->error;
9493aa9296aSHimanshu Bhavani 		dev_err(os05b10->dev, "control init failed (%d)\n", ret);
9503aa9296aSHimanshu Bhavani 		goto error;
9513aa9296aSHimanshu Bhavani 	}
9523aa9296aSHimanshu Bhavani 
9533aa9296aSHimanshu Bhavani 	ret = v4l2_fwnode_device_parse(os05b10->dev, &props);
9543aa9296aSHimanshu Bhavani 	if (ret)
9553aa9296aSHimanshu Bhavani 		goto error;
9563aa9296aSHimanshu Bhavani 
9573aa9296aSHimanshu Bhavani 	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &os05b10_ctrl_ops,
9583aa9296aSHimanshu Bhavani 					      &props);
9593aa9296aSHimanshu Bhavani 	if (ret)
9603aa9296aSHimanshu Bhavani 		goto error;
9613aa9296aSHimanshu Bhavani 
9623aa9296aSHimanshu Bhavani 	os05b10->sd.ctrl_handler = ctrl_hdlr;
9633aa9296aSHimanshu Bhavani 
9643aa9296aSHimanshu Bhavani 	return 0;
9653aa9296aSHimanshu Bhavani 
9663aa9296aSHimanshu Bhavani error:
9673aa9296aSHimanshu Bhavani 	v4l2_ctrl_handler_free(ctrl_hdlr);
9683aa9296aSHimanshu Bhavani 
9693aa9296aSHimanshu Bhavani 	return ret;
9703aa9296aSHimanshu Bhavani }
9713aa9296aSHimanshu Bhavani 
9723aa9296aSHimanshu Bhavani static int os05b10_probe(struct i2c_client *client)
9733aa9296aSHimanshu Bhavani {
9743aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10;
9753aa9296aSHimanshu Bhavani 	unsigned int xclk_freq;
9763aa9296aSHimanshu Bhavani 	int ret;
9773aa9296aSHimanshu Bhavani 
9783aa9296aSHimanshu Bhavani 	os05b10 = devm_kzalloc(&client->dev, sizeof(*os05b10), GFP_KERNEL);
9793aa9296aSHimanshu Bhavani 	if (!os05b10)
9803aa9296aSHimanshu Bhavani 		return -ENOMEM;
9813aa9296aSHimanshu Bhavani 
9823aa9296aSHimanshu Bhavani 	os05b10->client = client;
9833aa9296aSHimanshu Bhavani 	os05b10->dev = &client->dev;
9843aa9296aSHimanshu Bhavani 
9853aa9296aSHimanshu Bhavani 	v4l2_i2c_subdev_init(&os05b10->sd, client, &os05b10_subdev_ops);
9863aa9296aSHimanshu Bhavani 
9873aa9296aSHimanshu Bhavani 	os05b10->cci = devm_cci_regmap_init_i2c(client, 16);
9883aa9296aSHimanshu Bhavani 	if (IS_ERR(os05b10->cci))
9893aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->cci),
9903aa9296aSHimanshu Bhavani 				     "failed to initialize CCI\n");
9913aa9296aSHimanshu Bhavani 
9923aa9296aSHimanshu Bhavani 	os05b10->xclk = devm_v4l2_sensor_clk_get(os05b10->dev, NULL);
9933aa9296aSHimanshu Bhavani 	if (IS_ERR(os05b10->xclk))
9943aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->xclk),
9953aa9296aSHimanshu Bhavani 				     "failed to get xclk\n");
9963aa9296aSHimanshu Bhavani 
9973aa9296aSHimanshu Bhavani 	xclk_freq = clk_get_rate(os05b10->xclk);
9983aa9296aSHimanshu Bhavani 	if (xclk_freq != OS05B10_XCLK_FREQ)
9993aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, -EINVAL,
10003aa9296aSHimanshu Bhavani 				     "xclk frequency not supported: %d Hz\n",
10013aa9296aSHimanshu Bhavani 				     xclk_freq);
10023aa9296aSHimanshu Bhavani 
1003*f422140eSHimanshu Bhavani 	for (unsigned int i = 0; i < ARRAY_SIZE(os05b10_supply_name); i++)
10043aa9296aSHimanshu Bhavani 		os05b10->supplies[i].supply = os05b10_supply_name[i];
10053aa9296aSHimanshu Bhavani 
10063aa9296aSHimanshu Bhavani 	ret = devm_regulator_bulk_get(os05b10->dev,
10073aa9296aSHimanshu Bhavani 				      ARRAY_SIZE(os05b10_supply_name),
10083aa9296aSHimanshu Bhavani 				      os05b10->supplies);
10093aa9296aSHimanshu Bhavani 	if (ret)
10103aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, ret,
10113aa9296aSHimanshu Bhavani 				     "failed to get regulators\n");
10123aa9296aSHimanshu Bhavani 
10133aa9296aSHimanshu Bhavani 	ret = os05b10_parse_endpoint(os05b10);
10143aa9296aSHimanshu Bhavani 	if (ret)
10153aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, ret,
10163aa9296aSHimanshu Bhavani 				     "failed to parse endpoint configuration\n");
10173aa9296aSHimanshu Bhavani 
10183aa9296aSHimanshu Bhavani 	os05b10->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
10193aa9296aSHimanshu Bhavani 						      GPIOD_OUT_HIGH);
10203aa9296aSHimanshu Bhavani 	if (IS_ERR(os05b10->reset_gpio))
10213aa9296aSHimanshu Bhavani 		return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->reset_gpio),
10223aa9296aSHimanshu Bhavani 				     "failed to get reset GPIO\n");
10233aa9296aSHimanshu Bhavani 
10243aa9296aSHimanshu Bhavani 	ret = os05b10_power_on(os05b10->dev);
10253aa9296aSHimanshu Bhavani 	if (ret)
10263aa9296aSHimanshu Bhavani 		return ret;
10273aa9296aSHimanshu Bhavani 
10283aa9296aSHimanshu Bhavani 	ret = os05b10_identify_module(os05b10);
10293aa9296aSHimanshu Bhavani 	if (ret)
10303aa9296aSHimanshu Bhavani 		goto error_power_off;
10313aa9296aSHimanshu Bhavani 
10323aa9296aSHimanshu Bhavani 	/* This needs the pm runtime to be registered. */
10333aa9296aSHimanshu Bhavani 	ret = os05b10_init_controls(os05b10);
10343aa9296aSHimanshu Bhavani 	if (ret)
10353aa9296aSHimanshu Bhavani 		goto error_power_off;
10363aa9296aSHimanshu Bhavani 
10373aa9296aSHimanshu Bhavani 	/* Initialize subdev */
10383aa9296aSHimanshu Bhavani 	os05b10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
10393aa9296aSHimanshu Bhavani 	os05b10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
10403aa9296aSHimanshu Bhavani 	os05b10->sd.internal_ops = &os05b10_internal_ops;
10413aa9296aSHimanshu Bhavani 	os05b10->pad.flags = MEDIA_PAD_FL_SOURCE;
10423aa9296aSHimanshu Bhavani 
10433aa9296aSHimanshu Bhavani 	ret = media_entity_pads_init(&os05b10->sd.entity, 1, &os05b10->pad);
10443aa9296aSHimanshu Bhavani 	if (ret) {
10453aa9296aSHimanshu Bhavani 		dev_err_probe(os05b10->dev, ret,
10463aa9296aSHimanshu Bhavani 			      "failed to init entity pads\n");
10473aa9296aSHimanshu Bhavani 		goto error_handler_free;
10483aa9296aSHimanshu Bhavani 	}
10493aa9296aSHimanshu Bhavani 
10503aa9296aSHimanshu Bhavani 	os05b10->sd.state_lock = os05b10->handler.lock;
10513aa9296aSHimanshu Bhavani 	ret = v4l2_subdev_init_finalize(&os05b10->sd);
10523aa9296aSHimanshu Bhavani 	if (ret < 0) {
10533aa9296aSHimanshu Bhavani 		dev_err_probe(os05b10->dev, ret, "subdev init error\n");
10543aa9296aSHimanshu Bhavani 		goto error_media_entity;
10553aa9296aSHimanshu Bhavani 	}
10563aa9296aSHimanshu Bhavani 
10573aa9296aSHimanshu Bhavani 	pm_runtime_set_active(os05b10->dev);
10583aa9296aSHimanshu Bhavani 	pm_runtime_enable(os05b10->dev);
10593aa9296aSHimanshu Bhavani 
10603aa9296aSHimanshu Bhavani 	ret = v4l2_async_register_subdev_sensor(&os05b10->sd);
10613aa9296aSHimanshu Bhavani 	if (ret < 0) {
10623aa9296aSHimanshu Bhavani 		dev_err_probe(os05b10->dev, ret,
10633aa9296aSHimanshu Bhavani 			      "failed to register os05b10 sub-device\n");
10643aa9296aSHimanshu Bhavani 		goto error_subdev_cleanup;
10653aa9296aSHimanshu Bhavani 	}
10663aa9296aSHimanshu Bhavani 
10673aa9296aSHimanshu Bhavani 	pm_runtime_idle(os05b10->dev);
10683aa9296aSHimanshu Bhavani 
10693aa9296aSHimanshu Bhavani 	return 0;
10703aa9296aSHimanshu Bhavani 
10713aa9296aSHimanshu Bhavani error_subdev_cleanup:
10723aa9296aSHimanshu Bhavani 	v4l2_subdev_cleanup(&os05b10->sd);
10733aa9296aSHimanshu Bhavani 	pm_runtime_disable(os05b10->dev);
10743aa9296aSHimanshu Bhavani 	pm_runtime_set_suspended(os05b10->dev);
10753aa9296aSHimanshu Bhavani 
10763aa9296aSHimanshu Bhavani error_media_entity:
10773aa9296aSHimanshu Bhavani 	media_entity_cleanup(&os05b10->sd.entity);
10783aa9296aSHimanshu Bhavani 
10793aa9296aSHimanshu Bhavani error_handler_free:
10803aa9296aSHimanshu Bhavani 	v4l2_ctrl_handler_free(os05b10->sd.ctrl_handler);
10813aa9296aSHimanshu Bhavani 
10823aa9296aSHimanshu Bhavani error_power_off:
10833aa9296aSHimanshu Bhavani 	os05b10_power_off(os05b10->dev);
10843aa9296aSHimanshu Bhavani 
10853aa9296aSHimanshu Bhavani 	return ret;
10863aa9296aSHimanshu Bhavani }
10873aa9296aSHimanshu Bhavani 
10883aa9296aSHimanshu Bhavani static void os05b10_remove(struct i2c_client *client)
10893aa9296aSHimanshu Bhavani {
10903aa9296aSHimanshu Bhavani 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
10913aa9296aSHimanshu Bhavani 	struct os05b10 *os05b10 = to_os05b10(sd);
10923aa9296aSHimanshu Bhavani 
10933aa9296aSHimanshu Bhavani 	v4l2_async_unregister_subdev(sd);
10943aa9296aSHimanshu Bhavani 	v4l2_subdev_cleanup(&os05b10->sd);
10953aa9296aSHimanshu Bhavani 	media_entity_cleanup(&sd->entity);
10963aa9296aSHimanshu Bhavani 	v4l2_ctrl_handler_free(os05b10->sd.ctrl_handler);
10973aa9296aSHimanshu Bhavani 
10983aa9296aSHimanshu Bhavani 	pm_runtime_disable(&client->dev);
10993aa9296aSHimanshu Bhavani 	if (!pm_runtime_status_suspended(&client->dev)) {
11003aa9296aSHimanshu Bhavani 		os05b10_power_off(&client->dev);
11013aa9296aSHimanshu Bhavani 		pm_runtime_set_suspended(&client->dev);
11023aa9296aSHimanshu Bhavani 	}
11033aa9296aSHimanshu Bhavani }
11043aa9296aSHimanshu Bhavani 
11053aa9296aSHimanshu Bhavani static DEFINE_RUNTIME_DEV_PM_OPS(os05b10_pm_ops, os05b10_power_off,
11063aa9296aSHimanshu Bhavani 				 os05b10_power_on, NULL);
11073aa9296aSHimanshu Bhavani 
11083aa9296aSHimanshu Bhavani static const struct of_device_id os05b10_id[] = {
11093aa9296aSHimanshu Bhavani 	{ .compatible = "ovti,os05b10" },
11103aa9296aSHimanshu Bhavani 	{ /* sentinel */ }
11113aa9296aSHimanshu Bhavani };
11123aa9296aSHimanshu Bhavani 
11133aa9296aSHimanshu Bhavani MODULE_DEVICE_TABLE(of, os05b10_id);
11143aa9296aSHimanshu Bhavani 
11153aa9296aSHimanshu Bhavani static struct i2c_driver os05b10_driver = {
11163aa9296aSHimanshu Bhavani 	.driver = {
11173aa9296aSHimanshu Bhavani 		.name = "os05b10",
11183aa9296aSHimanshu Bhavani 		.pm = pm_ptr(&os05b10_pm_ops),
11193aa9296aSHimanshu Bhavani 		.of_match_table = os05b10_id,
11203aa9296aSHimanshu Bhavani 	},
11213aa9296aSHimanshu Bhavani 	.probe = os05b10_probe,
11223aa9296aSHimanshu Bhavani 	.remove = os05b10_remove,
11233aa9296aSHimanshu Bhavani };
11243aa9296aSHimanshu Bhavani 
11253aa9296aSHimanshu Bhavani module_i2c_driver(os05b10_driver);
11263aa9296aSHimanshu Bhavani 
11273aa9296aSHimanshu Bhavani MODULE_DESCRIPTION("OS05B10 Camera Sensor Driver");
11283aa9296aSHimanshu Bhavani MODULE_AUTHOR("Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>");
11293aa9296aSHimanshu Bhavani MODULE_AUTHOR("Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>");
11303aa9296aSHimanshu Bhavani MODULE_LICENSE("GPL");
1131