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