1828dbc29SManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0 2828dbc29SManivannan Sadhasivam /* 3828dbc29SManivannan Sadhasivam * Sony IMX290 CMOS Image Sensor Driver 4828dbc29SManivannan Sadhasivam * 5828dbc29SManivannan Sadhasivam * Copyright (C) 2019 FRAMOS GmbH. 6828dbc29SManivannan Sadhasivam * 7828dbc29SManivannan Sadhasivam * Copyright (C) 2019 Linaro Ltd. 8828dbc29SManivannan Sadhasivam * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 9828dbc29SManivannan Sadhasivam */ 10828dbc29SManivannan Sadhasivam 11828dbc29SManivannan Sadhasivam #include <linux/clk.h> 12828dbc29SManivannan Sadhasivam #include <linux/delay.h> 13828dbc29SManivannan Sadhasivam #include <linux/gpio/consumer.h> 14828dbc29SManivannan Sadhasivam #include <linux/i2c.h> 15828dbc29SManivannan Sadhasivam #include <linux/module.h> 16828dbc29SManivannan Sadhasivam #include <linux/pm_runtime.h> 17828dbc29SManivannan Sadhasivam #include <linux/regmap.h> 18828dbc29SManivannan Sadhasivam #include <linux/regulator/consumer.h> 19828dbc29SManivannan Sadhasivam #include <media/media-entity.h> 20828dbc29SManivannan Sadhasivam #include <media/v4l2-ctrls.h> 21828dbc29SManivannan Sadhasivam #include <media/v4l2-device.h> 22828dbc29SManivannan Sadhasivam #include <media/v4l2-fwnode.h> 23828dbc29SManivannan Sadhasivam #include <media/v4l2-subdev.h> 24828dbc29SManivannan Sadhasivam 25828dbc29SManivannan Sadhasivam #define IMX290_STANDBY 0x3000 26828dbc29SManivannan Sadhasivam #define IMX290_REGHOLD 0x3001 27828dbc29SManivannan Sadhasivam #define IMX290_XMSTA 0x3002 2897589ad6SManivannan Sadhasivam #define IMX290_FR_FDG_SEL 0x3009 29a58df1f9SManivannan Sadhasivam #define IMX290_BLKLEVEL_LOW 0x300a 30a58df1f9SManivannan Sadhasivam #define IMX290_BLKLEVEL_HIGH 0x300b 31828dbc29SManivannan Sadhasivam #define IMX290_GAIN 0x3014 3297589ad6SManivannan Sadhasivam #define IMX290_HMAX_LOW 0x301c 3397589ad6SManivannan Sadhasivam #define IMX290_HMAX_HIGH 0x301d 34a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL 0x308c 3597589ad6SManivannan Sadhasivam #define IMX290_PHY_LANE_NUM 0x3407 3697589ad6SManivannan Sadhasivam #define IMX290_CSI_LANE_MODE 0x3443 37828dbc29SManivannan Sadhasivam 38a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_REGEN BIT(0) 39a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_THRU BIT(1) 40a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_MODE(n) ((n) << 4) 41a58df1f9SManivannan Sadhasivam 42828dbc29SManivannan Sadhasivam static const char * const imx290_supply_name[] = { 43828dbc29SManivannan Sadhasivam "vdda", 44828dbc29SManivannan Sadhasivam "vddd", 45828dbc29SManivannan Sadhasivam "vdddo", 46828dbc29SManivannan Sadhasivam }; 47828dbc29SManivannan Sadhasivam 48828dbc29SManivannan Sadhasivam #define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name) 49828dbc29SManivannan Sadhasivam 50828dbc29SManivannan Sadhasivam struct imx290_regval { 51828dbc29SManivannan Sadhasivam u16 reg; 52828dbc29SManivannan Sadhasivam u8 val; 53828dbc29SManivannan Sadhasivam }; 54828dbc29SManivannan Sadhasivam 55828dbc29SManivannan Sadhasivam struct imx290_mode { 56828dbc29SManivannan Sadhasivam u32 width; 57828dbc29SManivannan Sadhasivam u32 height; 5897589ad6SManivannan Sadhasivam u32 hmax; 5998e0500eSManivannan Sadhasivam u8 link_freq_index; 60828dbc29SManivannan Sadhasivam 61828dbc29SManivannan Sadhasivam const struct imx290_regval *data; 62828dbc29SManivannan Sadhasivam u32 data_size; 63828dbc29SManivannan Sadhasivam }; 64828dbc29SManivannan Sadhasivam 65828dbc29SManivannan Sadhasivam struct imx290 { 66828dbc29SManivannan Sadhasivam struct device *dev; 67828dbc29SManivannan Sadhasivam struct clk *xclk; 68828dbc29SManivannan Sadhasivam struct regmap *regmap; 6997589ad6SManivannan Sadhasivam u8 nlanes; 70c566ac01SManivannan Sadhasivam u8 bpp; 71828dbc29SManivannan Sadhasivam 72828dbc29SManivannan Sadhasivam struct v4l2_subdev sd; 73828dbc29SManivannan Sadhasivam struct v4l2_fwnode_endpoint ep; 74828dbc29SManivannan Sadhasivam struct media_pad pad; 75828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt current_format; 76828dbc29SManivannan Sadhasivam const struct imx290_mode *current_mode; 77828dbc29SManivannan Sadhasivam 78828dbc29SManivannan Sadhasivam struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES]; 79828dbc29SManivannan Sadhasivam struct gpio_desc *rst_gpio; 80828dbc29SManivannan Sadhasivam 81828dbc29SManivannan Sadhasivam struct v4l2_ctrl_handler ctrls; 82828dbc29SManivannan Sadhasivam struct v4l2_ctrl *link_freq; 83828dbc29SManivannan Sadhasivam struct v4l2_ctrl *pixel_rate; 84828dbc29SManivannan Sadhasivam 85828dbc29SManivannan Sadhasivam struct mutex lock; 86828dbc29SManivannan Sadhasivam }; 87828dbc29SManivannan Sadhasivam 88828dbc29SManivannan Sadhasivam struct imx290_pixfmt { 89828dbc29SManivannan Sadhasivam u32 code; 90c566ac01SManivannan Sadhasivam u8 bpp; 91828dbc29SManivannan Sadhasivam }; 92828dbc29SManivannan Sadhasivam 93828dbc29SManivannan Sadhasivam static const struct imx290_pixfmt imx290_formats[] = { 94c566ac01SManivannan Sadhasivam { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 95c566ac01SManivannan Sadhasivam { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 96828dbc29SManivannan Sadhasivam }; 97828dbc29SManivannan Sadhasivam 98828dbc29SManivannan Sadhasivam static const struct regmap_config imx290_regmap_config = { 99828dbc29SManivannan Sadhasivam .reg_bits = 16, 100828dbc29SManivannan Sadhasivam .val_bits = 8, 101828dbc29SManivannan Sadhasivam .cache_type = REGCACHE_RBTREE, 102828dbc29SManivannan Sadhasivam }; 103828dbc29SManivannan Sadhasivam 104a58df1f9SManivannan Sadhasivam static const char * const imx290_test_pattern_menu[] = { 105a58df1f9SManivannan Sadhasivam "Disabled", 106a58df1f9SManivannan Sadhasivam "Sequence Pattern 1", 107a58df1f9SManivannan Sadhasivam "Horizontal Color-bar Chart", 108a58df1f9SManivannan Sadhasivam "Vertical Color-bar Chart", 109a58df1f9SManivannan Sadhasivam "Sequence Pattern 2", 110a58df1f9SManivannan Sadhasivam "Gradation Pattern 1", 111a58df1f9SManivannan Sadhasivam "Gradation Pattern 2", 112a58df1f9SManivannan Sadhasivam "000/555h Toggle Pattern", 113a58df1f9SManivannan Sadhasivam }; 114a58df1f9SManivannan Sadhasivam 115828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_global_init_settings[] = { 116828dbc29SManivannan Sadhasivam { 0x3007, 0x00 }, 117828dbc29SManivannan Sadhasivam { 0x3018, 0x65 }, 118828dbc29SManivannan Sadhasivam { 0x3019, 0x04 }, 119828dbc29SManivannan Sadhasivam { 0x301a, 0x00 }, 120828dbc29SManivannan Sadhasivam { 0x3444, 0x20 }, 121828dbc29SManivannan Sadhasivam { 0x3445, 0x25 }, 122828dbc29SManivannan Sadhasivam { 0x303a, 0x0c }, 123828dbc29SManivannan Sadhasivam { 0x3040, 0x00 }, 124828dbc29SManivannan Sadhasivam { 0x3041, 0x00 }, 125828dbc29SManivannan Sadhasivam { 0x303c, 0x00 }, 126828dbc29SManivannan Sadhasivam { 0x303d, 0x00 }, 127828dbc29SManivannan Sadhasivam { 0x3042, 0x9c }, 128828dbc29SManivannan Sadhasivam { 0x3043, 0x07 }, 129828dbc29SManivannan Sadhasivam { 0x303e, 0x49 }, 130828dbc29SManivannan Sadhasivam { 0x303f, 0x04 }, 131828dbc29SManivannan Sadhasivam { 0x304b, 0x0a }, 132828dbc29SManivannan Sadhasivam { 0x300f, 0x00 }, 133828dbc29SManivannan Sadhasivam { 0x3010, 0x21 }, 134828dbc29SManivannan Sadhasivam { 0x3012, 0x64 }, 135828dbc29SManivannan Sadhasivam { 0x3016, 0x09 }, 136828dbc29SManivannan Sadhasivam { 0x3070, 0x02 }, 137828dbc29SManivannan Sadhasivam { 0x3071, 0x11 }, 138828dbc29SManivannan Sadhasivam { 0x309b, 0x10 }, 139828dbc29SManivannan Sadhasivam { 0x309c, 0x22 }, 140828dbc29SManivannan Sadhasivam { 0x30a2, 0x02 }, 141828dbc29SManivannan Sadhasivam { 0x30a6, 0x20 }, 142828dbc29SManivannan Sadhasivam { 0x30a8, 0x20 }, 143828dbc29SManivannan Sadhasivam { 0x30aa, 0x20 }, 144828dbc29SManivannan Sadhasivam { 0x30ac, 0x20 }, 145828dbc29SManivannan Sadhasivam { 0x30b0, 0x43 }, 146828dbc29SManivannan Sadhasivam { 0x3119, 0x9e }, 147828dbc29SManivannan Sadhasivam { 0x311c, 0x1e }, 148828dbc29SManivannan Sadhasivam { 0x311e, 0x08 }, 149828dbc29SManivannan Sadhasivam { 0x3128, 0x05 }, 150828dbc29SManivannan Sadhasivam { 0x313d, 0x83 }, 151828dbc29SManivannan Sadhasivam { 0x3150, 0x03 }, 152828dbc29SManivannan Sadhasivam { 0x317e, 0x00 }, 153828dbc29SManivannan Sadhasivam { 0x32b8, 0x50 }, 154828dbc29SManivannan Sadhasivam { 0x32b9, 0x10 }, 155828dbc29SManivannan Sadhasivam { 0x32ba, 0x00 }, 156828dbc29SManivannan Sadhasivam { 0x32bb, 0x04 }, 157828dbc29SManivannan Sadhasivam { 0x32c8, 0x50 }, 158828dbc29SManivannan Sadhasivam { 0x32c9, 0x10 }, 159828dbc29SManivannan Sadhasivam { 0x32ca, 0x00 }, 160828dbc29SManivannan Sadhasivam { 0x32cb, 0x04 }, 161828dbc29SManivannan Sadhasivam { 0x332c, 0xd3 }, 162828dbc29SManivannan Sadhasivam { 0x332d, 0x10 }, 163828dbc29SManivannan Sadhasivam { 0x332e, 0x0d }, 164828dbc29SManivannan Sadhasivam { 0x3358, 0x06 }, 165828dbc29SManivannan Sadhasivam { 0x3359, 0xe1 }, 166828dbc29SManivannan Sadhasivam { 0x335a, 0x11 }, 167828dbc29SManivannan Sadhasivam { 0x3360, 0x1e }, 168828dbc29SManivannan Sadhasivam { 0x3361, 0x61 }, 169828dbc29SManivannan Sadhasivam { 0x3362, 0x10 }, 170828dbc29SManivannan Sadhasivam { 0x33b0, 0x50 }, 171828dbc29SManivannan Sadhasivam { 0x33b2, 0x1a }, 172828dbc29SManivannan Sadhasivam { 0x33b3, 0x04 }, 173828dbc29SManivannan Sadhasivam }; 174828dbc29SManivannan Sadhasivam 175828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_1080p_settings[] = { 176828dbc29SManivannan Sadhasivam /* mode settings */ 177828dbc29SManivannan Sadhasivam { 0x3007, 0x00 }, 178828dbc29SManivannan Sadhasivam { 0x303a, 0x0c }, 179828dbc29SManivannan Sadhasivam { 0x3414, 0x0a }, 180828dbc29SManivannan Sadhasivam { 0x3472, 0x80 }, 181828dbc29SManivannan Sadhasivam { 0x3473, 0x07 }, 182828dbc29SManivannan Sadhasivam { 0x3418, 0x38 }, 183828dbc29SManivannan Sadhasivam { 0x3419, 0x04 }, 184828dbc29SManivannan Sadhasivam { 0x3012, 0x64 }, 185828dbc29SManivannan Sadhasivam { 0x3013, 0x00 }, 186828dbc29SManivannan Sadhasivam { 0x305c, 0x18 }, 187828dbc29SManivannan Sadhasivam { 0x305d, 0x03 }, 188828dbc29SManivannan Sadhasivam { 0x305e, 0x20 }, 189828dbc29SManivannan Sadhasivam { 0x305f, 0x01 }, 190828dbc29SManivannan Sadhasivam { 0x315e, 0x1a }, 191828dbc29SManivannan Sadhasivam { 0x3164, 0x1a }, 192828dbc29SManivannan Sadhasivam { 0x3480, 0x49 }, 193828dbc29SManivannan Sadhasivam /* data rate settings */ 194828dbc29SManivannan Sadhasivam { 0x3405, 0x10 }, 195828dbc29SManivannan Sadhasivam { 0x3446, 0x57 }, 196828dbc29SManivannan Sadhasivam { 0x3447, 0x00 }, 197828dbc29SManivannan Sadhasivam { 0x3448, 0x37 }, 198828dbc29SManivannan Sadhasivam { 0x3449, 0x00 }, 199828dbc29SManivannan Sadhasivam { 0x344a, 0x1f }, 200828dbc29SManivannan Sadhasivam { 0x344b, 0x00 }, 201828dbc29SManivannan Sadhasivam { 0x344c, 0x1f }, 202828dbc29SManivannan Sadhasivam { 0x344d, 0x00 }, 203828dbc29SManivannan Sadhasivam { 0x344e, 0x1f }, 204828dbc29SManivannan Sadhasivam { 0x344f, 0x00 }, 205828dbc29SManivannan Sadhasivam { 0x3450, 0x77 }, 206828dbc29SManivannan Sadhasivam { 0x3451, 0x00 }, 207828dbc29SManivannan Sadhasivam { 0x3452, 0x1f }, 208828dbc29SManivannan Sadhasivam { 0x3453, 0x00 }, 209828dbc29SManivannan Sadhasivam { 0x3454, 0x17 }, 210828dbc29SManivannan Sadhasivam { 0x3455, 0x00 }, 211828dbc29SManivannan Sadhasivam }; 212828dbc29SManivannan Sadhasivam 213828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_720p_settings[] = { 214828dbc29SManivannan Sadhasivam /* mode settings */ 215828dbc29SManivannan Sadhasivam { 0x3007, 0x10 }, 216828dbc29SManivannan Sadhasivam { 0x303a, 0x06 }, 217828dbc29SManivannan Sadhasivam { 0x3414, 0x04 }, 218828dbc29SManivannan Sadhasivam { 0x3472, 0x00 }, 219828dbc29SManivannan Sadhasivam { 0x3473, 0x05 }, 220828dbc29SManivannan Sadhasivam { 0x3418, 0xd0 }, 221828dbc29SManivannan Sadhasivam { 0x3419, 0x02 }, 222828dbc29SManivannan Sadhasivam { 0x3012, 0x64 }, 223828dbc29SManivannan Sadhasivam { 0x3013, 0x00 }, 224828dbc29SManivannan Sadhasivam { 0x305c, 0x20 }, 225828dbc29SManivannan Sadhasivam { 0x305d, 0x00 }, 226828dbc29SManivannan Sadhasivam { 0x305e, 0x20 }, 227828dbc29SManivannan Sadhasivam { 0x305f, 0x01 }, 228828dbc29SManivannan Sadhasivam { 0x315e, 0x1a }, 229828dbc29SManivannan Sadhasivam { 0x3164, 0x1a }, 230828dbc29SManivannan Sadhasivam { 0x3480, 0x49 }, 231828dbc29SManivannan Sadhasivam /* data rate settings */ 232828dbc29SManivannan Sadhasivam { 0x3405, 0x10 }, 233828dbc29SManivannan Sadhasivam { 0x3446, 0x4f }, 234828dbc29SManivannan Sadhasivam { 0x3447, 0x00 }, 235828dbc29SManivannan Sadhasivam { 0x3448, 0x2f }, 236828dbc29SManivannan Sadhasivam { 0x3449, 0x00 }, 237828dbc29SManivannan Sadhasivam { 0x344a, 0x17 }, 238828dbc29SManivannan Sadhasivam { 0x344b, 0x00 }, 239828dbc29SManivannan Sadhasivam { 0x344c, 0x17 }, 240828dbc29SManivannan Sadhasivam { 0x344d, 0x00 }, 241828dbc29SManivannan Sadhasivam { 0x344e, 0x17 }, 242828dbc29SManivannan Sadhasivam { 0x344f, 0x00 }, 243828dbc29SManivannan Sadhasivam { 0x3450, 0x57 }, 244828dbc29SManivannan Sadhasivam { 0x3451, 0x00 }, 245828dbc29SManivannan Sadhasivam { 0x3452, 0x17 }, 246828dbc29SManivannan Sadhasivam { 0x3453, 0x00 }, 247828dbc29SManivannan Sadhasivam { 0x3454, 0x17 }, 248828dbc29SManivannan Sadhasivam { 0x3455, 0x00 }, 249828dbc29SManivannan Sadhasivam }; 250828dbc29SManivannan Sadhasivam 251828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_10bit_settings[] = { 252828dbc29SManivannan Sadhasivam { 0x3005, 0x00}, 253828dbc29SManivannan Sadhasivam { 0x3046, 0x00}, 254828dbc29SManivannan Sadhasivam { 0x3129, 0x1d}, 255828dbc29SManivannan Sadhasivam { 0x317c, 0x12}, 256828dbc29SManivannan Sadhasivam { 0x31ec, 0x37}, 257828dbc29SManivannan Sadhasivam { 0x3441, 0x0a}, 258828dbc29SManivannan Sadhasivam { 0x3442, 0x0a}, 259828dbc29SManivannan Sadhasivam { 0x300a, 0x3c}, 260828dbc29SManivannan Sadhasivam { 0x300b, 0x00}, 261828dbc29SManivannan Sadhasivam }; 262828dbc29SManivannan Sadhasivam 263c566ac01SManivannan Sadhasivam static const struct imx290_regval imx290_12bit_settings[] = { 264c566ac01SManivannan Sadhasivam { 0x3005, 0x01 }, 265c566ac01SManivannan Sadhasivam { 0x3046, 0x01 }, 266c566ac01SManivannan Sadhasivam { 0x3129, 0x00 }, 267c566ac01SManivannan Sadhasivam { 0x317c, 0x00 }, 268c566ac01SManivannan Sadhasivam { 0x31ec, 0x0e }, 269c566ac01SManivannan Sadhasivam { 0x3441, 0x0c }, 270c566ac01SManivannan Sadhasivam { 0x3442, 0x0c }, 271c566ac01SManivannan Sadhasivam { 0x300a, 0xf0 }, 272c566ac01SManivannan Sadhasivam { 0x300b, 0x00 }, 273c566ac01SManivannan Sadhasivam }; 274c566ac01SManivannan Sadhasivam 275828dbc29SManivannan Sadhasivam /* supported link frequencies */ 27698e0500eSManivannan Sadhasivam #define FREQ_INDEX_1080P 0 27798e0500eSManivannan Sadhasivam #define FREQ_INDEX_720P 1 27898e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_2lanes[] = { 27998e0500eSManivannan Sadhasivam [FREQ_INDEX_1080P] = 445500000, 28098e0500eSManivannan Sadhasivam [FREQ_INDEX_720P] = 297000000, 281828dbc29SManivannan Sadhasivam }; 28298e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_4lanes[] = { 28398e0500eSManivannan Sadhasivam [FREQ_INDEX_1080P] = 222750000, 28498e0500eSManivannan Sadhasivam [FREQ_INDEX_720P] = 148500000, 28598e0500eSManivannan Sadhasivam }; 28698e0500eSManivannan Sadhasivam 28798e0500eSManivannan Sadhasivam /* 28898e0500eSManivannan Sadhasivam * In this function and in the similar ones below We rely on imx290_probe() 28998e0500eSManivannan Sadhasivam * to ensure that nlanes is either 2 or 4. 29098e0500eSManivannan Sadhasivam */ 29198e0500eSManivannan Sadhasivam static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290) 29298e0500eSManivannan Sadhasivam { 29398e0500eSManivannan Sadhasivam if (imx290->nlanes == 2) 29498e0500eSManivannan Sadhasivam return imx290_link_freq_2lanes; 29598e0500eSManivannan Sadhasivam else 29698e0500eSManivannan Sadhasivam return imx290_link_freq_4lanes; 29798e0500eSManivannan Sadhasivam } 29898e0500eSManivannan Sadhasivam 29998e0500eSManivannan Sadhasivam static inline int imx290_link_freqs_num(const struct imx290 *imx290) 30098e0500eSManivannan Sadhasivam { 30198e0500eSManivannan Sadhasivam if (imx290->nlanes == 2) 30298e0500eSManivannan Sadhasivam return ARRAY_SIZE(imx290_link_freq_2lanes); 30398e0500eSManivannan Sadhasivam else 30498e0500eSManivannan Sadhasivam return ARRAY_SIZE(imx290_link_freq_4lanes); 30598e0500eSManivannan Sadhasivam } 306828dbc29SManivannan Sadhasivam 307828dbc29SManivannan Sadhasivam /* Mode configs */ 30897589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_2lanes[] = { 309828dbc29SManivannan Sadhasivam { 310828dbc29SManivannan Sadhasivam .width = 1920, 311828dbc29SManivannan Sadhasivam .height = 1080, 31297589ad6SManivannan Sadhasivam .hmax = 0x1130, 31398e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_1080P, 314828dbc29SManivannan Sadhasivam .data = imx290_1080p_settings, 315828dbc29SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_1080p_settings), 316828dbc29SManivannan Sadhasivam }, 317828dbc29SManivannan Sadhasivam { 318828dbc29SManivannan Sadhasivam .width = 1280, 319828dbc29SManivannan Sadhasivam .height = 720, 32097589ad6SManivannan Sadhasivam .hmax = 0x19c8, 32198e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_720P, 322828dbc29SManivannan Sadhasivam .data = imx290_720p_settings, 323828dbc29SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_720p_settings), 324828dbc29SManivannan Sadhasivam }, 325828dbc29SManivannan Sadhasivam }; 326828dbc29SManivannan Sadhasivam 32797589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_4lanes[] = { 32897589ad6SManivannan Sadhasivam { 32997589ad6SManivannan Sadhasivam .width = 1920, 33097589ad6SManivannan Sadhasivam .height = 1080, 33197589ad6SManivannan Sadhasivam .hmax = 0x0898, 33298e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_1080P, 33397589ad6SManivannan Sadhasivam .data = imx290_1080p_settings, 33497589ad6SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_1080p_settings), 33597589ad6SManivannan Sadhasivam }, 33697589ad6SManivannan Sadhasivam { 33797589ad6SManivannan Sadhasivam .width = 1280, 33897589ad6SManivannan Sadhasivam .height = 720, 33997589ad6SManivannan Sadhasivam .hmax = 0x0ce4, 34098e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_720P, 34197589ad6SManivannan Sadhasivam .data = imx290_720p_settings, 34297589ad6SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_720p_settings), 34397589ad6SManivannan Sadhasivam }, 34497589ad6SManivannan Sadhasivam }; 34597589ad6SManivannan Sadhasivam 34697589ad6SManivannan Sadhasivam static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) 34797589ad6SManivannan Sadhasivam { 34897589ad6SManivannan Sadhasivam if (imx290->nlanes == 2) 34997589ad6SManivannan Sadhasivam return imx290_modes_2lanes; 35097589ad6SManivannan Sadhasivam else 35197589ad6SManivannan Sadhasivam return imx290_modes_4lanes; 35297589ad6SManivannan Sadhasivam } 35397589ad6SManivannan Sadhasivam 35497589ad6SManivannan Sadhasivam static inline int imx290_modes_num(const struct imx290 *imx290) 35597589ad6SManivannan Sadhasivam { 35697589ad6SManivannan Sadhasivam if (imx290->nlanes == 2) 35797589ad6SManivannan Sadhasivam return ARRAY_SIZE(imx290_modes_2lanes); 35897589ad6SManivannan Sadhasivam else 35997589ad6SManivannan Sadhasivam return ARRAY_SIZE(imx290_modes_4lanes); 36097589ad6SManivannan Sadhasivam } 36197589ad6SManivannan Sadhasivam 362828dbc29SManivannan Sadhasivam static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) 363828dbc29SManivannan Sadhasivam { 364828dbc29SManivannan Sadhasivam return container_of(_sd, struct imx290, sd); 365828dbc29SManivannan Sadhasivam } 366828dbc29SManivannan Sadhasivam 367828dbc29SManivannan Sadhasivam static inline int imx290_read_reg(struct imx290 *imx290, u16 addr, u8 *value) 368828dbc29SManivannan Sadhasivam { 369828dbc29SManivannan Sadhasivam unsigned int regval; 370828dbc29SManivannan Sadhasivam int ret; 371828dbc29SManivannan Sadhasivam 372828dbc29SManivannan Sadhasivam ret = regmap_read(imx290->regmap, addr, ®val); 373828dbc29SManivannan Sadhasivam if (ret) { 374828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "I2C read failed for addr: %x\n", addr); 375828dbc29SManivannan Sadhasivam return ret; 376828dbc29SManivannan Sadhasivam } 377828dbc29SManivannan Sadhasivam 378828dbc29SManivannan Sadhasivam *value = regval & 0xff; 379828dbc29SManivannan Sadhasivam 380828dbc29SManivannan Sadhasivam return 0; 381828dbc29SManivannan Sadhasivam } 382828dbc29SManivannan Sadhasivam 383828dbc29SManivannan Sadhasivam static int imx290_write_reg(struct imx290 *imx290, u16 addr, u8 value) 384828dbc29SManivannan Sadhasivam { 385828dbc29SManivannan Sadhasivam int ret; 386828dbc29SManivannan Sadhasivam 387828dbc29SManivannan Sadhasivam ret = regmap_write(imx290->regmap, addr, value); 388828dbc29SManivannan Sadhasivam if (ret) { 389828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "I2C write failed for addr: %x\n", addr); 390828dbc29SManivannan Sadhasivam return ret; 391828dbc29SManivannan Sadhasivam } 392828dbc29SManivannan Sadhasivam 393828dbc29SManivannan Sadhasivam return ret; 394828dbc29SManivannan Sadhasivam } 395828dbc29SManivannan Sadhasivam 396828dbc29SManivannan Sadhasivam static int imx290_set_register_array(struct imx290 *imx290, 397828dbc29SManivannan Sadhasivam const struct imx290_regval *settings, 398828dbc29SManivannan Sadhasivam unsigned int num_settings) 399828dbc29SManivannan Sadhasivam { 400828dbc29SManivannan Sadhasivam unsigned int i; 401828dbc29SManivannan Sadhasivam int ret; 402828dbc29SManivannan Sadhasivam 403828dbc29SManivannan Sadhasivam for (i = 0; i < num_settings; ++i, ++settings) { 404828dbc29SManivannan Sadhasivam ret = imx290_write_reg(imx290, settings->reg, settings->val); 405828dbc29SManivannan Sadhasivam if (ret < 0) 406828dbc29SManivannan Sadhasivam return ret; 407828dbc29SManivannan Sadhasivam 408828dbc29SManivannan Sadhasivam /* Settle time is 10ms for all registers */ 409828dbc29SManivannan Sadhasivam msleep(10); 410828dbc29SManivannan Sadhasivam } 411828dbc29SManivannan Sadhasivam 412828dbc29SManivannan Sadhasivam return 0; 413828dbc29SManivannan Sadhasivam } 414828dbc29SManivannan Sadhasivam 415828dbc29SManivannan Sadhasivam static int imx290_write_buffered_reg(struct imx290 *imx290, u16 address_low, 416828dbc29SManivannan Sadhasivam u8 nr_regs, u32 value) 417828dbc29SManivannan Sadhasivam { 418828dbc29SManivannan Sadhasivam unsigned int i; 419828dbc29SManivannan Sadhasivam int ret; 420828dbc29SManivannan Sadhasivam 421828dbc29SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_REGHOLD, 0x01); 422828dbc29SManivannan Sadhasivam if (ret) { 423828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Error setting hold register\n"); 424828dbc29SManivannan Sadhasivam return ret; 425828dbc29SManivannan Sadhasivam } 426828dbc29SManivannan Sadhasivam 427828dbc29SManivannan Sadhasivam for (i = 0; i < nr_regs; i++) { 428828dbc29SManivannan Sadhasivam ret = imx290_write_reg(imx290, address_low + i, 429828dbc29SManivannan Sadhasivam (u8)(value >> (i * 8))); 430828dbc29SManivannan Sadhasivam if (ret) { 431828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Error writing buffered registers\n"); 432828dbc29SManivannan Sadhasivam return ret; 433828dbc29SManivannan Sadhasivam } 434828dbc29SManivannan Sadhasivam } 435828dbc29SManivannan Sadhasivam 436828dbc29SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_REGHOLD, 0x00); 437828dbc29SManivannan Sadhasivam if (ret) { 438828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Error setting hold register\n"); 439828dbc29SManivannan Sadhasivam return ret; 440828dbc29SManivannan Sadhasivam } 441828dbc29SManivannan Sadhasivam 442828dbc29SManivannan Sadhasivam return ret; 443828dbc29SManivannan Sadhasivam } 444828dbc29SManivannan Sadhasivam 445828dbc29SManivannan Sadhasivam static int imx290_set_gain(struct imx290 *imx290, u32 value) 446828dbc29SManivannan Sadhasivam { 447828dbc29SManivannan Sadhasivam int ret; 448828dbc29SManivannan Sadhasivam 449828dbc29SManivannan Sadhasivam ret = imx290_write_buffered_reg(imx290, IMX290_GAIN, 1, value); 450828dbc29SManivannan Sadhasivam if (ret) 451828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Unable to write gain\n"); 452828dbc29SManivannan Sadhasivam 453828dbc29SManivannan Sadhasivam return ret; 454828dbc29SManivannan Sadhasivam } 455828dbc29SManivannan Sadhasivam 456828dbc29SManivannan Sadhasivam /* Stop streaming */ 457828dbc29SManivannan Sadhasivam static int imx290_stop_streaming(struct imx290 *imx290) 458828dbc29SManivannan Sadhasivam { 459828dbc29SManivannan Sadhasivam int ret; 460828dbc29SManivannan Sadhasivam 461828dbc29SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_STANDBY, 0x01); 462828dbc29SManivannan Sadhasivam if (ret < 0) 463828dbc29SManivannan Sadhasivam return ret; 464828dbc29SManivannan Sadhasivam 465828dbc29SManivannan Sadhasivam msleep(30); 466828dbc29SManivannan Sadhasivam 467828dbc29SManivannan Sadhasivam return imx290_write_reg(imx290, IMX290_XMSTA, 0x01); 468828dbc29SManivannan Sadhasivam } 469828dbc29SManivannan Sadhasivam 470828dbc29SManivannan Sadhasivam static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) 471828dbc29SManivannan Sadhasivam { 472828dbc29SManivannan Sadhasivam struct imx290 *imx290 = container_of(ctrl->handler, 473828dbc29SManivannan Sadhasivam struct imx290, ctrls); 474828dbc29SManivannan Sadhasivam int ret = 0; 475828dbc29SManivannan Sadhasivam 476828dbc29SManivannan Sadhasivam /* V4L2 controls values will be applied only when power is already up */ 477828dbc29SManivannan Sadhasivam if (!pm_runtime_get_if_in_use(imx290->dev)) 478828dbc29SManivannan Sadhasivam return 0; 479828dbc29SManivannan Sadhasivam 480828dbc29SManivannan Sadhasivam switch (ctrl->id) { 481828dbc29SManivannan Sadhasivam case V4L2_CID_GAIN: 482828dbc29SManivannan Sadhasivam ret = imx290_set_gain(imx290, ctrl->val); 483828dbc29SManivannan Sadhasivam break; 484a58df1f9SManivannan Sadhasivam case V4L2_CID_TEST_PATTERN: 485a58df1f9SManivannan Sadhasivam if (ctrl->val) { 486a58df1f9SManivannan Sadhasivam imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00); 487a58df1f9SManivannan Sadhasivam imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); 488a58df1f9SManivannan Sadhasivam msleep(10); 489a58df1f9SManivannan Sadhasivam imx290_write_reg(imx290, IMX290_PGCTRL, 490a58df1f9SManivannan Sadhasivam (u8)(IMX290_PGCTRL_REGEN | 491a58df1f9SManivannan Sadhasivam IMX290_PGCTRL_THRU | 492a58df1f9SManivannan Sadhasivam IMX290_PGCTRL_MODE(ctrl->val))); 493a58df1f9SManivannan Sadhasivam } else { 494a58df1f9SManivannan Sadhasivam imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); 495a58df1f9SManivannan Sadhasivam msleep(10); 496c566ac01SManivannan Sadhasivam if (imx290->bpp == 10) 497c566ac01SManivannan Sadhasivam imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 498c566ac01SManivannan Sadhasivam 0x3c); 499c566ac01SManivannan Sadhasivam else /* 12 bits per pixel */ 500c566ac01SManivannan Sadhasivam imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 501c566ac01SManivannan Sadhasivam 0xf0); 502a58df1f9SManivannan Sadhasivam imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); 503a58df1f9SManivannan Sadhasivam } 504a58df1f9SManivannan Sadhasivam break; 505828dbc29SManivannan Sadhasivam default: 506828dbc29SManivannan Sadhasivam ret = -EINVAL; 507828dbc29SManivannan Sadhasivam break; 508828dbc29SManivannan Sadhasivam } 509828dbc29SManivannan Sadhasivam 510828dbc29SManivannan Sadhasivam pm_runtime_put(imx290->dev); 511828dbc29SManivannan Sadhasivam 512828dbc29SManivannan Sadhasivam return ret; 513828dbc29SManivannan Sadhasivam } 514828dbc29SManivannan Sadhasivam 515828dbc29SManivannan Sadhasivam static const struct v4l2_ctrl_ops imx290_ctrl_ops = { 516828dbc29SManivannan Sadhasivam .s_ctrl = imx290_set_ctrl, 517828dbc29SManivannan Sadhasivam }; 518828dbc29SManivannan Sadhasivam 519828dbc29SManivannan Sadhasivam static int imx290_enum_mbus_code(struct v4l2_subdev *sd, 520828dbc29SManivannan Sadhasivam struct v4l2_subdev_pad_config *cfg, 521828dbc29SManivannan Sadhasivam struct v4l2_subdev_mbus_code_enum *code) 522828dbc29SManivannan Sadhasivam { 523828dbc29SManivannan Sadhasivam if (code->index >= ARRAY_SIZE(imx290_formats)) 524828dbc29SManivannan Sadhasivam return -EINVAL; 525828dbc29SManivannan Sadhasivam 526828dbc29SManivannan Sadhasivam code->code = imx290_formats[code->index].code; 527828dbc29SManivannan Sadhasivam 528828dbc29SManivannan Sadhasivam return 0; 529828dbc29SManivannan Sadhasivam } 530828dbc29SManivannan Sadhasivam 5313b867fb6SManivannan Sadhasivam static int imx290_enum_frame_size(struct v4l2_subdev *sd, 5323b867fb6SManivannan Sadhasivam struct v4l2_subdev_pad_config *cfg, 5333b867fb6SManivannan Sadhasivam struct v4l2_subdev_frame_size_enum *fse) 5343b867fb6SManivannan Sadhasivam { 5353b867fb6SManivannan Sadhasivam const struct imx290 *imx290 = to_imx290(sd); 5363b867fb6SManivannan Sadhasivam const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); 5373b867fb6SManivannan Sadhasivam 5383b867fb6SManivannan Sadhasivam if ((fse->code != imx290_formats[0].code) && 5393b867fb6SManivannan Sadhasivam (fse->code != imx290_formats[1].code)) 5403b867fb6SManivannan Sadhasivam return -EINVAL; 5413b867fb6SManivannan Sadhasivam 5423b867fb6SManivannan Sadhasivam if (fse->index >= imx290_modes_num(imx290)) 5433b867fb6SManivannan Sadhasivam return -EINVAL; 5443b867fb6SManivannan Sadhasivam 5453b867fb6SManivannan Sadhasivam fse->min_width = imx290_modes[fse->index].width; 5463b867fb6SManivannan Sadhasivam fse->max_width = imx290_modes[fse->index].width; 5473b867fb6SManivannan Sadhasivam fse->min_height = imx290_modes[fse->index].height; 5483b867fb6SManivannan Sadhasivam fse->max_height = imx290_modes[fse->index].height; 5493b867fb6SManivannan Sadhasivam 5503b867fb6SManivannan Sadhasivam return 0; 5513b867fb6SManivannan Sadhasivam } 5523b867fb6SManivannan Sadhasivam 553828dbc29SManivannan Sadhasivam static int imx290_get_fmt(struct v4l2_subdev *sd, 554828dbc29SManivannan Sadhasivam struct v4l2_subdev_pad_config *cfg, 555828dbc29SManivannan Sadhasivam struct v4l2_subdev_format *fmt) 556828dbc29SManivannan Sadhasivam { 557828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 558828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt *framefmt; 559828dbc29SManivannan Sadhasivam 560828dbc29SManivannan Sadhasivam mutex_lock(&imx290->lock); 561828dbc29SManivannan Sadhasivam 562828dbc29SManivannan Sadhasivam if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 563828dbc29SManivannan Sadhasivam framefmt = v4l2_subdev_get_try_format(&imx290->sd, cfg, 564828dbc29SManivannan Sadhasivam fmt->pad); 565828dbc29SManivannan Sadhasivam else 566828dbc29SManivannan Sadhasivam framefmt = &imx290->current_format; 567828dbc29SManivannan Sadhasivam 568828dbc29SManivannan Sadhasivam fmt->format = *framefmt; 569828dbc29SManivannan Sadhasivam 570828dbc29SManivannan Sadhasivam mutex_unlock(&imx290->lock); 571828dbc29SManivannan Sadhasivam 572828dbc29SManivannan Sadhasivam return 0; 573828dbc29SManivannan Sadhasivam } 574828dbc29SManivannan Sadhasivam 57598e0500eSManivannan Sadhasivam static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) 57698e0500eSManivannan Sadhasivam { 57798e0500eSManivannan Sadhasivam return imx290->current_mode->link_freq_index; 57898e0500eSManivannan Sadhasivam } 57998e0500eSManivannan Sadhasivam 58098e0500eSManivannan Sadhasivam static s64 imx290_get_link_freq(struct imx290 *imx290) 58198e0500eSManivannan Sadhasivam { 58298e0500eSManivannan Sadhasivam u8 index = imx290_get_link_freq_index(imx290); 58398e0500eSManivannan Sadhasivam 58498e0500eSManivannan Sadhasivam return *(imx290_link_freqs_ptr(imx290) + index); 58598e0500eSManivannan Sadhasivam } 58698e0500eSManivannan Sadhasivam 58798e0500eSManivannan Sadhasivam static u64 imx290_calc_pixel_rate(struct imx290 *imx290) 58898e0500eSManivannan Sadhasivam { 58998e0500eSManivannan Sadhasivam s64 link_freq = imx290_get_link_freq(imx290); 59098e0500eSManivannan Sadhasivam u8 nlanes = imx290->nlanes; 59198e0500eSManivannan Sadhasivam u64 pixel_rate; 59298e0500eSManivannan Sadhasivam 59398e0500eSManivannan Sadhasivam /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ 59498e0500eSManivannan Sadhasivam pixel_rate = link_freq * 2 * nlanes; 595c566ac01SManivannan Sadhasivam do_div(pixel_rate, imx290->bpp); 59698e0500eSManivannan Sadhasivam return pixel_rate; 59798e0500eSManivannan Sadhasivam } 59898e0500eSManivannan Sadhasivam 599828dbc29SManivannan Sadhasivam static int imx290_set_fmt(struct v4l2_subdev *sd, 600828dbc29SManivannan Sadhasivam struct v4l2_subdev_pad_config *cfg, 601828dbc29SManivannan Sadhasivam struct v4l2_subdev_format *fmt) 602828dbc29SManivannan Sadhasivam { 603828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 604828dbc29SManivannan Sadhasivam const struct imx290_mode *mode; 605828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt *format; 606828dbc29SManivannan Sadhasivam unsigned int i; 607828dbc29SManivannan Sadhasivam 608828dbc29SManivannan Sadhasivam mutex_lock(&imx290->lock); 609828dbc29SManivannan Sadhasivam 61097589ad6SManivannan Sadhasivam mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290), 61197589ad6SManivannan Sadhasivam imx290_modes_num(imx290), width, height, 612828dbc29SManivannan Sadhasivam fmt->format.width, fmt->format.height); 613828dbc29SManivannan Sadhasivam 614828dbc29SManivannan Sadhasivam fmt->format.width = mode->width; 615828dbc29SManivannan Sadhasivam fmt->format.height = mode->height; 616828dbc29SManivannan Sadhasivam 617828dbc29SManivannan Sadhasivam for (i = 0; i < ARRAY_SIZE(imx290_formats); i++) 618828dbc29SManivannan Sadhasivam if (imx290_formats[i].code == fmt->format.code) 619828dbc29SManivannan Sadhasivam break; 620828dbc29SManivannan Sadhasivam 621828dbc29SManivannan Sadhasivam if (i >= ARRAY_SIZE(imx290_formats)) 622828dbc29SManivannan Sadhasivam i = 0; 623828dbc29SManivannan Sadhasivam 624828dbc29SManivannan Sadhasivam fmt->format.code = imx290_formats[i].code; 625828dbc29SManivannan Sadhasivam fmt->format.field = V4L2_FIELD_NONE; 626828dbc29SManivannan Sadhasivam 627828dbc29SManivannan Sadhasivam if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 628828dbc29SManivannan Sadhasivam format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 629828dbc29SManivannan Sadhasivam } else { 630828dbc29SManivannan Sadhasivam format = &imx290->current_format; 631828dbc29SManivannan Sadhasivam imx290->current_mode = mode; 632c566ac01SManivannan Sadhasivam imx290->bpp = imx290_formats[i].bpp; 63398e0500eSManivannan Sadhasivam 63498e0500eSManivannan Sadhasivam if (imx290->link_freq) 63598e0500eSManivannan Sadhasivam __v4l2_ctrl_s_ctrl(imx290->link_freq, 63698e0500eSManivannan Sadhasivam imx290_get_link_freq_index(imx290)); 63798e0500eSManivannan Sadhasivam if (imx290->pixel_rate) 63898e0500eSManivannan Sadhasivam __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, 63998e0500eSManivannan Sadhasivam imx290_calc_pixel_rate(imx290)); 640828dbc29SManivannan Sadhasivam } 641828dbc29SManivannan Sadhasivam 642828dbc29SManivannan Sadhasivam *format = fmt->format; 643828dbc29SManivannan Sadhasivam 644828dbc29SManivannan Sadhasivam mutex_unlock(&imx290->lock); 645828dbc29SManivannan Sadhasivam 646828dbc29SManivannan Sadhasivam return 0; 647828dbc29SManivannan Sadhasivam } 648828dbc29SManivannan Sadhasivam 649828dbc29SManivannan Sadhasivam static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, 650828dbc29SManivannan Sadhasivam struct v4l2_subdev_pad_config *cfg) 651828dbc29SManivannan Sadhasivam { 652828dbc29SManivannan Sadhasivam struct v4l2_subdev_format fmt = { 0 }; 653828dbc29SManivannan Sadhasivam 654828dbc29SManivannan Sadhasivam fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; 655828dbc29SManivannan Sadhasivam fmt.format.width = 1920; 656828dbc29SManivannan Sadhasivam fmt.format.height = 1080; 657828dbc29SManivannan Sadhasivam 658828dbc29SManivannan Sadhasivam imx290_set_fmt(subdev, cfg, &fmt); 659828dbc29SManivannan Sadhasivam 660828dbc29SManivannan Sadhasivam return 0; 661828dbc29SManivannan Sadhasivam } 662828dbc29SManivannan Sadhasivam 66398e0500eSManivannan Sadhasivam static int imx290_write_current_format(struct imx290 *imx290) 664828dbc29SManivannan Sadhasivam { 665828dbc29SManivannan Sadhasivam int ret; 666828dbc29SManivannan Sadhasivam 66798e0500eSManivannan Sadhasivam switch (imx290->current_format.code) { 668828dbc29SManivannan Sadhasivam case MEDIA_BUS_FMT_SRGGB10_1X10: 669828dbc29SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290_10bit_settings, 670828dbc29SManivannan Sadhasivam ARRAY_SIZE( 671828dbc29SManivannan Sadhasivam imx290_10bit_settings)); 672828dbc29SManivannan Sadhasivam if (ret < 0) { 673828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set format registers\n"); 674828dbc29SManivannan Sadhasivam return ret; 675828dbc29SManivannan Sadhasivam } 676828dbc29SManivannan Sadhasivam break; 677c566ac01SManivannan Sadhasivam case MEDIA_BUS_FMT_SRGGB12_1X12: 678c566ac01SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290_12bit_settings, 679c566ac01SManivannan Sadhasivam ARRAY_SIZE( 680c566ac01SManivannan Sadhasivam imx290_12bit_settings)); 681c566ac01SManivannan Sadhasivam if (ret < 0) { 682c566ac01SManivannan Sadhasivam dev_err(imx290->dev, "Could not set format registers\n"); 683c566ac01SManivannan Sadhasivam return ret; 684c566ac01SManivannan Sadhasivam } 685c566ac01SManivannan Sadhasivam break; 686828dbc29SManivannan Sadhasivam default: 687828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Unknown pixel format\n"); 688828dbc29SManivannan Sadhasivam return -EINVAL; 689828dbc29SManivannan Sadhasivam } 690828dbc29SManivannan Sadhasivam 691828dbc29SManivannan Sadhasivam return 0; 692828dbc29SManivannan Sadhasivam } 693828dbc29SManivannan Sadhasivam 69497589ad6SManivannan Sadhasivam static int imx290_set_hmax(struct imx290 *imx290, u32 val) 69597589ad6SManivannan Sadhasivam { 69697589ad6SManivannan Sadhasivam int ret; 69797589ad6SManivannan Sadhasivam 69897589ad6SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_HMAX_LOW, (val & 0xff)); 69997589ad6SManivannan Sadhasivam if (ret) { 70097589ad6SManivannan Sadhasivam dev_err(imx290->dev, "Error setting HMAX register\n"); 70197589ad6SManivannan Sadhasivam return ret; 70297589ad6SManivannan Sadhasivam } 70397589ad6SManivannan Sadhasivam 70497589ad6SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_HMAX_HIGH, ((val >> 8) & 0xff)); 70597589ad6SManivannan Sadhasivam if (ret) { 70697589ad6SManivannan Sadhasivam dev_err(imx290->dev, "Error setting HMAX register\n"); 70797589ad6SManivannan Sadhasivam return ret; 70897589ad6SManivannan Sadhasivam } 70997589ad6SManivannan Sadhasivam 71097589ad6SManivannan Sadhasivam return 0; 71197589ad6SManivannan Sadhasivam } 71297589ad6SManivannan Sadhasivam 713828dbc29SManivannan Sadhasivam /* Start streaming */ 714828dbc29SManivannan Sadhasivam static int imx290_start_streaming(struct imx290 *imx290) 715828dbc29SManivannan Sadhasivam { 716828dbc29SManivannan Sadhasivam int ret; 717828dbc29SManivannan Sadhasivam 718828dbc29SManivannan Sadhasivam /* Set init register settings */ 719828dbc29SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290_global_init_settings, 720828dbc29SManivannan Sadhasivam ARRAY_SIZE( 721828dbc29SManivannan Sadhasivam imx290_global_init_settings)); 722828dbc29SManivannan Sadhasivam if (ret < 0) { 723828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set init registers\n"); 724828dbc29SManivannan Sadhasivam return ret; 725828dbc29SManivannan Sadhasivam } 726828dbc29SManivannan Sadhasivam 72798e0500eSManivannan Sadhasivam /* Apply the register values related to current frame format */ 72898e0500eSManivannan Sadhasivam ret = imx290_write_current_format(imx290); 729828dbc29SManivannan Sadhasivam if (ret < 0) { 730828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set frame format\n"); 731828dbc29SManivannan Sadhasivam return ret; 732828dbc29SManivannan Sadhasivam } 733828dbc29SManivannan Sadhasivam 734828dbc29SManivannan Sadhasivam /* Apply default values of current mode */ 735828dbc29SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290->current_mode->data, 736828dbc29SManivannan Sadhasivam imx290->current_mode->data_size); 737828dbc29SManivannan Sadhasivam if (ret < 0) { 738828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set current mode\n"); 739828dbc29SManivannan Sadhasivam return ret; 740828dbc29SManivannan Sadhasivam } 74197589ad6SManivannan Sadhasivam ret = imx290_set_hmax(imx290, imx290->current_mode->hmax); 74297589ad6SManivannan Sadhasivam if (ret < 0) 74397589ad6SManivannan Sadhasivam return ret; 744828dbc29SManivannan Sadhasivam 745828dbc29SManivannan Sadhasivam /* Apply customized values from user */ 746828dbc29SManivannan Sadhasivam ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); 747828dbc29SManivannan Sadhasivam if (ret) { 748828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not sync v4l2 controls\n"); 749828dbc29SManivannan Sadhasivam return ret; 750828dbc29SManivannan Sadhasivam } 751828dbc29SManivannan Sadhasivam 752828dbc29SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_STANDBY, 0x00); 753828dbc29SManivannan Sadhasivam if (ret < 0) 754828dbc29SManivannan Sadhasivam return ret; 755828dbc29SManivannan Sadhasivam 756828dbc29SManivannan Sadhasivam msleep(30); 757828dbc29SManivannan Sadhasivam 758828dbc29SManivannan Sadhasivam /* Start streaming */ 759828dbc29SManivannan Sadhasivam return imx290_write_reg(imx290, IMX290_XMSTA, 0x00); 760828dbc29SManivannan Sadhasivam } 761828dbc29SManivannan Sadhasivam 762828dbc29SManivannan Sadhasivam static int imx290_set_stream(struct v4l2_subdev *sd, int enable) 763828dbc29SManivannan Sadhasivam { 764828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 765828dbc29SManivannan Sadhasivam int ret = 0; 766828dbc29SManivannan Sadhasivam 767828dbc29SManivannan Sadhasivam if (enable) { 768828dbc29SManivannan Sadhasivam ret = pm_runtime_get_sync(imx290->dev); 769828dbc29SManivannan Sadhasivam if (ret < 0) { 770828dbc29SManivannan Sadhasivam pm_runtime_put_noidle(imx290->dev); 771828dbc29SManivannan Sadhasivam goto unlock_and_return; 772828dbc29SManivannan Sadhasivam } 773828dbc29SManivannan Sadhasivam 774828dbc29SManivannan Sadhasivam ret = imx290_start_streaming(imx290); 775828dbc29SManivannan Sadhasivam if (ret) { 776828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Start stream failed\n"); 777828dbc29SManivannan Sadhasivam pm_runtime_put(imx290->dev); 778828dbc29SManivannan Sadhasivam goto unlock_and_return; 779828dbc29SManivannan Sadhasivam } 780828dbc29SManivannan Sadhasivam } else { 781828dbc29SManivannan Sadhasivam imx290_stop_streaming(imx290); 782828dbc29SManivannan Sadhasivam pm_runtime_put(imx290->dev); 783828dbc29SManivannan Sadhasivam } 784828dbc29SManivannan Sadhasivam 785828dbc29SManivannan Sadhasivam unlock_and_return: 786828dbc29SManivannan Sadhasivam 787828dbc29SManivannan Sadhasivam return ret; 788828dbc29SManivannan Sadhasivam } 789828dbc29SManivannan Sadhasivam 790828dbc29SManivannan Sadhasivam static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) 791828dbc29SManivannan Sadhasivam { 792828dbc29SManivannan Sadhasivam unsigned int i; 793828dbc29SManivannan Sadhasivam 794828dbc29SManivannan Sadhasivam for (i = 0; i < IMX290_NUM_SUPPLIES; i++) 795828dbc29SManivannan Sadhasivam imx290->supplies[i].supply = imx290_supply_name[i]; 796828dbc29SManivannan Sadhasivam 797828dbc29SManivannan Sadhasivam return devm_regulator_bulk_get(dev, IMX290_NUM_SUPPLIES, 798828dbc29SManivannan Sadhasivam imx290->supplies); 799828dbc29SManivannan Sadhasivam } 800828dbc29SManivannan Sadhasivam 80197589ad6SManivannan Sadhasivam static int imx290_set_data_lanes(struct imx290 *imx290) 80297589ad6SManivannan Sadhasivam { 80397589ad6SManivannan Sadhasivam int ret = 0, laneval, frsel; 80497589ad6SManivannan Sadhasivam 80597589ad6SManivannan Sadhasivam switch (imx290->nlanes) { 80697589ad6SManivannan Sadhasivam case 2: 80797589ad6SManivannan Sadhasivam laneval = 0x01; 80897589ad6SManivannan Sadhasivam frsel = 0x02; 80997589ad6SManivannan Sadhasivam break; 81097589ad6SManivannan Sadhasivam case 4: 81197589ad6SManivannan Sadhasivam laneval = 0x03; 81297589ad6SManivannan Sadhasivam frsel = 0x01; 81397589ad6SManivannan Sadhasivam break; 81497589ad6SManivannan Sadhasivam default: 81597589ad6SManivannan Sadhasivam /* 81697589ad6SManivannan Sadhasivam * We should never hit this since the data lane count is 81797589ad6SManivannan Sadhasivam * validated in probe itself 81897589ad6SManivannan Sadhasivam */ 81997589ad6SManivannan Sadhasivam dev_err(imx290->dev, "Lane configuration not supported\n"); 82097589ad6SManivannan Sadhasivam ret = -EINVAL; 82197589ad6SManivannan Sadhasivam goto exit; 82297589ad6SManivannan Sadhasivam } 82397589ad6SManivannan Sadhasivam 82497589ad6SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_PHY_LANE_NUM, laneval); 82597589ad6SManivannan Sadhasivam if (ret) { 82697589ad6SManivannan Sadhasivam dev_err(imx290->dev, "Error setting Physical Lane number register\n"); 82797589ad6SManivannan Sadhasivam goto exit; 82897589ad6SManivannan Sadhasivam } 82997589ad6SManivannan Sadhasivam 83097589ad6SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_CSI_LANE_MODE, laneval); 83197589ad6SManivannan Sadhasivam if (ret) { 83297589ad6SManivannan Sadhasivam dev_err(imx290->dev, "Error setting CSI Lane mode register\n"); 83397589ad6SManivannan Sadhasivam goto exit; 83497589ad6SManivannan Sadhasivam } 83597589ad6SManivannan Sadhasivam 83697589ad6SManivannan Sadhasivam ret = imx290_write_reg(imx290, IMX290_FR_FDG_SEL, frsel); 83797589ad6SManivannan Sadhasivam if (ret) 83897589ad6SManivannan Sadhasivam dev_err(imx290->dev, "Error setting FR/FDG SEL register\n"); 83997589ad6SManivannan Sadhasivam 84097589ad6SManivannan Sadhasivam exit: 84197589ad6SManivannan Sadhasivam return ret; 84297589ad6SManivannan Sadhasivam } 84397589ad6SManivannan Sadhasivam 844828dbc29SManivannan Sadhasivam static int imx290_power_on(struct device *dev) 845828dbc29SManivannan Sadhasivam { 846828dbc29SManivannan Sadhasivam struct i2c_client *client = to_i2c_client(dev); 847828dbc29SManivannan Sadhasivam struct v4l2_subdev *sd = i2c_get_clientdata(client); 848828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 849828dbc29SManivannan Sadhasivam int ret; 850828dbc29SManivannan Sadhasivam 851828dbc29SManivannan Sadhasivam ret = clk_prepare_enable(imx290->xclk); 852828dbc29SManivannan Sadhasivam if (ret) { 853828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Failed to enable clock\n"); 854828dbc29SManivannan Sadhasivam return ret; 855828dbc29SManivannan Sadhasivam } 856828dbc29SManivannan Sadhasivam 857828dbc29SManivannan Sadhasivam ret = regulator_bulk_enable(IMX290_NUM_SUPPLIES, imx290->supplies); 858828dbc29SManivannan Sadhasivam if (ret) { 859828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Failed to enable regulators\n"); 860828dbc29SManivannan Sadhasivam clk_disable_unprepare(imx290->xclk); 861828dbc29SManivannan Sadhasivam return ret; 862828dbc29SManivannan Sadhasivam } 863828dbc29SManivannan Sadhasivam 864828dbc29SManivannan Sadhasivam usleep_range(1, 2); 8653909a92dSAndrey Konovalov gpiod_set_value_cansleep(imx290->rst_gpio, 0); 866828dbc29SManivannan Sadhasivam usleep_range(30000, 31000); 867828dbc29SManivannan Sadhasivam 86897589ad6SManivannan Sadhasivam /* Set data lane count */ 86997589ad6SManivannan Sadhasivam imx290_set_data_lanes(imx290); 87097589ad6SManivannan Sadhasivam 871828dbc29SManivannan Sadhasivam return 0; 872828dbc29SManivannan Sadhasivam } 873828dbc29SManivannan Sadhasivam 874828dbc29SManivannan Sadhasivam static int imx290_power_off(struct device *dev) 875828dbc29SManivannan Sadhasivam { 876828dbc29SManivannan Sadhasivam struct i2c_client *client = to_i2c_client(dev); 877828dbc29SManivannan Sadhasivam struct v4l2_subdev *sd = i2c_get_clientdata(client); 878828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 879828dbc29SManivannan Sadhasivam 880828dbc29SManivannan Sadhasivam clk_disable_unprepare(imx290->xclk); 8813909a92dSAndrey Konovalov gpiod_set_value_cansleep(imx290->rst_gpio, 1); 882828dbc29SManivannan Sadhasivam regulator_bulk_disable(IMX290_NUM_SUPPLIES, imx290->supplies); 883828dbc29SManivannan Sadhasivam 884828dbc29SManivannan Sadhasivam return 0; 885828dbc29SManivannan Sadhasivam } 886828dbc29SManivannan Sadhasivam 887828dbc29SManivannan Sadhasivam static const struct dev_pm_ops imx290_pm_ops = { 8888d2d1bedSAndrey Konovalov SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) 889828dbc29SManivannan Sadhasivam }; 890828dbc29SManivannan Sadhasivam 891828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_video_ops imx290_video_ops = { 892828dbc29SManivannan Sadhasivam .s_stream = imx290_set_stream, 893828dbc29SManivannan Sadhasivam }; 894828dbc29SManivannan Sadhasivam 895828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_pad_ops imx290_pad_ops = { 896828dbc29SManivannan Sadhasivam .init_cfg = imx290_entity_init_cfg, 897828dbc29SManivannan Sadhasivam .enum_mbus_code = imx290_enum_mbus_code, 8983b867fb6SManivannan Sadhasivam .enum_frame_size = imx290_enum_frame_size, 899828dbc29SManivannan Sadhasivam .get_fmt = imx290_get_fmt, 900828dbc29SManivannan Sadhasivam .set_fmt = imx290_set_fmt, 901828dbc29SManivannan Sadhasivam }; 902828dbc29SManivannan Sadhasivam 903828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_ops imx290_subdev_ops = { 904828dbc29SManivannan Sadhasivam .video = &imx290_video_ops, 905828dbc29SManivannan Sadhasivam .pad = &imx290_pad_ops, 906828dbc29SManivannan Sadhasivam }; 907828dbc29SManivannan Sadhasivam 908828dbc29SManivannan Sadhasivam static const struct media_entity_operations imx290_subdev_entity_ops = { 909828dbc29SManivannan Sadhasivam .link_validate = v4l2_subdev_link_validate, 910828dbc29SManivannan Sadhasivam }; 911828dbc29SManivannan Sadhasivam 91298e0500eSManivannan Sadhasivam /* 91398e0500eSManivannan Sadhasivam * Returns 0 if all link frequencies used by the driver for the given number 91498e0500eSManivannan Sadhasivam * of MIPI data lanes are mentioned in the device tree, or the value of the 91598e0500eSManivannan Sadhasivam * first missing frequency otherwise. 91698e0500eSManivannan Sadhasivam */ 91798e0500eSManivannan Sadhasivam static s64 imx290_check_link_freqs(const struct imx290 *imx290) 91898e0500eSManivannan Sadhasivam { 91998e0500eSManivannan Sadhasivam int i, j; 92098e0500eSManivannan Sadhasivam const s64 *freqs = imx290_link_freqs_ptr(imx290); 92198e0500eSManivannan Sadhasivam int freqs_count = imx290_link_freqs_num(imx290); 92298e0500eSManivannan Sadhasivam 92398e0500eSManivannan Sadhasivam for (i = 0; i < freqs_count; i++) { 92498e0500eSManivannan Sadhasivam for (j = 0; j < imx290->ep.nr_of_link_frequencies; j++) 92598e0500eSManivannan Sadhasivam if (freqs[i] == imx290->ep.link_frequencies[j]) 92698e0500eSManivannan Sadhasivam break; 92798e0500eSManivannan Sadhasivam if (j == imx290->ep.nr_of_link_frequencies) 92898e0500eSManivannan Sadhasivam return freqs[i]; 92998e0500eSManivannan Sadhasivam } 93098e0500eSManivannan Sadhasivam return 0; 93198e0500eSManivannan Sadhasivam } 93298e0500eSManivannan Sadhasivam 933828dbc29SManivannan Sadhasivam static int imx290_probe(struct i2c_client *client) 934828dbc29SManivannan Sadhasivam { 935828dbc29SManivannan Sadhasivam struct device *dev = &client->dev; 936828dbc29SManivannan Sadhasivam struct fwnode_handle *endpoint; 937828dbc29SManivannan Sadhasivam struct imx290 *imx290; 938828dbc29SManivannan Sadhasivam u32 xclk_freq; 93998e0500eSManivannan Sadhasivam s64 fq; 940828dbc29SManivannan Sadhasivam int ret; 941828dbc29SManivannan Sadhasivam 942828dbc29SManivannan Sadhasivam imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); 943828dbc29SManivannan Sadhasivam if (!imx290) 944828dbc29SManivannan Sadhasivam return -ENOMEM; 945828dbc29SManivannan Sadhasivam 946828dbc29SManivannan Sadhasivam imx290->dev = dev; 947828dbc29SManivannan Sadhasivam imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config); 948828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->regmap)) { 949828dbc29SManivannan Sadhasivam dev_err(dev, "Unable to initialize I2C\n"); 950828dbc29SManivannan Sadhasivam return -ENODEV; 951828dbc29SManivannan Sadhasivam } 952828dbc29SManivannan Sadhasivam 953828dbc29SManivannan Sadhasivam endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); 954828dbc29SManivannan Sadhasivam if (!endpoint) { 955828dbc29SManivannan Sadhasivam dev_err(dev, "Endpoint node not found\n"); 956828dbc29SManivannan Sadhasivam return -EINVAL; 957828dbc29SManivannan Sadhasivam } 958828dbc29SManivannan Sadhasivam 959828dbc29SManivannan Sadhasivam ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &imx290->ep); 960828dbc29SManivannan Sadhasivam fwnode_handle_put(endpoint); 961828dbc29SManivannan Sadhasivam if (ret) { 962828dbc29SManivannan Sadhasivam dev_err(dev, "Parsing endpoint node failed\n"); 963828dbc29SManivannan Sadhasivam goto free_err; 964828dbc29SManivannan Sadhasivam } 965828dbc29SManivannan Sadhasivam 96697589ad6SManivannan Sadhasivam /* Get number of data lanes */ 96797589ad6SManivannan Sadhasivam imx290->nlanes = imx290->ep.bus.mipi_csi2.num_data_lanes; 96897589ad6SManivannan Sadhasivam if (imx290->nlanes != 2 && imx290->nlanes != 4) { 96997589ad6SManivannan Sadhasivam dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); 97097589ad6SManivannan Sadhasivam ret = -EINVAL; 97197589ad6SManivannan Sadhasivam goto free_err; 97297589ad6SManivannan Sadhasivam } 97397589ad6SManivannan Sadhasivam 97497589ad6SManivannan Sadhasivam dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); 97597589ad6SManivannan Sadhasivam 976828dbc29SManivannan Sadhasivam if (!imx290->ep.nr_of_link_frequencies) { 977828dbc29SManivannan Sadhasivam dev_err(dev, "link-frequency property not found in DT\n"); 978828dbc29SManivannan Sadhasivam ret = -EINVAL; 979828dbc29SManivannan Sadhasivam goto free_err; 980828dbc29SManivannan Sadhasivam } 981828dbc29SManivannan Sadhasivam 98298e0500eSManivannan Sadhasivam /* Check that link frequences for all the modes are in device tree */ 98398e0500eSManivannan Sadhasivam fq = imx290_check_link_freqs(imx290); 98498e0500eSManivannan Sadhasivam if (fq) { 98598e0500eSManivannan Sadhasivam dev_err(dev, "Link frequency of %lld is not supported\n", fq); 986828dbc29SManivannan Sadhasivam ret = -EINVAL; 987828dbc29SManivannan Sadhasivam goto free_err; 988828dbc29SManivannan Sadhasivam } 989828dbc29SManivannan Sadhasivam 990828dbc29SManivannan Sadhasivam /* Only CSI2 is supported for now */ 991828dbc29SManivannan Sadhasivam if (imx290->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { 992828dbc29SManivannan Sadhasivam dev_err(dev, "Unsupported bus type, should be CSI2\n"); 993828dbc29SManivannan Sadhasivam ret = -EINVAL; 994828dbc29SManivannan Sadhasivam goto free_err; 995828dbc29SManivannan Sadhasivam } 996828dbc29SManivannan Sadhasivam 997828dbc29SManivannan Sadhasivam /* get system clock (xclk) */ 998828dbc29SManivannan Sadhasivam imx290->xclk = devm_clk_get(dev, "xclk"); 999828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->xclk)) { 1000828dbc29SManivannan Sadhasivam dev_err(dev, "Could not get xclk"); 1001828dbc29SManivannan Sadhasivam ret = PTR_ERR(imx290->xclk); 1002828dbc29SManivannan Sadhasivam goto free_err; 1003828dbc29SManivannan Sadhasivam } 1004828dbc29SManivannan Sadhasivam 1005828dbc29SManivannan Sadhasivam ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", 1006828dbc29SManivannan Sadhasivam &xclk_freq); 1007828dbc29SManivannan Sadhasivam if (ret) { 1008828dbc29SManivannan Sadhasivam dev_err(dev, "Could not get xclk frequency\n"); 1009828dbc29SManivannan Sadhasivam goto free_err; 1010828dbc29SManivannan Sadhasivam } 1011828dbc29SManivannan Sadhasivam 1012828dbc29SManivannan Sadhasivam /* external clock must be 37.125 MHz */ 1013828dbc29SManivannan Sadhasivam if (xclk_freq != 37125000) { 1014828dbc29SManivannan Sadhasivam dev_err(dev, "External clock frequency %u is not supported\n", 1015828dbc29SManivannan Sadhasivam xclk_freq); 1016828dbc29SManivannan Sadhasivam ret = -EINVAL; 1017828dbc29SManivannan Sadhasivam goto free_err; 1018828dbc29SManivannan Sadhasivam } 1019828dbc29SManivannan Sadhasivam 1020828dbc29SManivannan Sadhasivam ret = clk_set_rate(imx290->xclk, xclk_freq); 1021828dbc29SManivannan Sadhasivam if (ret) { 1022828dbc29SManivannan Sadhasivam dev_err(dev, "Could not set xclk frequency\n"); 1023828dbc29SManivannan Sadhasivam goto free_err; 1024828dbc29SManivannan Sadhasivam } 1025828dbc29SManivannan Sadhasivam 1026828dbc29SManivannan Sadhasivam ret = imx290_get_regulators(dev, imx290); 1027828dbc29SManivannan Sadhasivam if (ret < 0) { 1028828dbc29SManivannan Sadhasivam dev_err(dev, "Cannot get regulators\n"); 1029828dbc29SManivannan Sadhasivam goto free_err; 1030828dbc29SManivannan Sadhasivam } 1031828dbc29SManivannan Sadhasivam 10323909a92dSAndrey Konovalov imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", 10333909a92dSAndrey Konovalov GPIOD_OUT_HIGH); 1034828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->rst_gpio)) { 1035828dbc29SManivannan Sadhasivam dev_err(dev, "Cannot get reset gpio\n"); 1036828dbc29SManivannan Sadhasivam ret = PTR_ERR(imx290->rst_gpio); 1037828dbc29SManivannan Sadhasivam goto free_err; 1038828dbc29SManivannan Sadhasivam } 1039828dbc29SManivannan Sadhasivam 1040828dbc29SManivannan Sadhasivam mutex_init(&imx290->lock); 1041828dbc29SManivannan Sadhasivam 104298e0500eSManivannan Sadhasivam /* 104398e0500eSManivannan Sadhasivam * Initialize the frame format. In particular, imx290->current_mode 104498e0500eSManivannan Sadhasivam * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call 104598e0500eSManivannan Sadhasivam * below relies on these fields. 104698e0500eSManivannan Sadhasivam */ 104798e0500eSManivannan Sadhasivam imx290_entity_init_cfg(&imx290->sd, NULL); 104898e0500eSManivannan Sadhasivam 1049a58df1f9SManivannan Sadhasivam v4l2_ctrl_handler_init(&imx290->ctrls, 4); 1050828dbc29SManivannan Sadhasivam 1051828dbc29SManivannan Sadhasivam v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 1052828dbc29SManivannan Sadhasivam V4L2_CID_GAIN, 0, 72, 1, 0); 105398e0500eSManivannan Sadhasivam 1054828dbc29SManivannan Sadhasivam imx290->link_freq = 105598e0500eSManivannan Sadhasivam v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, 1056828dbc29SManivannan Sadhasivam V4L2_CID_LINK_FREQ, 105798e0500eSManivannan Sadhasivam imx290_link_freqs_num(imx290) - 1, 0, 105898e0500eSManivannan Sadhasivam imx290_link_freqs_ptr(imx290)); 1059828dbc29SManivannan Sadhasivam if (imx290->link_freq) 1060828dbc29SManivannan Sadhasivam imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; 1061828dbc29SManivannan Sadhasivam 1062828dbc29SManivannan Sadhasivam imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 106398e0500eSManivannan Sadhasivam V4L2_CID_PIXEL_RATE, 106498e0500eSManivannan Sadhasivam 1, INT_MAX, 1, 106598e0500eSManivannan Sadhasivam imx290_calc_pixel_rate(imx290)); 1066828dbc29SManivannan Sadhasivam 1067a58df1f9SManivannan Sadhasivam v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, 1068a58df1f9SManivannan Sadhasivam V4L2_CID_TEST_PATTERN, 1069a58df1f9SManivannan Sadhasivam ARRAY_SIZE(imx290_test_pattern_menu) - 1, 1070a58df1f9SManivannan Sadhasivam 0, 0, imx290_test_pattern_menu); 1071a58df1f9SManivannan Sadhasivam 1072828dbc29SManivannan Sadhasivam imx290->sd.ctrl_handler = &imx290->ctrls; 1073828dbc29SManivannan Sadhasivam 1074828dbc29SManivannan Sadhasivam if (imx290->ctrls.error) { 1075828dbc29SManivannan Sadhasivam dev_err(dev, "Control initialization error %d\n", 1076828dbc29SManivannan Sadhasivam imx290->ctrls.error); 1077828dbc29SManivannan Sadhasivam ret = imx290->ctrls.error; 1078828dbc29SManivannan Sadhasivam goto free_ctrl; 1079828dbc29SManivannan Sadhasivam } 1080828dbc29SManivannan Sadhasivam 1081828dbc29SManivannan Sadhasivam v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); 1082828dbc29SManivannan Sadhasivam imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1083828dbc29SManivannan Sadhasivam imx290->sd.dev = &client->dev; 1084828dbc29SManivannan Sadhasivam imx290->sd.entity.ops = &imx290_subdev_entity_ops; 1085828dbc29SManivannan Sadhasivam imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 1086828dbc29SManivannan Sadhasivam 1087828dbc29SManivannan Sadhasivam imx290->pad.flags = MEDIA_PAD_FL_SOURCE; 1088828dbc29SManivannan Sadhasivam ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad); 1089828dbc29SManivannan Sadhasivam if (ret < 0) { 1090828dbc29SManivannan Sadhasivam dev_err(dev, "Could not register media entity\n"); 1091828dbc29SManivannan Sadhasivam goto free_ctrl; 1092828dbc29SManivannan Sadhasivam } 1093828dbc29SManivannan Sadhasivam 1094828dbc29SManivannan Sadhasivam ret = v4l2_async_register_subdev(&imx290->sd); 1095828dbc29SManivannan Sadhasivam if (ret < 0) { 1096828dbc29SManivannan Sadhasivam dev_err(dev, "Could not register v4l2 device\n"); 1097828dbc29SManivannan Sadhasivam goto free_entity; 1098828dbc29SManivannan Sadhasivam } 1099828dbc29SManivannan Sadhasivam 1100828dbc29SManivannan Sadhasivam /* Power on the device to match runtime PM state below */ 1101828dbc29SManivannan Sadhasivam ret = imx290_power_on(dev); 1102828dbc29SManivannan Sadhasivam if (ret < 0) { 1103828dbc29SManivannan Sadhasivam dev_err(dev, "Could not power on the device\n"); 1104828dbc29SManivannan Sadhasivam goto free_entity; 1105828dbc29SManivannan Sadhasivam } 1106828dbc29SManivannan Sadhasivam 1107828dbc29SManivannan Sadhasivam pm_runtime_set_active(dev); 1108828dbc29SManivannan Sadhasivam pm_runtime_enable(dev); 1109828dbc29SManivannan Sadhasivam pm_runtime_idle(dev); 1110828dbc29SManivannan Sadhasivam 1111828dbc29SManivannan Sadhasivam v4l2_fwnode_endpoint_free(&imx290->ep); 1112828dbc29SManivannan Sadhasivam 1113828dbc29SManivannan Sadhasivam return 0; 1114828dbc29SManivannan Sadhasivam 1115828dbc29SManivannan Sadhasivam free_entity: 1116828dbc29SManivannan Sadhasivam media_entity_cleanup(&imx290->sd.entity); 1117828dbc29SManivannan Sadhasivam free_ctrl: 1118828dbc29SManivannan Sadhasivam v4l2_ctrl_handler_free(&imx290->ctrls); 1119828dbc29SManivannan Sadhasivam mutex_destroy(&imx290->lock); 1120828dbc29SManivannan Sadhasivam free_err: 1121828dbc29SManivannan Sadhasivam v4l2_fwnode_endpoint_free(&imx290->ep); 1122828dbc29SManivannan Sadhasivam 1123828dbc29SManivannan Sadhasivam return ret; 1124828dbc29SManivannan Sadhasivam } 1125828dbc29SManivannan Sadhasivam 1126828dbc29SManivannan Sadhasivam static int imx290_remove(struct i2c_client *client) 1127828dbc29SManivannan Sadhasivam { 1128828dbc29SManivannan Sadhasivam struct v4l2_subdev *sd = i2c_get_clientdata(client); 1129828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 1130828dbc29SManivannan Sadhasivam 1131828dbc29SManivannan Sadhasivam v4l2_async_unregister_subdev(sd); 1132828dbc29SManivannan Sadhasivam media_entity_cleanup(&sd->entity); 1133828dbc29SManivannan Sadhasivam v4l2_ctrl_handler_free(sd->ctrl_handler); 1134828dbc29SManivannan Sadhasivam 1135828dbc29SManivannan Sadhasivam mutex_destroy(&imx290->lock); 1136828dbc29SManivannan Sadhasivam 1137828dbc29SManivannan Sadhasivam pm_runtime_disable(imx290->dev); 1138828dbc29SManivannan Sadhasivam if (!pm_runtime_status_suspended(imx290->dev)) 1139828dbc29SManivannan Sadhasivam imx290_power_off(imx290->dev); 1140828dbc29SManivannan Sadhasivam pm_runtime_set_suspended(imx290->dev); 1141828dbc29SManivannan Sadhasivam 1142828dbc29SManivannan Sadhasivam return 0; 1143828dbc29SManivannan Sadhasivam } 1144828dbc29SManivannan Sadhasivam 1145828dbc29SManivannan Sadhasivam static const struct of_device_id imx290_of_match[] = { 1146828dbc29SManivannan Sadhasivam { .compatible = "sony,imx290" }, 1147828dbc29SManivannan Sadhasivam { /* sentinel */ } 1148828dbc29SManivannan Sadhasivam }; 1149828dbc29SManivannan Sadhasivam MODULE_DEVICE_TABLE(of, imx290_of_match); 1150828dbc29SManivannan Sadhasivam 1151828dbc29SManivannan Sadhasivam static struct i2c_driver imx290_i2c_driver = { 1152828dbc29SManivannan Sadhasivam .probe_new = imx290_probe, 1153828dbc29SManivannan Sadhasivam .remove = imx290_remove, 1154828dbc29SManivannan Sadhasivam .driver = { 1155828dbc29SManivannan Sadhasivam .name = "imx290", 1156828dbc29SManivannan Sadhasivam .pm = &imx290_pm_ops, 1157828dbc29SManivannan Sadhasivam .of_match_table = of_match_ptr(imx290_of_match), 1158828dbc29SManivannan Sadhasivam }, 1159828dbc29SManivannan Sadhasivam }; 1160828dbc29SManivannan Sadhasivam 1161828dbc29SManivannan Sadhasivam module_i2c_driver(imx290_i2c_driver); 1162828dbc29SManivannan Sadhasivam 1163828dbc29SManivannan Sadhasivam MODULE_DESCRIPTION("Sony IMX290 CMOS Image Sensor Driver"); 1164828dbc29SManivannan Sadhasivam MODULE_AUTHOR("FRAMOS GmbH"); 1165828dbc29SManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 1166828dbc29SManivannan Sadhasivam MODULE_LICENSE("GPL v2"); 1167