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