xref: /openbmc/linux/drivers/media/i2c/ov08d10.c (revision aaeb31c0)
17be91e02SJimmy Su // SPDX-License-Identifier: GPL-2.0
27be91e02SJimmy Su // Copyright (c) 2022 Intel Corporation.
37be91e02SJimmy Su 
47be91e02SJimmy Su #include <linux/acpi.h>
57be91e02SJimmy Su #include <linux/clk.h>
67be91e02SJimmy Su #include <linux/delay.h>
77be91e02SJimmy Su #include <linux/i2c.h>
87be91e02SJimmy Su #include <linux/module.h>
97be91e02SJimmy Su #include <linux/pm_runtime.h>
107be91e02SJimmy Su #include <linux/regulator/consumer.h>
117be91e02SJimmy Su #include <media/v4l2-ctrls.h>
127be91e02SJimmy Su #include <media/v4l2-device.h>
137be91e02SJimmy Su #include <media/v4l2-fwnode.h>
147be91e02SJimmy Su 
157be91e02SJimmy Su #define OV08D10_SCLK			144000000ULL
167be91e02SJimmy Su #define OV08D10_XVCLK_19_2		19200000
177be91e02SJimmy Su #define OV08D10_ROWCLK			36000
187be91e02SJimmy Su #define OV08D10_DATA_LANES		2
197be91e02SJimmy Su #define OV08D10_RGB_DEPTH		10
207be91e02SJimmy Su 
217be91e02SJimmy Su #define OV08D10_REG_PAGE		0xfd
227be91e02SJimmy Su #define OV08D10_REG_GLOBAL_EFFECTIVE		0x01
237be91e02SJimmy Su #define OV08D10_REG_CHIP_ID_0		0x00
247be91e02SJimmy Su #define OV08D10_REG_CHIP_ID_1		0x01
257be91e02SJimmy Su #define OV08D10_ID_MASK			GENMASK(15, 0)
267be91e02SJimmy Su #define OV08D10_CHIP_ID			0x5608
277be91e02SJimmy Su 
287be91e02SJimmy Su #define OV08D10_REG_MODE_SELECT		0xa0
297be91e02SJimmy Su #define OV08D10_MODE_STANDBY		0x00
307be91e02SJimmy Su #define OV08D10_MODE_STREAMING		0x01
317be91e02SJimmy Su 
327be91e02SJimmy Su /* vertical-timings from sensor */
337be91e02SJimmy Su #define OV08D10_REG_VTS_H		0x05
347be91e02SJimmy Su #define OV08D10_REG_VTS_L		0x06
357be91e02SJimmy Su #define OV08D10_VTS_MAX			0x7fff
367be91e02SJimmy Su 
377be91e02SJimmy Su /* Exposure controls from sensor */
387be91e02SJimmy Su #define OV08D10_REG_EXPOSURE_H		0x02
397be91e02SJimmy Su #define OV08D10_REG_EXPOSURE_M		0x03
407be91e02SJimmy Su #define OV08D10_REG_EXPOSURE_L		0x04
417be91e02SJimmy Su #define	OV08D10_EXPOSURE_MIN		6
427be91e02SJimmy Su #define OV08D10_EXPOSURE_MAX_MARGIN	6
437be91e02SJimmy Su #define	OV08D10_EXPOSURE_STEP		1
447be91e02SJimmy Su 
457be91e02SJimmy Su /* Analog gain controls from sensor */
467be91e02SJimmy Su #define OV08D10_REG_ANALOG_GAIN		0x24
477be91e02SJimmy Su #define	OV08D10_ANAL_GAIN_MIN		128
487be91e02SJimmy Su #define	OV08D10_ANAL_GAIN_MAX		2047
497be91e02SJimmy Su #define	OV08D10_ANAL_GAIN_STEP		1
507be91e02SJimmy Su 
517be91e02SJimmy Su /* Digital gain controls from sensor */
527be91e02SJimmy Su #define OV08D10_REG_MWB_DGAIN_C		0x21
537be91e02SJimmy Su #define OV08D10_REG_MWB_DGAIN_F		0x22
547be91e02SJimmy Su #define OV08D10_DGTL_GAIN_MIN		0
557be91e02SJimmy Su #define OV08D10_DGTL_GAIN_MAX		4095
567be91e02SJimmy Su #define OV08D10_DGTL_GAIN_STEP		1
577be91e02SJimmy Su #define OV08D10_DGTL_GAIN_DEFAULT	1024
587be91e02SJimmy Su 
597be91e02SJimmy Su /* Test Pattern Control */
607be91e02SJimmy Su #define OV08D10_REG_TEST_PATTERN		0x12
617be91e02SJimmy Su #define OV08D10_TEST_PATTERN_ENABLE		0x01
627be91e02SJimmy Su #define OV08D10_TEST_PATTERN_DISABLE		0x00
637be91e02SJimmy Su 
647be91e02SJimmy Su /* Flip Mirror Controls from sensor */
657be91e02SJimmy Su #define OV08D10_REG_FLIP_OPT			0x32
667be91e02SJimmy Su #define OV08D10_REG_FLIP_MASK			0x3
677be91e02SJimmy Su 
687be91e02SJimmy Su #define to_ov08d10(_sd)		container_of(_sd, struct ov08d10, sd)
697be91e02SJimmy Su 
707be91e02SJimmy Su struct ov08d10_reg {
717be91e02SJimmy Su 	u8 address;
727be91e02SJimmy Su 	u8 val;
737be91e02SJimmy Su };
747be91e02SJimmy Su 
757be91e02SJimmy Su struct ov08d10_reg_list {
767be91e02SJimmy Su 	u32 num_of_regs;
777be91e02SJimmy Su 	const struct ov08d10_reg *regs;
787be91e02SJimmy Su };
797be91e02SJimmy Su 
807be91e02SJimmy Su struct ov08d10_link_freq_config {
817be91e02SJimmy Su 	const struct ov08d10_reg_list reg_list;
827be91e02SJimmy Su };
837be91e02SJimmy Su 
847be91e02SJimmy Su struct ov08d10_mode {
857be91e02SJimmy Su 	/* Frame width in pixels */
867be91e02SJimmy Su 	u32 width;
877be91e02SJimmy Su 
887be91e02SJimmy Su 	/* Frame height in pixels */
897be91e02SJimmy Su 	u32 height;
907be91e02SJimmy Su 
917be91e02SJimmy Su 	/* Horizontal timining size */
927be91e02SJimmy Su 	u32 hts;
937be91e02SJimmy Su 
947be91e02SJimmy Su 	/* Default vertical timining size */
957be91e02SJimmy Su 	u32 vts_def;
967be91e02SJimmy Su 
977be91e02SJimmy Su 	/* Min vertical timining size */
987be91e02SJimmy Su 	u32 vts_min;
997be91e02SJimmy Su 
1007be91e02SJimmy Su 	/* Link frequency needed for this resolution */
1017be91e02SJimmy Su 	u32 link_freq_index;
1027be91e02SJimmy Su 
1037be91e02SJimmy Su 	/* Sensor register settings for this resolution */
1047be91e02SJimmy Su 	const struct ov08d10_reg_list reg_list;
1057be91e02SJimmy Su 
1067be91e02SJimmy Su 	/* Number of data lanes */
1077be91e02SJimmy Su 	u8 data_lanes;
1087be91e02SJimmy Su };
1097be91e02SJimmy Su 
1107be91e02SJimmy Su /* 3280x2460, 3264x2448 need 720Mbps/lane, 2 lanes */
1117be91e02SJimmy Su static const struct ov08d10_reg mipi_data_rate_720mbps[] = {
1127be91e02SJimmy Su 	{0xfd, 0x00},
1137be91e02SJimmy Su 	{0x11, 0x2a},
1147be91e02SJimmy Su 	{0x14, 0x43},
1157be91e02SJimmy Su 	{0x1a, 0x04},
1167be91e02SJimmy Su 	{0x1b, 0xe1},
1177be91e02SJimmy Su 	{0x1e, 0x13},
1187be91e02SJimmy Su 	{0xb7, 0x02}
1197be91e02SJimmy Su };
1207be91e02SJimmy Su 
1217be91e02SJimmy Su /* 1632x1224 needs 360Mbps/lane, 2 lanes */
1227be91e02SJimmy Su static const struct ov08d10_reg mipi_data_rate_360mbps[] = {
1237be91e02SJimmy Su 	{0xfd, 0x00},
1247be91e02SJimmy Su 	{0x1a, 0x04},
1257be91e02SJimmy Su 	{0x1b, 0xe1},
1267be91e02SJimmy Su 	{0x1d, 0x00},
1277be91e02SJimmy Su 	{0x1c, 0x19},
1287be91e02SJimmy Su 	{0x11, 0x2a},
1297be91e02SJimmy Su 	{0x14, 0x54},
1307be91e02SJimmy Su 	{0x1e, 0x13},
1317be91e02SJimmy Su 	{0xb7, 0x02}
1327be91e02SJimmy Su };
1337be91e02SJimmy Su 
1347be91e02SJimmy Su static const struct ov08d10_reg lane_2_mode_3280x2460[] = {
1357be91e02SJimmy Su 	/* 3280x2460 resolution */
1367be91e02SJimmy Su 	{0xfd, 0x01},
1377be91e02SJimmy Su 	{0x12, 0x00},
1387be91e02SJimmy Su 	{0x03, 0x12},
1397be91e02SJimmy Su 	{0x04, 0x58},
1407be91e02SJimmy Su 	{0x07, 0x05},
1417be91e02SJimmy Su 	{0x21, 0x02},
1427be91e02SJimmy Su 	{0x24, 0x30},
1437be91e02SJimmy Su 	{0x33, 0x03},
1447be91e02SJimmy Su 	{0x01, 0x03},
1457be91e02SJimmy Su 	{0x19, 0x10},
1467be91e02SJimmy Su 	{0x42, 0x55},
1477be91e02SJimmy Su 	{0x43, 0x00},
1487be91e02SJimmy Su 	{0x47, 0x07},
1497be91e02SJimmy Su 	{0x48, 0x08},
1507be91e02SJimmy Su 	{0xb2, 0x7f},
1517be91e02SJimmy Su 	{0xb3, 0x7b},
1527be91e02SJimmy Su 	{0xbd, 0x08},
1537be91e02SJimmy Su 	{0xd2, 0x57},
1547be91e02SJimmy Su 	{0xd3, 0x10},
1557be91e02SJimmy Su 	{0xd4, 0x08},
1567be91e02SJimmy Su 	{0xd5, 0x08},
1577be91e02SJimmy Su 	{0xd6, 0x06},
1587be91e02SJimmy Su 	{0xb1, 0x00},
1597be91e02SJimmy Su 	{0xb4, 0x00},
1607be91e02SJimmy Su 	{0xb7, 0x0a},
1617be91e02SJimmy Su 	{0xbc, 0x44},
1627be91e02SJimmy Su 	{0xbf, 0x48},
1637be91e02SJimmy Su 	{0xc1, 0x10},
1647be91e02SJimmy Su 	{0xc3, 0x24},
1657be91e02SJimmy Su 	{0xc8, 0x03},
1667be91e02SJimmy Su 	{0xc9, 0xf8},
1677be91e02SJimmy Su 	{0xe1, 0x33},
1687be91e02SJimmy Su 	{0xe2, 0xbb},
1697be91e02SJimmy Su 	{0x51, 0x0c},
1707be91e02SJimmy Su 	{0x52, 0x0a},
1717be91e02SJimmy Su 	{0x57, 0x8c},
1727be91e02SJimmy Su 	{0x59, 0x09},
1737be91e02SJimmy Su 	{0x5a, 0x08},
1747be91e02SJimmy Su 	{0x5e, 0x10},
1757be91e02SJimmy Su 	{0x60, 0x02},
1767be91e02SJimmy Su 	{0x6d, 0x5c},
1777be91e02SJimmy Su 	{0x76, 0x16},
1787be91e02SJimmy Su 	{0x7c, 0x11},
1797be91e02SJimmy Su 	{0x90, 0x28},
1807be91e02SJimmy Su 	{0x91, 0x16},
1817be91e02SJimmy Su 	{0x92, 0x1c},
1827be91e02SJimmy Su 	{0x93, 0x24},
1837be91e02SJimmy Su 	{0x95, 0x48},
1847be91e02SJimmy Su 	{0x9c, 0x06},
1857be91e02SJimmy Su 	{0xca, 0x0c},
1867be91e02SJimmy Su 	{0xce, 0x0d},
1877be91e02SJimmy Su 	{0xfd, 0x01},
1887be91e02SJimmy Su 	{0xc0, 0x00},
1897be91e02SJimmy Su 	{0xdd, 0x18},
1907be91e02SJimmy Su 	{0xde, 0x19},
1917be91e02SJimmy Su 	{0xdf, 0x32},
1927be91e02SJimmy Su 	{0xe0, 0x70},
1937be91e02SJimmy Su 	{0xfd, 0x01},
1947be91e02SJimmy Su 	{0xc2, 0x05},
1957be91e02SJimmy Su 	{0xd7, 0x88},
1967be91e02SJimmy Su 	{0xd8, 0x77},
1977be91e02SJimmy Su 	{0xd9, 0x00},
1987be91e02SJimmy Su 	{0xfd, 0x07},
1997be91e02SJimmy Su 	{0x00, 0xf8},
2007be91e02SJimmy Su 	{0x01, 0x2b},
2017be91e02SJimmy Su 	{0x05, 0x40},
2027be91e02SJimmy Su 	{0x08, 0x06},
2037be91e02SJimmy Su 	{0x09, 0x11},
2047be91e02SJimmy Su 	{0x28, 0x6f},
2057be91e02SJimmy Su 	{0x2a, 0x20},
2067be91e02SJimmy Su 	{0x2b, 0x05},
2077be91e02SJimmy Su 	{0x5e, 0x10},
2087be91e02SJimmy Su 	{0x52, 0x00},
2097be91e02SJimmy Su 	{0x53, 0x7c},
2107be91e02SJimmy Su 	{0x54, 0x00},
2117be91e02SJimmy Su 	{0x55, 0x7c},
2127be91e02SJimmy Su 	{0x56, 0x00},
2137be91e02SJimmy Su 	{0x57, 0x7c},
2147be91e02SJimmy Su 	{0x58, 0x00},
2157be91e02SJimmy Su 	{0x59, 0x7c},
2167be91e02SJimmy Su 	{0xfd, 0x02},
2177be91e02SJimmy Su 	{0x9a, 0x30},
2187be91e02SJimmy Su 	{0xa8, 0x02},
2197be91e02SJimmy Su 	{0xfd, 0x02},
2207be91e02SJimmy Su 	{0xa1, 0x01},
2217be91e02SJimmy Su 	{0xa2, 0x09},
2227be91e02SJimmy Su 	{0xa3, 0x9c},
2237be91e02SJimmy Su 	{0xa5, 0x00},
2247be91e02SJimmy Su 	{0xa6, 0x0c},
2257be91e02SJimmy Su 	{0xa7, 0xd0},
2267be91e02SJimmy Su 	{0xfd, 0x00},
2277be91e02SJimmy Su 	{0x24, 0x01},
2287be91e02SJimmy Su 	{0xc0, 0x16},
2297be91e02SJimmy Su 	{0xc1, 0x08},
2307be91e02SJimmy Su 	{0xc2, 0x30},
2317be91e02SJimmy Su 	{0x8e, 0x0c},
2327be91e02SJimmy Su 	{0x8f, 0xd0},
2337be91e02SJimmy Su 	{0x90, 0x09},
2347be91e02SJimmy Su 	{0x91, 0x9c},
2357be91e02SJimmy Su 	{0xfd, 0x05},
2367be91e02SJimmy Su 	{0x04, 0x40},
2377be91e02SJimmy Su 	{0x07, 0x00},
2387be91e02SJimmy Su 	{0x0d, 0x01},
2397be91e02SJimmy Su 	{0x0f, 0x01},
2407be91e02SJimmy Su 	{0x10, 0x00},
2417be91e02SJimmy Su 	{0x11, 0x00},
2427be91e02SJimmy Su 	{0x12, 0x0c},
2437be91e02SJimmy Su 	{0x13, 0xcf},
2447be91e02SJimmy Su 	{0x14, 0x00},
2457be91e02SJimmy Su 	{0x15, 0x00},
2467be91e02SJimmy Su 	{0xfd, 0x00},
2477be91e02SJimmy Su 	{0x20, 0x0f},
2487be91e02SJimmy Su 	{0xe7, 0x03},
2497be91e02SJimmy Su 	{0xe7, 0x00}
2507be91e02SJimmy Su };
2517be91e02SJimmy Su 
2527be91e02SJimmy Su static const struct ov08d10_reg lane_2_mode_3264x2448[] = {
2537be91e02SJimmy Su 	/* 3264x2448 resolution */
2547be91e02SJimmy Su 	{0xfd, 0x01},
2557be91e02SJimmy Su 	{0x12, 0x00},
2567be91e02SJimmy Su 	{0x03, 0x12},
2577be91e02SJimmy Su 	{0x04, 0x58},
2587be91e02SJimmy Su 	{0x07, 0x05},
2597be91e02SJimmy Su 	{0x21, 0x02},
2607be91e02SJimmy Su 	{0x24, 0x30},
2617be91e02SJimmy Su 	{0x33, 0x03},
2627be91e02SJimmy Su 	{0x01, 0x03},
2637be91e02SJimmy Su 	{0x19, 0x10},
2647be91e02SJimmy Su 	{0x42, 0x55},
2657be91e02SJimmy Su 	{0x43, 0x00},
2667be91e02SJimmy Su 	{0x47, 0x07},
2677be91e02SJimmy Su 	{0x48, 0x08},
2687be91e02SJimmy Su 	{0xb2, 0x7f},
2697be91e02SJimmy Su 	{0xb3, 0x7b},
2707be91e02SJimmy Su 	{0xbd, 0x08},
2717be91e02SJimmy Su 	{0xd2, 0x57},
2727be91e02SJimmy Su 	{0xd3, 0x10},
2737be91e02SJimmy Su 	{0xd4, 0x08},
2747be91e02SJimmy Su 	{0xd5, 0x08},
2757be91e02SJimmy Su 	{0xd6, 0x06},
2767be91e02SJimmy Su 	{0xb1, 0x00},
2777be91e02SJimmy Su 	{0xb4, 0x00},
2787be91e02SJimmy Su 	{0xb7, 0x0a},
2797be91e02SJimmy Su 	{0xbc, 0x44},
2807be91e02SJimmy Su 	{0xbf, 0x48},
2817be91e02SJimmy Su 	{0xc1, 0x10},
2827be91e02SJimmy Su 	{0xc3, 0x24},
2837be91e02SJimmy Su 	{0xc8, 0x03},
2847be91e02SJimmy Su 	{0xc9, 0xf8},
2857be91e02SJimmy Su 	{0xe1, 0x33},
2867be91e02SJimmy Su 	{0xe2, 0xbb},
2877be91e02SJimmy Su 	{0x51, 0x0c},
2887be91e02SJimmy Su 	{0x52, 0x0a},
2897be91e02SJimmy Su 	{0x57, 0x8c},
2907be91e02SJimmy Su 	{0x59, 0x09},
2917be91e02SJimmy Su 	{0x5a, 0x08},
2927be91e02SJimmy Su 	{0x5e, 0x10},
2937be91e02SJimmy Su 	{0x60, 0x02},
2947be91e02SJimmy Su 	{0x6d, 0x5c},
2957be91e02SJimmy Su 	{0x76, 0x16},
2967be91e02SJimmy Su 	{0x7c, 0x11},
2977be91e02SJimmy Su 	{0x90, 0x28},
2987be91e02SJimmy Su 	{0x91, 0x16},
2997be91e02SJimmy Su 	{0x92, 0x1c},
3007be91e02SJimmy Su 	{0x93, 0x24},
3017be91e02SJimmy Su 	{0x95, 0x48},
3027be91e02SJimmy Su 	{0x9c, 0x06},
3037be91e02SJimmy Su 	{0xca, 0x0c},
3047be91e02SJimmy Su 	{0xce, 0x0d},
3057be91e02SJimmy Su 	{0xfd, 0x01},
3067be91e02SJimmy Su 	{0xc0, 0x00},
3077be91e02SJimmy Su 	{0xdd, 0x18},
3087be91e02SJimmy Su 	{0xde, 0x19},
3097be91e02SJimmy Su 	{0xdf, 0x32},
3107be91e02SJimmy Su 	{0xe0, 0x70},
3117be91e02SJimmy Su 	{0xfd, 0x01},
3127be91e02SJimmy Su 	{0xc2, 0x05},
3137be91e02SJimmy Su 	{0xd7, 0x88},
3147be91e02SJimmy Su 	{0xd8, 0x77},
3157be91e02SJimmy Su 	{0xd9, 0x00},
3167be91e02SJimmy Su 	{0xfd, 0x07},
3177be91e02SJimmy Su 	{0x00, 0xf8},
3187be91e02SJimmy Su 	{0x01, 0x2b},
3197be91e02SJimmy Su 	{0x05, 0x40},
3207be91e02SJimmy Su 	{0x08, 0x06},
3217be91e02SJimmy Su 	{0x09, 0x11},
3227be91e02SJimmy Su 	{0x28, 0x6f},
3237be91e02SJimmy Su 	{0x2a, 0x20},
3247be91e02SJimmy Su 	{0x2b, 0x05},
3257be91e02SJimmy Su 	{0x5e, 0x10},
3267be91e02SJimmy Su 	{0x52, 0x00},
3277be91e02SJimmy Su 	{0x53, 0x7c},
3287be91e02SJimmy Su 	{0x54, 0x00},
3297be91e02SJimmy Su 	{0x55, 0x7c},
3307be91e02SJimmy Su 	{0x56, 0x00},
3317be91e02SJimmy Su 	{0x57, 0x7c},
3327be91e02SJimmy Su 	{0x58, 0x00},
3337be91e02SJimmy Su 	{0x59, 0x7c},
3347be91e02SJimmy Su 	{0xfd, 0x02},
3357be91e02SJimmy Su 	{0x9a, 0x30},
3367be91e02SJimmy Su 	{0xa8, 0x02},
3377be91e02SJimmy Su 	{0xfd, 0x02},
3387be91e02SJimmy Su 	{0xa1, 0x09},
3397be91e02SJimmy Su 	{0xa2, 0x09},
3407be91e02SJimmy Su 	{0xa3, 0x90},
3417be91e02SJimmy Su 	{0xa5, 0x08},
3427be91e02SJimmy Su 	{0xa6, 0x0c},
3437be91e02SJimmy Su 	{0xa7, 0xc0},
3447be91e02SJimmy Su 	{0xfd, 0x00},
3457be91e02SJimmy Su 	{0x24, 0x01},
3467be91e02SJimmy Su 	{0xc0, 0x16},
3477be91e02SJimmy Su 	{0xc1, 0x08},
3487be91e02SJimmy Su 	{0xc2, 0x30},
3497be91e02SJimmy Su 	{0x8e, 0x0c},
3507be91e02SJimmy Su 	{0x8f, 0xc0},
3517be91e02SJimmy Su 	{0x90, 0x09},
3527be91e02SJimmy Su 	{0x91, 0x90},
3537be91e02SJimmy Su 	{0xfd, 0x05},
3547be91e02SJimmy Su 	{0x04, 0x40},
3557be91e02SJimmy Su 	{0x07, 0x00},
3567be91e02SJimmy Su 	{0x0d, 0x01},
3577be91e02SJimmy Su 	{0x0f, 0x01},
3587be91e02SJimmy Su 	{0x10, 0x00},
3597be91e02SJimmy Su 	{0x11, 0x00},
3607be91e02SJimmy Su 	{0x12, 0x0c},
3617be91e02SJimmy Su 	{0x13, 0xcf},
3627be91e02SJimmy Su 	{0x14, 0x00},
3637be91e02SJimmy Su 	{0x15, 0x00},
3647be91e02SJimmy Su 	{0xfd, 0x00},
3657be91e02SJimmy Su 	{0x20, 0x0f},
3667be91e02SJimmy Su 	{0xe7, 0x03},
3677be91e02SJimmy Su 	{0xe7, 0x00}
3687be91e02SJimmy Su };
3697be91e02SJimmy Su 
3707be91e02SJimmy Su static const struct ov08d10_reg lane_2_mode_1632x1224[] = {
3717be91e02SJimmy Su 	/* 1640x1232 resolution */
3727be91e02SJimmy Su 	{0xfd, 0x01},
3737be91e02SJimmy Su 	{0x1a, 0x0a},
3747be91e02SJimmy Su 	{0x1b, 0x08},
3757be91e02SJimmy Su 	{0x2a, 0x01},
3767be91e02SJimmy Su 	{0x2b, 0x9a},
3777be91e02SJimmy Su 	{0xfd, 0x01},
3787be91e02SJimmy Su 	{0x12, 0x00},
3797be91e02SJimmy Su 	{0x03, 0x05},
3807be91e02SJimmy Su 	{0x04, 0xe2},
3817be91e02SJimmy Su 	{0x07, 0x05},
3827be91e02SJimmy Su 	{0x21, 0x02},
3837be91e02SJimmy Su 	{0x24, 0x30},
3847be91e02SJimmy Su 	{0x33, 0x03},
3857be91e02SJimmy Su 	{0x31, 0x06},
3867be91e02SJimmy Su 	{0x33, 0x03},
3877be91e02SJimmy Su 	{0x01, 0x03},
3887be91e02SJimmy Su 	{0x19, 0x10},
3897be91e02SJimmy Su 	{0x42, 0x55},
3907be91e02SJimmy Su 	{0x43, 0x00},
3917be91e02SJimmy Su 	{0x47, 0x07},
3927be91e02SJimmy Su 	{0x48, 0x08},
3937be91e02SJimmy Su 	{0xb2, 0x7f},
3947be91e02SJimmy Su 	{0xb3, 0x7b},
3957be91e02SJimmy Su 	{0xbd, 0x08},
3967be91e02SJimmy Su 	{0xd2, 0x57},
3977be91e02SJimmy Su 	{0xd3, 0x10},
3987be91e02SJimmy Su 	{0xd4, 0x08},
3997be91e02SJimmy Su 	{0xd5, 0x08},
4007be91e02SJimmy Su 	{0xd6, 0x06},
4017be91e02SJimmy Su 	{0xb1, 0x00},
4027be91e02SJimmy Su 	{0xb4, 0x00},
4037be91e02SJimmy Su 	{0xb7, 0x0a},
4047be91e02SJimmy Su 	{0xbc, 0x44},
4057be91e02SJimmy Su 	{0xbf, 0x48},
4067be91e02SJimmy Su 	{0xc1, 0x10},
4077be91e02SJimmy Su 	{0xc3, 0x24},
4087be91e02SJimmy Su 	{0xc8, 0x03},
4097be91e02SJimmy Su 	{0xc9, 0xf8},
4107be91e02SJimmy Su 	{0xe1, 0x33},
4117be91e02SJimmy Su 	{0xe2, 0xbb},
4127be91e02SJimmy Su 	{0x51, 0x0c},
4137be91e02SJimmy Su 	{0x52, 0x0a},
4147be91e02SJimmy Su 	{0x57, 0x8c},
4157be91e02SJimmy Su 	{0x59, 0x09},
4167be91e02SJimmy Su 	{0x5a, 0x08},
4177be91e02SJimmy Su 	{0x5e, 0x10},
4187be91e02SJimmy Su 	{0x60, 0x02},
4197be91e02SJimmy Su 	{0x6d, 0x5c},
4207be91e02SJimmy Su 	{0x76, 0x16},
4217be91e02SJimmy Su 	{0x7c, 0x1a},
4227be91e02SJimmy Su 	{0x90, 0x28},
4237be91e02SJimmy Su 	{0x91, 0x16},
4247be91e02SJimmy Su 	{0x92, 0x1c},
4257be91e02SJimmy Su 	{0x93, 0x24},
4267be91e02SJimmy Su 	{0x95, 0x48},
4277be91e02SJimmy Su 	{0x9c, 0x06},
4287be91e02SJimmy Su 	{0xca, 0x0c},
4297be91e02SJimmy Su 	{0xce, 0x0d},
4307be91e02SJimmy Su 	{0xfd, 0x01},
4317be91e02SJimmy Su 	{0xc0, 0x00},
4327be91e02SJimmy Su 	{0xdd, 0x18},
4337be91e02SJimmy Su 	{0xde, 0x19},
4347be91e02SJimmy Su 	{0xdf, 0x32},
4357be91e02SJimmy Su 	{0xe0, 0x70},
4367be91e02SJimmy Su 	{0xfd, 0x01},
4377be91e02SJimmy Su 	{0xc2, 0x05},
4387be91e02SJimmy Su 	{0xd7, 0x88},
4397be91e02SJimmy Su 	{0xd8, 0x77},
4407be91e02SJimmy Su 	{0xd9, 0x00},
4417be91e02SJimmy Su 	{0xfd, 0x07},
4427be91e02SJimmy Su 	{0x00, 0xf8},
4437be91e02SJimmy Su 	{0x01, 0x2b},
4447be91e02SJimmy Su 	{0x05, 0x40},
4457be91e02SJimmy Su 	{0x08, 0x03},
4467be91e02SJimmy Su 	{0x09, 0x08},
4477be91e02SJimmy Su 	{0x28, 0x6f},
4487be91e02SJimmy Su 	{0x2a, 0x20},
4497be91e02SJimmy Su 	{0x2b, 0x05},
4507be91e02SJimmy Su 	{0x2c, 0x01},
4517be91e02SJimmy Su 	{0x50, 0x02},
4527be91e02SJimmy Su 	{0x51, 0x03},
4537be91e02SJimmy Su 	{0x5e, 0x00},
4547be91e02SJimmy Su 	{0x52, 0x00},
4557be91e02SJimmy Su 	{0x53, 0x7c},
4567be91e02SJimmy Su 	{0x54, 0x00},
4577be91e02SJimmy Su 	{0x55, 0x7c},
4587be91e02SJimmy Su 	{0x56, 0x00},
4597be91e02SJimmy Su 	{0x57, 0x7c},
4607be91e02SJimmy Su 	{0x58, 0x00},
4617be91e02SJimmy Su 	{0x59, 0x7c},
4627be91e02SJimmy Su 	{0xfd, 0x02},
4637be91e02SJimmy Su 	{0x9a, 0x30},
4647be91e02SJimmy Su 	{0xa8, 0x02},
4657be91e02SJimmy Su 	{0xfd, 0x02},
4667be91e02SJimmy Su 	{0xa9, 0x04},
4677be91e02SJimmy Su 	{0xaa, 0xd0},
4687be91e02SJimmy Su 	{0xab, 0x06},
4697be91e02SJimmy Su 	{0xac, 0x68},
4707be91e02SJimmy Su 	{0xa1, 0x09},
4717be91e02SJimmy Su 	{0xa2, 0x04},
4727be91e02SJimmy Su 	{0xa3, 0xc8},
4737be91e02SJimmy Su 	{0xa5, 0x04},
4747be91e02SJimmy Su 	{0xa6, 0x06},
4757be91e02SJimmy Su 	{0xa7, 0x60},
4767be91e02SJimmy Su 	{0xfd, 0x05},
4777be91e02SJimmy Su 	{0x06, 0x80},
4787be91e02SJimmy Su 	{0x18, 0x06},
4797be91e02SJimmy Su 	{0x19, 0x68},
4807be91e02SJimmy Su 	{0xfd, 0x00},
4817be91e02SJimmy Su 	{0x24, 0x01},
4827be91e02SJimmy Su 	{0xc0, 0x16},
4837be91e02SJimmy Su 	{0xc1, 0x08},
4847be91e02SJimmy Su 	{0xc2, 0x30},
4857be91e02SJimmy Su 	{0x8e, 0x06},
4867be91e02SJimmy Su 	{0x8f, 0x60},
4877be91e02SJimmy Su 	{0x90, 0x04},
4887be91e02SJimmy Su 	{0x91, 0xc8},
4897be91e02SJimmy Su 	{0x93, 0x0e},
4907be91e02SJimmy Su 	{0x94, 0x77},
4917be91e02SJimmy Su 	{0x95, 0x77},
4927be91e02SJimmy Su 	{0x96, 0x10},
4937be91e02SJimmy Su 	{0x98, 0x88},
4947be91e02SJimmy Su 	{0x9c, 0x1a},
4957be91e02SJimmy Su 	{0xfd, 0x05},
4967be91e02SJimmy Su 	{0x04, 0x40},
4977be91e02SJimmy Su 	{0x07, 0x99},
4987be91e02SJimmy Su 	{0x0d, 0x03},
4997be91e02SJimmy Su 	{0x0f, 0x03},
5007be91e02SJimmy Su 	{0x10, 0x00},
5017be91e02SJimmy Su 	{0x11, 0x00},
5027be91e02SJimmy Su 	{0x12, 0x0c},
5037be91e02SJimmy Su 	{0x13, 0xcf},
5047be91e02SJimmy Su 	{0x14, 0x00},
5057be91e02SJimmy Su 	{0x15, 0x00},
5067be91e02SJimmy Su 	{0xfd, 0x00},
5077be91e02SJimmy Su 	{0x20, 0x0f},
5087be91e02SJimmy Su 	{0xe7, 0x03},
5097be91e02SJimmy Su 	{0xe7, 0x00},
5107be91e02SJimmy Su };
5117be91e02SJimmy Su 
5127be91e02SJimmy Su static const char * const ov08d10_test_pattern_menu[] = {
5137be91e02SJimmy Su 	"Disabled",
5147be91e02SJimmy Su 	"Standard Color Bar",
5157be91e02SJimmy Su };
5167be91e02SJimmy Su 
5177be91e02SJimmy Su struct ov08d10 {
5187be91e02SJimmy Su 	struct v4l2_subdev sd;
5197be91e02SJimmy Su 	struct media_pad pad;
5207be91e02SJimmy Su 	struct v4l2_ctrl_handler ctrl_handler;
5217be91e02SJimmy Su 
5227be91e02SJimmy Su 	struct clk		*xvclk;
5237be91e02SJimmy Su 
5247be91e02SJimmy Su 	/* V4L2 Controls */
5257be91e02SJimmy Su 	struct v4l2_ctrl *link_freq;
5267be91e02SJimmy Su 	struct v4l2_ctrl *pixel_rate;
5277be91e02SJimmy Su 	struct v4l2_ctrl *vblank;
5287be91e02SJimmy Su 	struct v4l2_ctrl *hblank;
5297be91e02SJimmy Su 	struct v4l2_ctrl *vflip;
5307be91e02SJimmy Su 	struct v4l2_ctrl *hflip;
5317be91e02SJimmy Su 	struct v4l2_ctrl *exposure;
5327be91e02SJimmy Su 
5337be91e02SJimmy Su 	/* Current mode */
5347be91e02SJimmy Su 	const struct ov08d10_mode *cur_mode;
5357be91e02SJimmy Su 
5367be91e02SJimmy Su 	/* To serialize asynchronus callbacks */
5377be91e02SJimmy Su 	struct mutex mutex;
5387be91e02SJimmy Su 
5397be91e02SJimmy Su 	/* Streaming on/off */
5407be91e02SJimmy Su 	bool streaming;
5417be91e02SJimmy Su 
5427be91e02SJimmy Su 	/* lanes index */
5437be91e02SJimmy Su 	u8 nlanes;
5447be91e02SJimmy Su 
5457be91e02SJimmy Su 	const struct ov08d10_lane_cfg *priv_lane;
5467be91e02SJimmy Su 	u8 modes_size;
5477be91e02SJimmy Su };
5487be91e02SJimmy Su 
5497be91e02SJimmy Su struct ov08d10_lane_cfg {
5507be91e02SJimmy Su 	const s64 link_freq_menu[2];
5517be91e02SJimmy Su 	const struct ov08d10_link_freq_config link_freq_configs[2];
5527be91e02SJimmy Su 	const struct ov08d10_mode sp_modes[3];
5537be91e02SJimmy Su };
5547be91e02SJimmy Su 
5557be91e02SJimmy Su static const struct ov08d10_lane_cfg lane_cfg_2 = {
5567be91e02SJimmy Su 	{
5577be91e02SJimmy Su 		720000000,
5587be91e02SJimmy Su 		360000000,
5597be91e02SJimmy Su 	},
5607be91e02SJimmy Su 	{{
5617be91e02SJimmy Su 		.reg_list = {
5627be91e02SJimmy Su 			.num_of_regs =
5637be91e02SJimmy Su 				ARRAY_SIZE(mipi_data_rate_720mbps),
5647be91e02SJimmy Su 			.regs = mipi_data_rate_720mbps,
5657be91e02SJimmy Su 		}
5667be91e02SJimmy Su 	},
5677be91e02SJimmy Su 	{
5687be91e02SJimmy Su 		.reg_list = {
5697be91e02SJimmy Su 			.num_of_regs =
5707be91e02SJimmy Su 				ARRAY_SIZE(mipi_data_rate_360mbps),
5717be91e02SJimmy Su 			.regs = mipi_data_rate_360mbps,
5727be91e02SJimmy Su 		}
5737be91e02SJimmy Su 	}},
5747be91e02SJimmy Su 	{{
5757be91e02SJimmy Su 		.width = 3280,
5767be91e02SJimmy Su 		.height = 2460,
5777be91e02SJimmy Su 		.hts = 1840,
5787be91e02SJimmy Su 		.vts_def = 2504,
5797be91e02SJimmy Su 		.vts_min = 2504,
5807be91e02SJimmy Su 		.reg_list = {
5817be91e02SJimmy Su 			.num_of_regs = ARRAY_SIZE(lane_2_mode_3280x2460),
5827be91e02SJimmy Su 			.regs = lane_2_mode_3280x2460,
5837be91e02SJimmy Su 		},
5847be91e02SJimmy Su 		.link_freq_index = 0,
5857be91e02SJimmy Su 		.data_lanes = 2,
5867be91e02SJimmy Su 	},
5877be91e02SJimmy Su 	{
5887be91e02SJimmy Su 		.width = 3264,
5897be91e02SJimmy Su 		.height = 2448,
5907be91e02SJimmy Su 		.hts = 1840,
5917be91e02SJimmy Su 		.vts_def = 2504,
5927be91e02SJimmy Su 		.vts_min = 2504,
5937be91e02SJimmy Su 		.reg_list = {
5947be91e02SJimmy Su 			.num_of_regs = ARRAY_SIZE(lane_2_mode_3264x2448),
5957be91e02SJimmy Su 			.regs = lane_2_mode_3264x2448,
5967be91e02SJimmy Su 		},
5977be91e02SJimmy Su 		.link_freq_index = 0,
5987be91e02SJimmy Su 		.data_lanes = 2,
5997be91e02SJimmy Su 	},
6007be91e02SJimmy Su 	{
6017be91e02SJimmy Su 		.width = 1632,
6027be91e02SJimmy Su 		.height = 1224,
6037be91e02SJimmy Su 		.hts = 1912,
6047be91e02SJimmy Su 		.vts_def = 3736,
6057be91e02SJimmy Su 		.vts_min = 3736,
6067be91e02SJimmy Su 		.reg_list = {
6077be91e02SJimmy Su 			.num_of_regs = ARRAY_SIZE(lane_2_mode_1632x1224),
6087be91e02SJimmy Su 			.regs = lane_2_mode_1632x1224,
6097be91e02SJimmy Su 		},
6107be91e02SJimmy Su 		.link_freq_index = 1,
6117be91e02SJimmy Su 		.data_lanes = 2,
6127be91e02SJimmy Su 	}}
6137be91e02SJimmy Su };
6147be91e02SJimmy Su 
ov08d10_get_format_code(struct ov08d10 * ov08d10)6157be91e02SJimmy Su static u32 ov08d10_get_format_code(struct ov08d10 *ov08d10)
6167be91e02SJimmy Su {
6177be91e02SJimmy Su 	static const u32 codes[2][2] = {
6187be91e02SJimmy Su 		{ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10},
6197be91e02SJimmy Su 		{ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10},
6207be91e02SJimmy Su 	};
6217be91e02SJimmy Su 
6227be91e02SJimmy Su 	return codes[ov08d10->vflip->val][ov08d10->hflip->val];
6237be91e02SJimmy Su }
6247be91e02SJimmy Su 
ov08d10_modes_num(const struct ov08d10 * ov08d10)6257be91e02SJimmy Su static unsigned int ov08d10_modes_num(const struct ov08d10 *ov08d10)
6267be91e02SJimmy Su {
6277be91e02SJimmy Su 	unsigned int i, count = 0;
6287be91e02SJimmy Su 
6297be91e02SJimmy Su 	for (i = 0; i < ARRAY_SIZE(ov08d10->priv_lane->sp_modes); i++) {
6307be91e02SJimmy Su 		if (ov08d10->priv_lane->sp_modes[i].width == 0)
6317be91e02SJimmy Su 			break;
6327be91e02SJimmy Su 		count++;
6337be91e02SJimmy Su 	}
6347be91e02SJimmy Su 
6357be91e02SJimmy Su 	return count;
6367be91e02SJimmy Su }
6377be91e02SJimmy Su 
to_rate(const s64 * link_freq_menu,u32 f_index,u8 nlanes)6387be91e02SJimmy Su static u64 to_rate(const s64 *link_freq_menu,
6397be91e02SJimmy Su 		   u32 f_index, u8 nlanes)
6407be91e02SJimmy Su {
6417be91e02SJimmy Su 	u64 pixel_rate = link_freq_menu[f_index] * 2 * nlanes;
6427be91e02SJimmy Su 
6437be91e02SJimmy Su 	do_div(pixel_rate, OV08D10_RGB_DEPTH);
6447be91e02SJimmy Su 
6457be91e02SJimmy Su 	return pixel_rate;
6467be91e02SJimmy Su }
6477be91e02SJimmy Su 
to_pixels_per_line(const s64 * link_freq_menu,u32 hts,u32 f_index,u8 nlanes)6487be91e02SJimmy Su static u64 to_pixels_per_line(const s64 *link_freq_menu, u32 hts,
6497be91e02SJimmy Su 			      u32 f_index, u8 nlanes)
6507be91e02SJimmy Su {
6517be91e02SJimmy Su 	u64 ppl = hts * to_rate(link_freq_menu, f_index, nlanes);
6527be91e02SJimmy Su 
6537be91e02SJimmy Su 	do_div(ppl, OV08D10_SCLK);
6547be91e02SJimmy Su 
6557be91e02SJimmy Su 	return ppl;
6567be91e02SJimmy Su }
6577be91e02SJimmy Su 
ov08d10_write_reg_list(struct ov08d10 * ov08d10,const struct ov08d10_reg_list * r_list)6587be91e02SJimmy Su static int ov08d10_write_reg_list(struct ov08d10 *ov08d10,
6597be91e02SJimmy Su 				  const struct ov08d10_reg_list *r_list)
6607be91e02SJimmy Su {
6617be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
6627be91e02SJimmy Su 	unsigned int i;
6637be91e02SJimmy Su 	int ret;
6647be91e02SJimmy Su 
6657be91e02SJimmy Su 	for (i = 0; i < r_list->num_of_regs; i++) {
6667be91e02SJimmy Su 		ret = i2c_smbus_write_byte_data(client, r_list->regs[i].address,
6677be91e02SJimmy Su 						r_list->regs[i].val);
6687be91e02SJimmy Su 		if (ret) {
6697be91e02SJimmy Su 			dev_err_ratelimited(&client->dev,
6707be91e02SJimmy Su 					    "failed to write reg 0x%2.2x. error = %d",
6717be91e02SJimmy Su 					    r_list->regs[i].address, ret);
6727be91e02SJimmy Su 			return ret;
6737be91e02SJimmy Su 		}
6747be91e02SJimmy Su 	}
6757be91e02SJimmy Su 
6767be91e02SJimmy Su 	return 0;
6777be91e02SJimmy Su }
6787be91e02SJimmy Su 
ov08d10_update_analog_gain(struct ov08d10 * ov08d10,u32 a_gain)6797be91e02SJimmy Su static int ov08d10_update_analog_gain(struct ov08d10 *ov08d10, u32 a_gain)
6807be91e02SJimmy Su {
6817be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
6827be91e02SJimmy Su 	u8 val;
6837be91e02SJimmy Su 	int ret;
6847be91e02SJimmy Su 
6857be91e02SJimmy Su 	val = ((a_gain >> 3) & 0xFF);
6867be91e02SJimmy Su 	/* CIS control registers */
6877be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
6887be91e02SJimmy Su 	if (ret < 0)
6897be91e02SJimmy Su 		return ret;
6907be91e02SJimmy Su 
6917be91e02SJimmy Su 	/* update AGAIN */
6927be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_ANALOG_GAIN, val);
6937be91e02SJimmy Su 	if (ret < 0)
6947be91e02SJimmy Su 		return ret;
6957be91e02SJimmy Su 
6967be91e02SJimmy Su 	return i2c_smbus_write_byte_data(client,
6977be91e02SJimmy Su 					 OV08D10_REG_GLOBAL_EFFECTIVE, 0x01);
6987be91e02SJimmy Su }
6997be91e02SJimmy Su 
ov08d10_update_digital_gain(struct ov08d10 * ov08d10,u32 d_gain)7007be91e02SJimmy Su static int ov08d10_update_digital_gain(struct ov08d10 *ov08d10, u32 d_gain)
7017be91e02SJimmy Su {
7027be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
7037be91e02SJimmy Su 	u8 val;
7047be91e02SJimmy Su 	int ret;
7057be91e02SJimmy Su 
7067be91e02SJimmy Su 	d_gain = (d_gain >> 1);
7077be91e02SJimmy Su 	/* CIS control registers */
7087be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
7097be91e02SJimmy Su 	if (ret < 0)
7107be91e02SJimmy Su 		return ret;
7117be91e02SJimmy Su 
7127be91e02SJimmy Su 	val = ((d_gain >> 8) & 0x3F);
7137be91e02SJimmy Su 	/* update DGAIN */
7147be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MWB_DGAIN_C, val);
7157be91e02SJimmy Su 	if (ret < 0)
7167be91e02SJimmy Su 		return ret;
7177be91e02SJimmy Su 
7187be91e02SJimmy Su 	val = d_gain & 0xFF;
7197be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MWB_DGAIN_F, val);
7207be91e02SJimmy Su 	if (ret < 0)
7217be91e02SJimmy Su 		return ret;
7227be91e02SJimmy Su 
7237be91e02SJimmy Su 	return i2c_smbus_write_byte_data(client,
7247be91e02SJimmy Su 					 OV08D10_REG_GLOBAL_EFFECTIVE, 0x01);
7257be91e02SJimmy Su }
7267be91e02SJimmy Su 
ov08d10_set_exposure(struct ov08d10 * ov08d10,u32 exposure)7277be91e02SJimmy Su static int ov08d10_set_exposure(struct ov08d10 *ov08d10, u32 exposure)
7287be91e02SJimmy Su {
7297be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
7307be91e02SJimmy Su 	u8 val;
7317be91e02SJimmy Su 	u8 hts_h, hts_l;
7327be91e02SJimmy Su 	u32 hts, cur_vts, exp_cal;
7337be91e02SJimmy Su 	int ret;
7347be91e02SJimmy Su 
7357be91e02SJimmy Su 	cur_vts = ov08d10->cur_mode->vts_def;
7367be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
7377be91e02SJimmy Su 	if (ret < 0)
7387be91e02SJimmy Su 		return ret;
7397be91e02SJimmy Su 
7407be91e02SJimmy Su 	hts_h = i2c_smbus_read_byte_data(client, 0x37);
7417be91e02SJimmy Su 	hts_l = i2c_smbus_read_byte_data(client, 0x38);
7427be91e02SJimmy Su 	hts = ((hts_h << 8) | (hts_l));
7437be91e02SJimmy Su 	exp_cal = 66 * OV08D10_ROWCLK / hts;
7447be91e02SJimmy Su 	exposure = exposure * exp_cal / (cur_vts - OV08D10_EXPOSURE_MAX_MARGIN);
7457be91e02SJimmy Su 	/* CIS control registers */
7467be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
7477be91e02SJimmy Su 	if (ret < 0)
7487be91e02SJimmy Su 		return ret;
7497be91e02SJimmy Su 
7507be91e02SJimmy Su 	/* update exposure */
7517be91e02SJimmy Su 	val = ((exposure >> 16) & 0xFF);
7527be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_EXPOSURE_H, val);
7537be91e02SJimmy Su 	if (ret < 0)
7547be91e02SJimmy Su 		return ret;
7557be91e02SJimmy Su 
7567be91e02SJimmy Su 	val = ((exposure >> 8) & 0xFF);
7577be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_EXPOSURE_M, val);
7587be91e02SJimmy Su 	if (ret < 0)
7597be91e02SJimmy Su 		return ret;
7607be91e02SJimmy Su 
7617be91e02SJimmy Su 	val = exposure & 0xFF;
7627be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_EXPOSURE_L, val);
7637be91e02SJimmy Su 	if (ret < 0)
7647be91e02SJimmy Su 		return ret;
7657be91e02SJimmy Su 
7667be91e02SJimmy Su 	return i2c_smbus_write_byte_data(client,
7677be91e02SJimmy Su 					 OV08D10_REG_GLOBAL_EFFECTIVE, 0x01);
7687be91e02SJimmy Su }
7697be91e02SJimmy Su 
ov08d10_set_vblank(struct ov08d10 * ov08d10,u32 vblank)7707be91e02SJimmy Su static int ov08d10_set_vblank(struct ov08d10 *ov08d10, u32 vblank)
7717be91e02SJimmy Su {
7727be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
7737be91e02SJimmy Su 	u8 val;
7747be91e02SJimmy Su 	int ret;
7757be91e02SJimmy Su 
7767be91e02SJimmy Su 	/* CIS control registers */
7777be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
7787be91e02SJimmy Su 	if (ret < 0)
7797be91e02SJimmy Su 		return ret;
7807be91e02SJimmy Su 
7817be91e02SJimmy Su 	val = ((vblank >> 8) & 0xFF);
7827be91e02SJimmy Su 	/* update vblank */
7837be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_VTS_H, val);
7847be91e02SJimmy Su 	if (ret < 0)
7857be91e02SJimmy Su 		return ret;
7867be91e02SJimmy Su 
7877be91e02SJimmy Su 	val = vblank & 0xFF;
7887be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_VTS_L, val);
7897be91e02SJimmy Su 	if (ret < 0)
7907be91e02SJimmy Su 		return ret;
7917be91e02SJimmy Su 
7927be91e02SJimmy Su 	return i2c_smbus_write_byte_data(client,
7937be91e02SJimmy Su 					 OV08D10_REG_GLOBAL_EFFECTIVE, 0x01);
7947be91e02SJimmy Su }
7957be91e02SJimmy Su 
ov08d10_test_pattern(struct ov08d10 * ov08d10,u32 pattern)7967be91e02SJimmy Su static int ov08d10_test_pattern(struct ov08d10 *ov08d10, u32 pattern)
7977be91e02SJimmy Su {
7987be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
7997be91e02SJimmy Su 	u8 val;
8007be91e02SJimmy Su 	int ret;
8017be91e02SJimmy Su 
8027be91e02SJimmy Su 	if (pattern)
8037be91e02SJimmy Su 		val = OV08D10_TEST_PATTERN_ENABLE;
8047be91e02SJimmy Su 	else
8057be91e02SJimmy Su 		val = OV08D10_TEST_PATTERN_DISABLE;
8067be91e02SJimmy Su 
8077be91e02SJimmy Su 	/* CIS control registers */
8087be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
8097be91e02SJimmy Su 	if (ret < 0)
8107be91e02SJimmy Su 		return ret;
8117be91e02SJimmy Su 
8127be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client,
8137be91e02SJimmy Su 					OV08D10_REG_TEST_PATTERN, val);
8147be91e02SJimmy Su 	if (ret < 0)
8157be91e02SJimmy Su 		return ret;
8167be91e02SJimmy Su 
8177be91e02SJimmy Su 	return i2c_smbus_write_byte_data(client,
8187be91e02SJimmy Su 					 OV08D10_REG_GLOBAL_EFFECTIVE, 0x01);
8197be91e02SJimmy Su }
8207be91e02SJimmy Su 
ov08d10_set_ctrl_flip(struct ov08d10 * ov08d10,u32 ctrl_val)8217be91e02SJimmy Su static int ov08d10_set_ctrl_flip(struct ov08d10 *ov08d10, u32 ctrl_val)
8227be91e02SJimmy Su {
8237be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
8247be91e02SJimmy Su 	u8 val;
8257be91e02SJimmy Su 	int ret;
8267be91e02SJimmy Su 
8277be91e02SJimmy Su 	/* System control registers */
8287be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
8297be91e02SJimmy Su 	if (ret < 0)
8307be91e02SJimmy Su 		return ret;
8317be91e02SJimmy Su 
8327be91e02SJimmy Su 	ret = i2c_smbus_read_byte_data(client, OV08D10_REG_FLIP_OPT);
8337be91e02SJimmy Su 	if (ret < 0)
8347be91e02SJimmy Su 		return ret;
8357be91e02SJimmy Su 
8367be91e02SJimmy Su 	val = ret | (ctrl_val & OV08D10_REG_FLIP_MASK);
8377be91e02SJimmy Su 
8387be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
8397be91e02SJimmy Su 	if (ret < 0)
8407be91e02SJimmy Su 		return ret;
8417be91e02SJimmy Su 
8427be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_FLIP_OPT, val);
8437be91e02SJimmy Su 
8447be91e02SJimmy Su 	if (ret < 0)
8457be91e02SJimmy Su 		return ret;
8467be91e02SJimmy Su 
8477be91e02SJimmy Su 	return i2c_smbus_write_byte_data(client,
8487be91e02SJimmy Su 					 OV08D10_REG_GLOBAL_EFFECTIVE, 0x01);
8497be91e02SJimmy Su }
8507be91e02SJimmy Su 
ov08d10_set_ctrl(struct v4l2_ctrl * ctrl)8517be91e02SJimmy Su static int ov08d10_set_ctrl(struct v4l2_ctrl *ctrl)
8527be91e02SJimmy Su {
8537be91e02SJimmy Su 	struct ov08d10 *ov08d10 = container_of(ctrl->handler,
8547be91e02SJimmy Su 					     struct ov08d10, ctrl_handler);
8557be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
8567be91e02SJimmy Su 	s64 exposure_max;
8577be91e02SJimmy Su 	int ret;
8587be91e02SJimmy Su 
8597be91e02SJimmy Su 	/* Propagate change of current control to all related controls */
8607be91e02SJimmy Su 	if (ctrl->id == V4L2_CID_VBLANK) {
8617be91e02SJimmy Su 		/* Update max exposure while meeting expected vblanking */
8627be91e02SJimmy Su 		exposure_max = ov08d10->cur_mode->height + ctrl->val -
8637be91e02SJimmy Su 			       OV08D10_EXPOSURE_MAX_MARGIN;
8647be91e02SJimmy Su 		__v4l2_ctrl_modify_range(ov08d10->exposure,
8657be91e02SJimmy Su 					 ov08d10->exposure->minimum,
8667be91e02SJimmy Su 					 exposure_max, ov08d10->exposure->step,
8677be91e02SJimmy Su 					 exposure_max);
8687be91e02SJimmy Su 	}
8697be91e02SJimmy Su 
8707be91e02SJimmy Su 	/* V4L2 controls values will be applied only when power is already up */
8717be91e02SJimmy Su 	if (!pm_runtime_get_if_in_use(&client->dev))
8727be91e02SJimmy Su 		return 0;
8737be91e02SJimmy Su 
8747be91e02SJimmy Su 	switch (ctrl->id) {
8757be91e02SJimmy Su 	case V4L2_CID_ANALOGUE_GAIN:
8767be91e02SJimmy Su 		ret = ov08d10_update_analog_gain(ov08d10, ctrl->val);
8777be91e02SJimmy Su 		break;
8787be91e02SJimmy Su 
8797be91e02SJimmy Su 	case V4L2_CID_DIGITAL_GAIN:
8807be91e02SJimmy Su 		ret = ov08d10_update_digital_gain(ov08d10, ctrl->val);
8817be91e02SJimmy Su 		break;
8827be91e02SJimmy Su 
8837be91e02SJimmy Su 	case V4L2_CID_EXPOSURE:
8847be91e02SJimmy Su 		ret = ov08d10_set_exposure(ov08d10, ctrl->val);
8857be91e02SJimmy Su 		break;
8867be91e02SJimmy Su 
8877be91e02SJimmy Su 	case V4L2_CID_VBLANK:
8887be91e02SJimmy Su 		ret = ov08d10_set_vblank(ov08d10, ctrl->val);
8897be91e02SJimmy Su 		break;
8907be91e02SJimmy Su 
8917be91e02SJimmy Su 	case V4L2_CID_TEST_PATTERN:
8927be91e02SJimmy Su 		ret = ov08d10_test_pattern(ov08d10, ctrl->val);
8937be91e02SJimmy Su 		break;
8947be91e02SJimmy Su 
8957be91e02SJimmy Su 	case V4L2_CID_HFLIP:
8967be91e02SJimmy Su 	case V4L2_CID_VFLIP:
8977be91e02SJimmy Su 		ret = ov08d10_set_ctrl_flip(ov08d10,
8987be91e02SJimmy Su 					    ov08d10->hflip->val |
8997be91e02SJimmy Su 					    ov08d10->vflip->val << 1);
9007be91e02SJimmy Su 		break;
9017be91e02SJimmy Su 
9027be91e02SJimmy Su 	default:
9037be91e02SJimmy Su 		ret = -EINVAL;
9047be91e02SJimmy Su 		break;
9057be91e02SJimmy Su 	}
9067be91e02SJimmy Su 
9077be91e02SJimmy Su 	pm_runtime_put(&client->dev);
9087be91e02SJimmy Su 
9097be91e02SJimmy Su 	return ret;
9107be91e02SJimmy Su }
9117be91e02SJimmy Su 
9127be91e02SJimmy Su static const struct v4l2_ctrl_ops ov08d10_ctrl_ops = {
9137be91e02SJimmy Su 	.s_ctrl = ov08d10_set_ctrl,
9147be91e02SJimmy Su };
9157be91e02SJimmy Su 
ov08d10_init_controls(struct ov08d10 * ov08d10)9167be91e02SJimmy Su static int ov08d10_init_controls(struct ov08d10 *ov08d10)
9177be91e02SJimmy Su {
9187be91e02SJimmy Su 	struct v4l2_ctrl_handler *ctrl_hdlr;
9197be91e02SJimmy Su 	u8 link_freq_size;
9207be91e02SJimmy Su 	s64 exposure_max;
9217be91e02SJimmy Su 	s64 vblank_def;
9227be91e02SJimmy Su 	s64 vblank_min;
9237be91e02SJimmy Su 	s64 h_blank;
9247be91e02SJimmy Su 	s64 pixel_rate_max;
9257be91e02SJimmy Su 	const struct ov08d10_mode *mode;
9267be91e02SJimmy Su 	int ret;
9277be91e02SJimmy Su 
9287be91e02SJimmy Su 	ctrl_hdlr = &ov08d10->ctrl_handler;
9297be91e02SJimmy Su 	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
9307be91e02SJimmy Su 	if (ret)
9317be91e02SJimmy Su 		return ret;
9327be91e02SJimmy Su 
9337be91e02SJimmy Su 	ctrl_hdlr->lock = &ov08d10->mutex;
9347be91e02SJimmy Su 	link_freq_size = ARRAY_SIZE(ov08d10->priv_lane->link_freq_menu);
9357be91e02SJimmy Su 	ov08d10->link_freq =
9367be91e02SJimmy Su 		v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov08d10_ctrl_ops,
9377be91e02SJimmy Su 				       V4L2_CID_LINK_FREQ,
9387be91e02SJimmy Su 				       link_freq_size - 1,
9397be91e02SJimmy Su 				       0,
9407be91e02SJimmy Su 				       ov08d10->priv_lane->link_freq_menu);
9417be91e02SJimmy Su 	if (ov08d10->link_freq)
9427be91e02SJimmy Su 		ov08d10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
9437be91e02SJimmy Su 
9447be91e02SJimmy Su 	pixel_rate_max = to_rate(ov08d10->priv_lane->link_freq_menu, 0,
9457be91e02SJimmy Su 				 ov08d10->cur_mode->data_lanes);
9467be91e02SJimmy Su 	ov08d10->pixel_rate =
9477be91e02SJimmy Su 		v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops,
9487be91e02SJimmy Su 				  V4L2_CID_PIXEL_RATE, 0, pixel_rate_max, 1,
9497be91e02SJimmy Su 				  pixel_rate_max);
9507be91e02SJimmy Su 
9517be91e02SJimmy Su 	mode = ov08d10->cur_mode;
9527be91e02SJimmy Su 	vblank_def = mode->vts_def - mode->height;
9537be91e02SJimmy Su 	vblank_min = mode->vts_min - mode->height;
9547be91e02SJimmy Su 	ov08d10->vblank =
9557be91e02SJimmy Su 		v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops,
9567be91e02SJimmy Su 				  V4L2_CID_VBLANK, vblank_min,
9577be91e02SJimmy Su 				  OV08D10_VTS_MAX - mode->height, 1,
9587be91e02SJimmy Su 				  vblank_def);
9597be91e02SJimmy Su 
9607be91e02SJimmy Su 	h_blank = to_pixels_per_line(ov08d10->priv_lane->link_freq_menu,
9617be91e02SJimmy Su 				     mode->hts, mode->link_freq_index,
9627be91e02SJimmy Su 				     mode->data_lanes) -
9637be91e02SJimmy Su 				     mode->width;
9647be91e02SJimmy Su 	ov08d10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops,
9657be91e02SJimmy Su 					    V4L2_CID_HBLANK, h_blank, h_blank,
9667be91e02SJimmy Su 					    1, h_blank);
9677be91e02SJimmy Su 	if (ov08d10->hblank)
9687be91e02SJimmy Su 		ov08d10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
9697be91e02SJimmy Su 
9707be91e02SJimmy Su 	v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
9717be91e02SJimmy Su 			  OV08D10_ANAL_GAIN_MIN, OV08D10_ANAL_GAIN_MAX,
9727be91e02SJimmy Su 			  OV08D10_ANAL_GAIN_STEP, OV08D10_ANAL_GAIN_MIN);
9737be91e02SJimmy Su 
9747be91e02SJimmy Su 	v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
9757be91e02SJimmy Su 			  OV08D10_DGTL_GAIN_MIN, OV08D10_DGTL_GAIN_MAX,
9767be91e02SJimmy Su 			  OV08D10_DGTL_GAIN_STEP, OV08D10_DGTL_GAIN_DEFAULT);
9777be91e02SJimmy Su 
9787be91e02SJimmy Su 	exposure_max = mode->vts_def - OV08D10_EXPOSURE_MAX_MARGIN;
9797be91e02SJimmy Su 	ov08d10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops,
9807be91e02SJimmy Su 					      V4L2_CID_EXPOSURE,
9817be91e02SJimmy Su 					      OV08D10_EXPOSURE_MIN,
9827be91e02SJimmy Su 					      exposure_max,
9837be91e02SJimmy Su 					      OV08D10_EXPOSURE_STEP,
9847be91e02SJimmy Su 					      exposure_max);
9857be91e02SJimmy Su 
9867be91e02SJimmy Su 	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov08d10_ctrl_ops,
9877be91e02SJimmy Su 				     V4L2_CID_TEST_PATTERN,
9887be91e02SJimmy Su 				     ARRAY_SIZE(ov08d10_test_pattern_menu) - 1,
9897be91e02SJimmy Su 				     0, 0, ov08d10_test_pattern_menu);
9907be91e02SJimmy Su 
9917be91e02SJimmy Su 	ov08d10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops,
9927be91e02SJimmy Su 					   V4L2_CID_HFLIP, 0, 1, 1, 0);
993e70fefdcSDave Stevenson 	if (ov08d10->hflip)
994e70fefdcSDave Stevenson 		ov08d10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
9957be91e02SJimmy Su 	ov08d10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov08d10_ctrl_ops,
9967be91e02SJimmy Su 					   V4L2_CID_VFLIP, 0, 1, 1, 0);
997e70fefdcSDave Stevenson 	if (ov08d10->vflip)
998e70fefdcSDave Stevenson 		ov08d10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
999e70fefdcSDave Stevenson 
10007be91e02SJimmy Su 	if (ctrl_hdlr->error)
10017be91e02SJimmy Su 		return ctrl_hdlr->error;
10027be91e02SJimmy Su 
10037be91e02SJimmy Su 	ov08d10->sd.ctrl_handler = ctrl_hdlr;
10047be91e02SJimmy Su 
10057be91e02SJimmy Su 	return 0;
10067be91e02SJimmy Su }
10077be91e02SJimmy Su 
ov08d10_update_pad_format(struct ov08d10 * ov08d10,const struct ov08d10_mode * mode,struct v4l2_mbus_framefmt * fmt)10087be91e02SJimmy Su static void ov08d10_update_pad_format(struct ov08d10 *ov08d10,
10097be91e02SJimmy Su 				      const struct ov08d10_mode *mode,
10107be91e02SJimmy Su 				      struct v4l2_mbus_framefmt *fmt)
10117be91e02SJimmy Su {
10127be91e02SJimmy Su 	fmt->width = mode->width;
10137be91e02SJimmy Su 	fmt->height = mode->height;
10147be91e02SJimmy Su 	fmt->code = ov08d10_get_format_code(ov08d10);
10157be91e02SJimmy Su 	fmt->field = V4L2_FIELD_NONE;
10167be91e02SJimmy Su }
10177be91e02SJimmy Su 
ov08d10_start_streaming(struct ov08d10 * ov08d10)10187be91e02SJimmy Su static int ov08d10_start_streaming(struct ov08d10 *ov08d10)
10197be91e02SJimmy Su {
10207be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
10217be91e02SJimmy Su 	const struct ov08d10_reg_list *reg_list;
10227be91e02SJimmy Su 	int link_freq_index, ret;
10237be91e02SJimmy Su 
10247be91e02SJimmy Su 	link_freq_index = ov08d10->cur_mode->link_freq_index;
10257be91e02SJimmy Su 	reg_list =
10267be91e02SJimmy Su 	    &ov08d10->priv_lane->link_freq_configs[link_freq_index].reg_list;
10277be91e02SJimmy Su 
10287be91e02SJimmy Su 	/* soft reset */
10297be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
10307be91e02SJimmy Su 	if (ret < 0) {
10317be91e02SJimmy Su 		dev_err(&client->dev, "failed to reset sensor");
10327be91e02SJimmy Su 		return ret;
10337be91e02SJimmy Su 	}
10347be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, 0x20, 0x0e);
10357be91e02SJimmy Su 	if (ret < 0) {
10367be91e02SJimmy Su 		dev_err(&client->dev, "failed to reset sensor");
10377be91e02SJimmy Su 		return ret;
10387be91e02SJimmy Su 	}
10397be91e02SJimmy Su 	usleep_range(3000, 4000);
10407be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, 0x20, 0x0b);
10417be91e02SJimmy Su 	if (ret < 0) {
10427be91e02SJimmy Su 		dev_err(&client->dev, "failed to reset sensor");
10437be91e02SJimmy Su 		return ret;
10447be91e02SJimmy Su 	}
10457be91e02SJimmy Su 
10467be91e02SJimmy Su 	/* update sensor setting */
10477be91e02SJimmy Su 	ret = ov08d10_write_reg_list(ov08d10, reg_list);
10487be91e02SJimmy Su 	if (ret) {
10497be91e02SJimmy Su 		dev_err(&client->dev, "failed to set plls");
10507be91e02SJimmy Su 		return ret;
10517be91e02SJimmy Su 	}
10527be91e02SJimmy Su 
10537be91e02SJimmy Su 	reg_list = &ov08d10->cur_mode->reg_list;
10547be91e02SJimmy Su 	ret = ov08d10_write_reg_list(ov08d10, reg_list);
10557be91e02SJimmy Su 	if (ret) {
10567be91e02SJimmy Su 		dev_err(&client->dev, "failed to set mode");
10577be91e02SJimmy Su 		return ret;
10587be91e02SJimmy Su 	}
10597be91e02SJimmy Su 
10607be91e02SJimmy Su 	ret = __v4l2_ctrl_handler_setup(ov08d10->sd.ctrl_handler);
10617be91e02SJimmy Su 	if (ret)
10627be91e02SJimmy Su 		return ret;
10637be91e02SJimmy Su 
10647be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
10657be91e02SJimmy Su 	if (ret < 0)
10667be91e02SJimmy Su 		return ret;
10677be91e02SJimmy Su 
10687be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT,
10697be91e02SJimmy Su 					OV08D10_MODE_STREAMING);
10707be91e02SJimmy Su 	if (ret < 0)
10717be91e02SJimmy Su 		return ret;
10727be91e02SJimmy Su 
10737be91e02SJimmy Su 	return i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
10747be91e02SJimmy Su }
10757be91e02SJimmy Su 
ov08d10_stop_streaming(struct ov08d10 * ov08d10)10767be91e02SJimmy Su static void ov08d10_stop_streaming(struct ov08d10 *ov08d10)
10777be91e02SJimmy Su {
10787be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
10797be91e02SJimmy Su 	int ret;
10807be91e02SJimmy Su 
10817be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
10827be91e02SJimmy Su 	if (ret < 0) {
10837be91e02SJimmy Su 		dev_err(&client->dev, "failed to stop streaming");
10847be91e02SJimmy Su 		return;
10857be91e02SJimmy Su 	}
10867be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_MODE_SELECT,
10877be91e02SJimmy Su 					OV08D10_MODE_STANDBY);
10887be91e02SJimmy Su 	if (ret < 0) {
10897be91e02SJimmy Su 		dev_err(&client->dev, "failed to stop streaming");
10907be91e02SJimmy Su 		return;
10917be91e02SJimmy Su 	}
10927be91e02SJimmy Su 
10937be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x01);
10947be91e02SJimmy Su 	if (ret < 0) {
10957be91e02SJimmy Su 		dev_err(&client->dev, "failed to stop streaming");
10967be91e02SJimmy Su 		return;
10977be91e02SJimmy Su 	}
10987be91e02SJimmy Su }
10997be91e02SJimmy Su 
ov08d10_set_stream(struct v4l2_subdev * sd,int enable)11007be91e02SJimmy Su static int ov08d10_set_stream(struct v4l2_subdev *sd, int enable)
11017be91e02SJimmy Su {
11027be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
11037be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(sd);
11047be91e02SJimmy Su 	int ret = 0;
11057be91e02SJimmy Su 
11067be91e02SJimmy Su 	if (ov08d10->streaming == enable)
11077be91e02SJimmy Su 		return 0;
11087be91e02SJimmy Su 
11097be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
11107be91e02SJimmy Su 	if (enable) {
11117be91e02SJimmy Su 		ret = pm_runtime_resume_and_get(&client->dev);
11127be91e02SJimmy Su 		if (ret < 0) {
11137be91e02SJimmy Su 			mutex_unlock(&ov08d10->mutex);
11147be91e02SJimmy Su 			return ret;
11157be91e02SJimmy Su 		}
11167be91e02SJimmy Su 
11177be91e02SJimmy Su 		ret = ov08d10_start_streaming(ov08d10);
11187be91e02SJimmy Su 		if (ret) {
11197be91e02SJimmy Su 			enable = 0;
11207be91e02SJimmy Su 			ov08d10_stop_streaming(ov08d10);
11217be91e02SJimmy Su 			pm_runtime_put(&client->dev);
11227be91e02SJimmy Su 		}
11237be91e02SJimmy Su 	} else {
11247be91e02SJimmy Su 		ov08d10_stop_streaming(ov08d10);
11257be91e02SJimmy Su 		pm_runtime_put(&client->dev);
11267be91e02SJimmy Su 	}
11277be91e02SJimmy Su 
11287be91e02SJimmy Su 	ov08d10->streaming = enable;
11297be91e02SJimmy Su 
11307be91e02SJimmy Su 	/* vflip and hflip cannot change during streaming */
11317be91e02SJimmy Su 	__v4l2_ctrl_grab(ov08d10->vflip, enable);
11327be91e02SJimmy Su 	__v4l2_ctrl_grab(ov08d10->hflip, enable);
11337be91e02SJimmy Su 
11347be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
11357be91e02SJimmy Su 
11367be91e02SJimmy Su 	return ret;
11377be91e02SJimmy Su }
11387be91e02SJimmy Su 
ov08d10_suspend(struct device * dev)11397be91e02SJimmy Su static int __maybe_unused ov08d10_suspend(struct device *dev)
11407be91e02SJimmy Su {
11417be91e02SJimmy Su 	struct i2c_client *client = to_i2c_client(dev);
11427be91e02SJimmy Su 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
11437be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
11447be91e02SJimmy Su 
11457be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
11467be91e02SJimmy Su 	if (ov08d10->streaming)
11477be91e02SJimmy Su 		ov08d10_stop_streaming(ov08d10);
11487be91e02SJimmy Su 
11497be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
11507be91e02SJimmy Su 
11517be91e02SJimmy Su 	return 0;
11527be91e02SJimmy Su }
11537be91e02SJimmy Su 
ov08d10_resume(struct device * dev)11547be91e02SJimmy Su static int __maybe_unused ov08d10_resume(struct device *dev)
11557be91e02SJimmy Su {
11567be91e02SJimmy Su 	struct i2c_client *client = to_i2c_client(dev);
11577be91e02SJimmy Su 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
11587be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
11597be91e02SJimmy Su 	int ret;
11607be91e02SJimmy Su 
11617be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
11627be91e02SJimmy Su 
11637be91e02SJimmy Su 	if (ov08d10->streaming) {
11647be91e02SJimmy Su 		ret = ov08d10_start_streaming(ov08d10);
11657be91e02SJimmy Su 		if (ret) {
11667be91e02SJimmy Su 			ov08d10->streaming = false;
11677be91e02SJimmy Su 			ov08d10_stop_streaming(ov08d10);
11687be91e02SJimmy Su 			mutex_unlock(&ov08d10->mutex);
11697be91e02SJimmy Su 			return ret;
11707be91e02SJimmy Su 		}
11717be91e02SJimmy Su 	}
11727be91e02SJimmy Su 
11737be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
11747be91e02SJimmy Su 
11757be91e02SJimmy Su 	return 0;
11767be91e02SJimmy Su }
11777be91e02SJimmy Su 
ov08d10_set_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)11787be91e02SJimmy Su static int ov08d10_set_format(struct v4l2_subdev *sd,
11797be91e02SJimmy Su 			      struct v4l2_subdev_state *sd_state,
11807be91e02SJimmy Su 			      struct v4l2_subdev_format *fmt)
11817be91e02SJimmy Su {
11827be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
11837be91e02SJimmy Su 	const struct ov08d10_mode *mode;
11847be91e02SJimmy Su 	s32 vblank_def, h_blank;
11857be91e02SJimmy Su 	s64 pixel_rate;
11867be91e02SJimmy Su 
11877be91e02SJimmy Su 	mode = v4l2_find_nearest_size(ov08d10->priv_lane->sp_modes,
11887be91e02SJimmy Su 				      ov08d10->modes_size,
11897be91e02SJimmy Su 				      width, height, fmt->format.width,
11907be91e02SJimmy Su 				      fmt->format.height);
11917be91e02SJimmy Su 
11927be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
11937be91e02SJimmy Su 	ov08d10_update_pad_format(ov08d10, mode, &fmt->format);
11947be91e02SJimmy Su 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
11957be91e02SJimmy Su 		*v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) =
11967be91e02SJimmy Su 								fmt->format;
11977be91e02SJimmy Su 	} else {
11987be91e02SJimmy Su 		ov08d10->cur_mode = mode;
11997be91e02SJimmy Su 		__v4l2_ctrl_s_ctrl(ov08d10->link_freq, mode->link_freq_index);
12007be91e02SJimmy Su 		pixel_rate = to_rate(ov08d10->priv_lane->link_freq_menu,
12017be91e02SJimmy Su 				     mode->link_freq_index,
12027be91e02SJimmy Su 				     ov08d10->cur_mode->data_lanes);
12037be91e02SJimmy Su 		__v4l2_ctrl_s_ctrl_int64(ov08d10->pixel_rate, pixel_rate);
12047be91e02SJimmy Su 
12057be91e02SJimmy Su 		/* Update limits and set FPS to default */
12067be91e02SJimmy Su 		vblank_def = mode->vts_def - mode->height;
12077be91e02SJimmy Su 		__v4l2_ctrl_modify_range(ov08d10->vblank,
12087be91e02SJimmy Su 					 mode->vts_min - mode->height,
12097be91e02SJimmy Su 					 OV08D10_VTS_MAX - mode->height, 1,
12107be91e02SJimmy Su 					 vblank_def);
12117be91e02SJimmy Su 		__v4l2_ctrl_s_ctrl(ov08d10->vblank, vblank_def);
12127be91e02SJimmy Su 		h_blank = to_pixels_per_line(ov08d10->priv_lane->link_freq_menu,
12137be91e02SJimmy Su 					     mode->hts,
12147be91e02SJimmy Su 					     mode->link_freq_index,
12157be91e02SJimmy Su 					     ov08d10->cur_mode->data_lanes)
12167be91e02SJimmy Su 					     - mode->width;
12177be91e02SJimmy Su 		__v4l2_ctrl_modify_range(ov08d10->hblank, h_blank, h_blank, 1,
12187be91e02SJimmy Su 					 h_blank);
12197be91e02SJimmy Su 	}
12207be91e02SJimmy Su 
12217be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
12227be91e02SJimmy Su 
12237be91e02SJimmy Su 	return 0;
12247be91e02SJimmy Su }
12257be91e02SJimmy Su 
ov08d10_get_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)12267be91e02SJimmy Su static int ov08d10_get_format(struct v4l2_subdev *sd,
12277be91e02SJimmy Su 			      struct v4l2_subdev_state *sd_state,
12287be91e02SJimmy Su 			      struct v4l2_subdev_format *fmt)
12297be91e02SJimmy Su {
12307be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
12317be91e02SJimmy Su 
12327be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
12337be91e02SJimmy Su 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
12347be91e02SJimmy Su 		fmt->format = *v4l2_subdev_get_try_format(&ov08d10->sd,
12357be91e02SJimmy Su 							  sd_state,
12367be91e02SJimmy Su 							  fmt->pad);
12377be91e02SJimmy Su 	else
12387be91e02SJimmy Su 		ov08d10_update_pad_format(ov08d10, ov08d10->cur_mode,
12397be91e02SJimmy Su 					  &fmt->format);
12407be91e02SJimmy Su 
12417be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
12427be91e02SJimmy Su 
12437be91e02SJimmy Su 	return 0;
12447be91e02SJimmy Su }
12457be91e02SJimmy Su 
ov08d10_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)12467be91e02SJimmy Su static int ov08d10_enum_mbus_code(struct v4l2_subdev *sd,
12477be91e02SJimmy Su 				  struct v4l2_subdev_state *sd_state,
12487be91e02SJimmy Su 				  struct v4l2_subdev_mbus_code_enum *code)
12497be91e02SJimmy Su {
12507be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
12517be91e02SJimmy Su 
12527be91e02SJimmy Su 	if (code->index > 0)
12537be91e02SJimmy Su 		return -EINVAL;
12547be91e02SJimmy Su 
12557be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
12567be91e02SJimmy Su 	code->code = ov08d10_get_format_code(ov08d10);
12577be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
12587be91e02SJimmy Su 
12597be91e02SJimmy Su 	return 0;
12607be91e02SJimmy Su }
12617be91e02SJimmy Su 
ov08d10_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)12627be91e02SJimmy Su static int ov08d10_enum_frame_size(struct v4l2_subdev *sd,
12637be91e02SJimmy Su 				   struct v4l2_subdev_state *sd_state,
12647be91e02SJimmy Su 				   struct v4l2_subdev_frame_size_enum *fse)
12657be91e02SJimmy Su {
12667be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
12677be91e02SJimmy Su 
12687be91e02SJimmy Su 	if (fse->index >= ov08d10->modes_size)
12697be91e02SJimmy Su 		return -EINVAL;
12707be91e02SJimmy Su 
12717be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
1272cc74074aSDan Carpenter 	if (fse->code != ov08d10_get_format_code(ov08d10)) {
1273cc74074aSDan Carpenter 		mutex_unlock(&ov08d10->mutex);
12747be91e02SJimmy Su 		return -EINVAL;
1275cc74074aSDan Carpenter 	}
12767be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
12777be91e02SJimmy Su 
12787be91e02SJimmy Su 	fse->min_width = ov08d10->priv_lane->sp_modes[fse->index].width;
12797be91e02SJimmy Su 	fse->max_width = fse->min_width;
12807be91e02SJimmy Su 	fse->min_height = ov08d10->priv_lane->sp_modes[fse->index].height;
12817be91e02SJimmy Su 	fse->max_height = fse->min_height;
12827be91e02SJimmy Su 
12837be91e02SJimmy Su 	return 0;
12847be91e02SJimmy Su }
12857be91e02SJimmy Su 
ov08d10_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)12867be91e02SJimmy Su static int ov08d10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
12877be91e02SJimmy Su {
12887be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
12897be91e02SJimmy Su 
12907be91e02SJimmy Su 	mutex_lock(&ov08d10->mutex);
12917be91e02SJimmy Su 	ov08d10_update_pad_format(ov08d10, &ov08d10->priv_lane->sp_modes[0],
12927be91e02SJimmy Su 				  v4l2_subdev_get_try_format(sd, fh->state, 0));
12937be91e02SJimmy Su 	mutex_unlock(&ov08d10->mutex);
12947be91e02SJimmy Su 
12957be91e02SJimmy Su 	return 0;
12967be91e02SJimmy Su }
12977be91e02SJimmy Su 
12987be91e02SJimmy Su static const struct v4l2_subdev_video_ops ov08d10_video_ops = {
12997be91e02SJimmy Su 	.s_stream = ov08d10_set_stream,
13007be91e02SJimmy Su };
13017be91e02SJimmy Su 
13027be91e02SJimmy Su static const struct v4l2_subdev_pad_ops ov08d10_pad_ops = {
13037be91e02SJimmy Su 	.set_fmt = ov08d10_set_format,
13047be91e02SJimmy Su 	.get_fmt = ov08d10_get_format,
13057be91e02SJimmy Su 	.enum_mbus_code = ov08d10_enum_mbus_code,
13067be91e02SJimmy Su 	.enum_frame_size = ov08d10_enum_frame_size,
13077be91e02SJimmy Su };
13087be91e02SJimmy Su 
13097be91e02SJimmy Su static const struct v4l2_subdev_ops ov08d10_subdev_ops = {
13107be91e02SJimmy Su 	.video = &ov08d10_video_ops,
13117be91e02SJimmy Su 	.pad = &ov08d10_pad_ops,
13127be91e02SJimmy Su };
13137be91e02SJimmy Su 
13147be91e02SJimmy Su static const struct v4l2_subdev_internal_ops ov08d10_internal_ops = {
13157be91e02SJimmy Su 	.open = ov08d10_open,
13167be91e02SJimmy Su };
13177be91e02SJimmy Su 
ov08d10_identify_module(struct ov08d10 * ov08d10)13187be91e02SJimmy Su static int ov08d10_identify_module(struct ov08d10 *ov08d10)
13197be91e02SJimmy Su {
13207be91e02SJimmy Su 	struct i2c_client *client = v4l2_get_subdevdata(&ov08d10->sd);
13217be91e02SJimmy Su 	u32 val;
13227be91e02SJimmy Su 	u16 chip_id;
13237be91e02SJimmy Su 	int ret;
13247be91e02SJimmy Su 
13257be91e02SJimmy Su 	/* System control registers */
13267be91e02SJimmy Su 	ret = i2c_smbus_write_byte_data(client, OV08D10_REG_PAGE, 0x00);
13277be91e02SJimmy Su 	if (ret < 0)
13287be91e02SJimmy Su 		return ret;
13297be91e02SJimmy Su 
13307be91e02SJimmy Su 	/* Validate the chip ID */
13317be91e02SJimmy Su 	ret = i2c_smbus_read_byte_data(client, OV08D10_REG_CHIP_ID_0);
13327be91e02SJimmy Su 	if (ret < 0)
13337be91e02SJimmy Su 		return ret;
13347be91e02SJimmy Su 
13357be91e02SJimmy Su 	val = ret << 8;
13367be91e02SJimmy Su 
13377be91e02SJimmy Su 	ret = i2c_smbus_read_byte_data(client, OV08D10_REG_CHIP_ID_1);
13387be91e02SJimmy Su 	if (ret < 0)
13397be91e02SJimmy Su 		return ret;
13407be91e02SJimmy Su 
13417be91e02SJimmy Su 	chip_id = val | ret;
13427be91e02SJimmy Su 
13437be91e02SJimmy Su 	if ((chip_id & OV08D10_ID_MASK) != OV08D10_CHIP_ID) {
13447be91e02SJimmy Su 		dev_err(&client->dev, "unexpected sensor id(0x%04x)\n",
13457be91e02SJimmy Su 			chip_id);
13467be91e02SJimmy Su 		return -EINVAL;
13477be91e02SJimmy Su 	}
13487be91e02SJimmy Su 
13497be91e02SJimmy Su 	return 0;
13507be91e02SJimmy Su }
13517be91e02SJimmy Su 
ov08d10_get_hwcfg(struct ov08d10 * ov08d10,struct device * dev)13527be91e02SJimmy Su static int ov08d10_get_hwcfg(struct ov08d10 *ov08d10, struct device *dev)
13537be91e02SJimmy Su {
13547be91e02SJimmy Su 	struct fwnode_handle *ep;
13557be91e02SJimmy Su 	struct fwnode_handle *fwnode = dev_fwnode(dev);
13567be91e02SJimmy Su 	struct v4l2_fwnode_endpoint bus_cfg = {
13577be91e02SJimmy Su 		.bus_type = V4L2_MBUS_CSI2_DPHY
13587be91e02SJimmy Su 	};
13597be91e02SJimmy Su 	u32 xvclk_rate;
13607be91e02SJimmy Su 	unsigned int i, j;
13617be91e02SJimmy Su 	int ret;
13627be91e02SJimmy Su 
13637be91e02SJimmy Su 	if (!fwnode)
13647be91e02SJimmy Su 		return -ENXIO;
13657be91e02SJimmy Su 
13667be91e02SJimmy Su 	ret = fwnode_property_read_u32(fwnode, "clock-frequency", &xvclk_rate);
13677be91e02SJimmy Su 	if (ret)
13687be91e02SJimmy Su 		return ret;
13697be91e02SJimmy Su 
13707be91e02SJimmy Su 	if (xvclk_rate != OV08D10_XVCLK_19_2)
13717be91e02SJimmy Su 		dev_warn(dev, "external clock rate %u is unsupported",
13727be91e02SJimmy Su 			 xvclk_rate);
13737be91e02SJimmy Su 
13747be91e02SJimmy Su 	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
13757be91e02SJimmy Su 	if (!ep)
13767be91e02SJimmy Su 		return -ENXIO;
13777be91e02SJimmy Su 
13787be91e02SJimmy Su 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
13797be91e02SJimmy Su 	fwnode_handle_put(ep);
13807be91e02SJimmy Su 	if (ret)
13817be91e02SJimmy Su 		return ret;
13827be91e02SJimmy Su 
13837be91e02SJimmy Su 	/* Get number of data lanes */
13847be91e02SJimmy Su 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
13857be91e02SJimmy Su 		dev_err(dev, "number of CSI2 data lanes %d is not supported",
13867be91e02SJimmy Su 			bus_cfg.bus.mipi_csi2.num_data_lanes);
13877be91e02SJimmy Su 		ret = -EINVAL;
13887be91e02SJimmy Su 		goto check_hwcfg_error;
13897be91e02SJimmy Su 	}
13907be91e02SJimmy Su 
13917be91e02SJimmy Su 	dev_dbg(dev, "Using %u data lanes\n", ov08d10->cur_mode->data_lanes);
13927be91e02SJimmy Su 
13937be91e02SJimmy Su 	ov08d10->priv_lane = &lane_cfg_2;
13947be91e02SJimmy Su 	ov08d10->modes_size = ov08d10_modes_num(ov08d10);
13957be91e02SJimmy Su 
13967be91e02SJimmy Su 	if (!bus_cfg.nr_of_link_frequencies) {
13977be91e02SJimmy Su 		dev_err(dev, "no link frequencies defined");
13987be91e02SJimmy Su 		ret = -EINVAL;
13997be91e02SJimmy Su 		goto check_hwcfg_error;
14007be91e02SJimmy Su 	}
14017be91e02SJimmy Su 
14027be91e02SJimmy Su 	for (i = 0; i < ARRAY_SIZE(ov08d10->priv_lane->link_freq_menu); i++) {
14037be91e02SJimmy Su 		for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
14047be91e02SJimmy Su 			if (ov08d10->priv_lane->link_freq_menu[i] ==
14057be91e02SJimmy Su 			    bus_cfg.link_frequencies[j])
14067be91e02SJimmy Su 				break;
14077be91e02SJimmy Su 		}
14087be91e02SJimmy Su 
14097be91e02SJimmy Su 		if (j == bus_cfg.nr_of_link_frequencies) {
14107be91e02SJimmy Su 			dev_err(dev, "no link frequency %lld supported",
14117be91e02SJimmy Su 				ov08d10->priv_lane->link_freq_menu[i]);
14127be91e02SJimmy Su 			ret = -EINVAL;
14137be91e02SJimmy Su 			goto check_hwcfg_error;
14147be91e02SJimmy Su 		}
14157be91e02SJimmy Su 	}
14167be91e02SJimmy Su 
14177be91e02SJimmy Su check_hwcfg_error:
14187be91e02SJimmy Su 	v4l2_fwnode_endpoint_free(&bus_cfg);
14197be91e02SJimmy Su 
14207be91e02SJimmy Su 	return ret;
14217be91e02SJimmy Su }
14227be91e02SJimmy Su 
ov08d10_remove(struct i2c_client * client)1423ed5c2f5fSUwe Kleine-König static void ov08d10_remove(struct i2c_client *client)
14247be91e02SJimmy Su {
14257be91e02SJimmy Su 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
14267be91e02SJimmy Su 	struct ov08d10 *ov08d10 = to_ov08d10(sd);
14277be91e02SJimmy Su 
14287be91e02SJimmy Su 	v4l2_async_unregister_subdev(sd);
14297be91e02SJimmy Su 	media_entity_cleanup(&sd->entity);
14307be91e02SJimmy Su 	v4l2_ctrl_handler_free(sd->ctrl_handler);
14317be91e02SJimmy Su 	pm_runtime_disable(&client->dev);
14327be91e02SJimmy Su 	mutex_destroy(&ov08d10->mutex);
14337be91e02SJimmy Su }
14347be91e02SJimmy Su 
ov08d10_probe(struct i2c_client * client)14357be91e02SJimmy Su static int ov08d10_probe(struct i2c_client *client)
14367be91e02SJimmy Su {
14377be91e02SJimmy Su 	struct ov08d10 *ov08d10;
14387be91e02SJimmy Su 	int ret;
14397be91e02SJimmy Su 
14407be91e02SJimmy Su 	ov08d10 = devm_kzalloc(&client->dev, sizeof(*ov08d10), GFP_KERNEL);
14417be91e02SJimmy Su 	if (!ov08d10)
14427be91e02SJimmy Su 		return -ENOMEM;
14437be91e02SJimmy Su 
14447be91e02SJimmy Su 	ret = ov08d10_get_hwcfg(ov08d10, &client->dev);
14457be91e02SJimmy Su 	if (ret) {
14467be91e02SJimmy Su 		dev_err(&client->dev, "failed to get HW configuration: %d",
14477be91e02SJimmy Su 			ret);
14487be91e02SJimmy Su 		return ret;
14497be91e02SJimmy Su 	}
14507be91e02SJimmy Su 
14517be91e02SJimmy Su 	v4l2_i2c_subdev_init(&ov08d10->sd, client, &ov08d10_subdev_ops);
14527be91e02SJimmy Su 
14537be91e02SJimmy Su 	ret = ov08d10_identify_module(ov08d10);
14547be91e02SJimmy Su 	if (ret) {
14557be91e02SJimmy Su 		dev_err(&client->dev, "failed to find sensor: %d", ret);
14567be91e02SJimmy Su 		return ret;
14577be91e02SJimmy Su 	}
14587be91e02SJimmy Su 
14597be91e02SJimmy Su 	mutex_init(&ov08d10->mutex);
14607be91e02SJimmy Su 	ov08d10->cur_mode = &ov08d10->priv_lane->sp_modes[0];
14617be91e02SJimmy Su 	ret = ov08d10_init_controls(ov08d10);
14627be91e02SJimmy Su 	if (ret) {
14637be91e02SJimmy Su 		dev_err(&client->dev, "failed to init controls: %d", ret);
14647be91e02SJimmy Su 		goto probe_error_v4l2_ctrl_handler_free;
14657be91e02SJimmy Su 	}
14667be91e02SJimmy Su 
14677be91e02SJimmy Su 	ov08d10->sd.internal_ops = &ov08d10_internal_ops;
14687be91e02SJimmy Su 	ov08d10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
14697be91e02SJimmy Su 	ov08d10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
14707be91e02SJimmy Su 	ov08d10->pad.flags = MEDIA_PAD_FL_SOURCE;
14717be91e02SJimmy Su 	ret = media_entity_pads_init(&ov08d10->sd.entity, 1, &ov08d10->pad);
14727be91e02SJimmy Su 	if (ret) {
14737be91e02SJimmy Su 		dev_err(&client->dev, "failed to init entity pads: %d", ret);
14747be91e02SJimmy Su 		goto probe_error_v4l2_ctrl_handler_free;
14757be91e02SJimmy Su 	}
14767be91e02SJimmy Su 
14777be91e02SJimmy Su 	ret = v4l2_async_register_subdev_sensor(&ov08d10->sd);
14787be91e02SJimmy Su 	if (ret < 0) {
14797be91e02SJimmy Su 		dev_err(&client->dev, "failed to register V4L2 subdev: %d",
14807be91e02SJimmy Su 			ret);
14817be91e02SJimmy Su 		goto probe_error_media_entity_cleanup;
14827be91e02SJimmy Su 	}
14837be91e02SJimmy Su 
14847be91e02SJimmy Su 	/*
14857be91e02SJimmy Su 	 * Device is already turned on by i2c-core with ACPI domain PM.
14867be91e02SJimmy Su 	 * Enable runtime PM and turn off the device.
14877be91e02SJimmy Su 	 */
14887be91e02SJimmy Su 	pm_runtime_set_active(&client->dev);
14897be91e02SJimmy Su 	pm_runtime_enable(&client->dev);
14907be91e02SJimmy Su 	pm_runtime_idle(&client->dev);
14917be91e02SJimmy Su 
14927be91e02SJimmy Su 	return 0;
14937be91e02SJimmy Su 
14947be91e02SJimmy Su probe_error_media_entity_cleanup:
14957be91e02SJimmy Su 	media_entity_cleanup(&ov08d10->sd.entity);
14967be91e02SJimmy Su 
14977be91e02SJimmy Su probe_error_v4l2_ctrl_handler_free:
14987be91e02SJimmy Su 	v4l2_ctrl_handler_free(ov08d10->sd.ctrl_handler);
14997be91e02SJimmy Su 	mutex_destroy(&ov08d10->mutex);
15007be91e02SJimmy Su 
15017be91e02SJimmy Su 	return ret;
15027be91e02SJimmy Su }
15037be91e02SJimmy Su 
15047be91e02SJimmy Su static const struct dev_pm_ops ov08d10_pm_ops = {
15057be91e02SJimmy Su 	SET_SYSTEM_SLEEP_PM_OPS(ov08d10_suspend, ov08d10_resume)
15067be91e02SJimmy Su };
15077be91e02SJimmy Su 
15087be91e02SJimmy Su #ifdef CONFIG_ACPI
15097be91e02SJimmy Su static const struct acpi_device_id ov08d10_acpi_ids[] = {
15107be91e02SJimmy Su 	{ "OVTI08D1" },
15117be91e02SJimmy Su 	{ /* sentinel */ }
15127be91e02SJimmy Su };
15137be91e02SJimmy Su 
15147be91e02SJimmy Su MODULE_DEVICE_TABLE(acpi, ov08d10_acpi_ids);
15157be91e02SJimmy Su #endif
15167be91e02SJimmy Su 
15177be91e02SJimmy Su static struct i2c_driver ov08d10_i2c_driver = {
15187be91e02SJimmy Su 	.driver = {
15197be91e02SJimmy Su 		.name = "ov08d10",
15207be91e02SJimmy Su 		.pm = &ov08d10_pm_ops,
15217be91e02SJimmy Su 		.acpi_match_table = ACPI_PTR(ov08d10_acpi_ids),
15227be91e02SJimmy Su 	},
1523*aaeb31c0SUwe Kleine-König 	.probe = ov08d10_probe,
15247be91e02SJimmy Su 	.remove = ov08d10_remove,
15257be91e02SJimmy Su };
15267be91e02SJimmy Su 
15277be91e02SJimmy Su module_i2c_driver(ov08d10_i2c_driver);
15287be91e02SJimmy Su 
15297be91e02SJimmy Su MODULE_AUTHOR("Su, Jimmy <jimmy.su@intel.com>");
15307be91e02SJimmy Su MODULE_DESCRIPTION("OmniVision ov08d10 sensor driver");
15317be91e02SJimmy Su MODULE_LICENSE("GPL v2");
1532