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