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 25e70abe88SLaurent Pinchart #define IMX290_REG_SIZE_SHIFT 16 26e70abe88SLaurent Pinchart #define IMX290_REG_ADDR_MASK 0xffff 27e70abe88SLaurent Pinchart #define IMX290_REG_8BIT(n) ((1U << IMX290_REG_SIZE_SHIFT) | (n)) 28e70abe88SLaurent Pinchart #define IMX290_REG_16BIT(n) ((2U << IMX290_REG_SIZE_SHIFT) | (n)) 29e70abe88SLaurent Pinchart #define IMX290_REG_24BIT(n) ((3U << IMX290_REG_SIZE_SHIFT) | (n)) 30e70abe88SLaurent Pinchart 31e70abe88SLaurent Pinchart #define IMX290_STANDBY IMX290_REG_8BIT(0x3000) 32e70abe88SLaurent Pinchart #define IMX290_REGHOLD IMX290_REG_8BIT(0x3001) 33e70abe88SLaurent Pinchart #define IMX290_XMSTA IMX290_REG_8BIT(0x3002) 3479d99ae8SLaurent Pinchart #define IMX290_ADBIT IMX290_REG_8BIT(0x3005) 3579d99ae8SLaurent Pinchart #define IMX290_ADBIT_10BIT (0 << 0) 3679d99ae8SLaurent Pinchart #define IMX290_ADBIT_12BIT (1 << 0) 3779d99ae8SLaurent Pinchart #define IMX290_CTRL_07 IMX290_REG_8BIT(0x3007) 3879d99ae8SLaurent Pinchart #define IMX290_VREVERSE BIT(0) 3979d99ae8SLaurent Pinchart #define IMX290_HREVERSE BIT(1) 4079d99ae8SLaurent Pinchart #define IMX290_WINMODE_1080P (0 << 4) 4179d99ae8SLaurent Pinchart #define IMX290_WINMODE_720P (1 << 4) 4279d99ae8SLaurent Pinchart #define IMX290_WINMODE_CROP (4 << 4) 43e70abe88SLaurent Pinchart #define IMX290_FR_FDG_SEL IMX290_REG_8BIT(0x3009) 44454a86f3SLaurent Pinchart #define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a) 45e70abe88SLaurent Pinchart #define IMX290_GAIN IMX290_REG_8BIT(0x3014) 4679d99ae8SLaurent Pinchart #define IMX290_VMAX IMX290_REG_24BIT(0x3018) 47454a86f3SLaurent Pinchart #define IMX290_HMAX IMX290_REG_16BIT(0x301c) 4879d99ae8SLaurent Pinchart #define IMX290_SHS1 IMX290_REG_24BIT(0x3020) 4979d99ae8SLaurent Pinchart #define IMX290_WINWV_OB IMX290_REG_8BIT(0x303a) 5079d99ae8SLaurent Pinchart #define IMX290_WINPV IMX290_REG_16BIT(0x303c) 5179d99ae8SLaurent Pinchart #define IMX290_WINWV IMX290_REG_16BIT(0x303e) 5279d99ae8SLaurent Pinchart #define IMX290_WINPH IMX290_REG_16BIT(0x3040) 5379d99ae8SLaurent Pinchart #define IMX290_WINWH IMX290_REG_16BIT(0x3042) 5479d99ae8SLaurent Pinchart #define IMX290_OUT_CTRL IMX290_REG_8BIT(0x3046) 5579d99ae8SLaurent Pinchart #define IMX290_ODBIT_10BIT (0 << 0) 5679d99ae8SLaurent Pinchart #define IMX290_ODBIT_12BIT (1 << 0) 5779d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_PARALLEL (0x0 << 4) 5879d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_LVDS_2CH (0xd << 4) 5979d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_LVDS_4CH (0xe << 4) 6079d99ae8SLaurent Pinchart #define IMX290_OPORTSEL_LVDS_8CH (0xf << 4) 6179d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL IMX290_REG_8BIT(0x304b) 6279d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XVSOUTSEL_HIGH (0 << 0) 6379d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XVSOUTSEL_VSYNC (2 << 0) 6479d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XHSOUTSEL_HIGH (0 << 2) 6579d99ae8SLaurent Pinchart #define IMX290_XSOUTSEL_XHSOUTSEL_HSYNC (2 << 2) 6679d99ae8SLaurent Pinchart #define IMX290_INCKSEL1 IMX290_REG_8BIT(0x305c) 6779d99ae8SLaurent Pinchart #define IMX290_INCKSEL2 IMX290_REG_8BIT(0x305d) 6879d99ae8SLaurent Pinchart #define IMX290_INCKSEL3 IMX290_REG_8BIT(0x305e) 6979d99ae8SLaurent Pinchart #define IMX290_INCKSEL4 IMX290_REG_8BIT(0x305f) 70e70abe88SLaurent Pinchart #define IMX290_PGCTRL IMX290_REG_8BIT(0x308c) 7179d99ae8SLaurent Pinchart #define IMX290_ADBIT1 IMX290_REG_8BIT(0x3129) 7279d99ae8SLaurent Pinchart #define IMX290_ADBIT1_10BIT 0x1d 7379d99ae8SLaurent Pinchart #define IMX290_ADBIT1_12BIT 0x00 7479d99ae8SLaurent Pinchart #define IMX290_INCKSEL5 IMX290_REG_8BIT(0x315e) 7579d99ae8SLaurent Pinchart #define IMX290_INCKSEL6 IMX290_REG_8BIT(0x3164) 7679d99ae8SLaurent Pinchart #define IMX290_ADBIT2 IMX290_REG_8BIT(0x317c) 7779d99ae8SLaurent Pinchart #define IMX290_ADBIT2_10BIT 0x12 7879d99ae8SLaurent Pinchart #define IMX290_ADBIT2_12BIT 0x00 79454a86f3SLaurent Pinchart #define IMX290_CHIP_ID IMX290_REG_16BIT(0x319a) 8079d99ae8SLaurent Pinchart #define IMX290_ADBIT3 IMX290_REG_8BIT(0x31ec) 8179d99ae8SLaurent Pinchart #define IMX290_ADBIT3_10BIT 0x37 8279d99ae8SLaurent Pinchart #define IMX290_ADBIT3_12BIT 0x0e 8379d99ae8SLaurent Pinchart #define IMX290_REPETITION IMX290_REG_8BIT(0x3405) 84e70abe88SLaurent Pinchart #define IMX290_PHY_LANE_NUM IMX290_REG_8BIT(0x3407) 8579d99ae8SLaurent Pinchart #define IMX290_OPB_SIZE_V IMX290_REG_8BIT(0x3414) 8679d99ae8SLaurent Pinchart #define IMX290_Y_OUT_SIZE IMX290_REG_16BIT(0x3418) 8779d99ae8SLaurent Pinchart #define IMX290_CSI_DT_FMT IMX290_REG_16BIT(0x3441) 8879d99ae8SLaurent Pinchart #define IMX290_CSI_DT_FMT_RAW10 0x0a0a 8979d99ae8SLaurent Pinchart #define IMX290_CSI_DT_FMT_RAW12 0x0c0c 90e70abe88SLaurent Pinchart #define IMX290_CSI_LANE_MODE IMX290_REG_8BIT(0x3443) 9179d99ae8SLaurent Pinchart #define IMX290_EXTCK_FREQ IMX290_REG_16BIT(0x3444) 9279d99ae8SLaurent Pinchart #define IMX290_TCLKPOST IMX290_REG_16BIT(0x3446) 9379d99ae8SLaurent Pinchart #define IMX290_THSZERO IMX290_REG_16BIT(0x3448) 9479d99ae8SLaurent Pinchart #define IMX290_THSPREPARE IMX290_REG_16BIT(0x344a) 9579d99ae8SLaurent Pinchart #define IMX290_TCLKTRAIL IMX290_REG_16BIT(0x344c) 9679d99ae8SLaurent Pinchart #define IMX290_THSTRAIL IMX290_REG_16BIT(0x344e) 9779d99ae8SLaurent Pinchart #define IMX290_TCLKZERO IMX290_REG_16BIT(0x3450) 9879d99ae8SLaurent Pinchart #define IMX290_TCLKPREPARE IMX290_REG_16BIT(0x3452) 9979d99ae8SLaurent Pinchart #define IMX290_TLPX IMX290_REG_16BIT(0x3454) 10079d99ae8SLaurent Pinchart #define IMX290_X_OUT_SIZE IMX290_REG_16BIT(0x3472) 101828dbc29SManivannan Sadhasivam 102a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_REGEN BIT(0) 103a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_THRU BIT(1) 104a58df1f9SManivannan Sadhasivam #define IMX290_PGCTRL_MODE(n) ((n) << 4) 105a58df1f9SManivannan Sadhasivam 106827c7e69SLaurent Pinchart #define IMX290_VMAX_DEFAULT 1125 107827c7e69SLaurent Pinchart 108b4ab57b0SLaurent Pinchart 109b4ab57b0SLaurent Pinchart /* 110b4ab57b0SLaurent Pinchart * The IMX290 pixel array is organized as follows: 111b4ab57b0SLaurent Pinchart * 112b4ab57b0SLaurent Pinchart * +------------------------------------+ 113b4ab57b0SLaurent Pinchart * | Optical Black | } Vertical effective optical black (10) 114b4ab57b0SLaurent Pinchart * +---+------------------------------------+---+ 115b4ab57b0SLaurent Pinchart * | | | | } Effective top margin (8) 116b4ab57b0SLaurent Pinchart * | | +----------------------------+ | | \ 117b4ab57b0SLaurent Pinchart * | | | | | | | 118b4ab57b0SLaurent Pinchart * | | | | | | | 119b4ab57b0SLaurent Pinchart * | | | | | | | 120b4ab57b0SLaurent Pinchart * | | | Recording Pixel Area | | | | Recommended height (1080) 121b4ab57b0SLaurent Pinchart * | | | | | | | 122b4ab57b0SLaurent Pinchart * | | | | | | | 123b4ab57b0SLaurent Pinchart * | | | | | | | 124b4ab57b0SLaurent Pinchart * | | +----------------------------+ | | / 125b4ab57b0SLaurent Pinchart * | | | | } Effective bottom margin (9) 126b4ab57b0SLaurent Pinchart * +---+------------------------------------+---+ 127b4ab57b0SLaurent Pinchart * <-> <-> <--------------------------> <-> <-> 128b4ab57b0SLaurent Pinchart * \---- Ignored right margin (4) 129b4ab57b0SLaurent Pinchart * \-------- Effective right margin (9) 130b4ab57b0SLaurent Pinchart * \------------------------- Recommended width (1920) 131b4ab57b0SLaurent Pinchart * \----------------------------------------- Effective left margin (8) 132b4ab57b0SLaurent Pinchart * \--------------------------------------------- Ignored left margin (4) 133b4ab57b0SLaurent Pinchart * 134b4ab57b0SLaurent Pinchart * The optical black lines are output over CSI-2 with a separate data type. 135b4ab57b0SLaurent Pinchart * 136b4ab57b0SLaurent Pinchart * The pixel array is meant to have 1920x1080 usable pixels after image 137b4ab57b0SLaurent Pinchart * processing in an ISP. It has 8 (9) extra active pixels usable for color 138b4ab57b0SLaurent Pinchart * processing in the ISP on the top and left (bottom and right) sides of the 139b4ab57b0SLaurent Pinchart * image. In addition, 4 additional pixels are present on the left and right 140b4ab57b0SLaurent Pinchart * sides of the image, documented as "ignored area". 141b4ab57b0SLaurent Pinchart * 142b4ab57b0SLaurent Pinchart * As far as is understood, all pixels of the pixel array (ignored area, color 143b4ab57b0SLaurent Pinchart * processing margins and recording area) can be output by the sensor. 144b4ab57b0SLaurent Pinchart */ 145b4ab57b0SLaurent Pinchart 146b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_WIDTH 1945 147b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_HEIGHT 1097 148b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_LEFT 12 149b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_RIGHT 13 150b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_TOP 8 151b4ab57b0SLaurent Pinchart #define IMX920_PIXEL_ARRAY_MARGIN_BOTTOM 9 152b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH 1920 153b4ab57b0SLaurent Pinchart #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT 1080 154b4ab57b0SLaurent Pinchart 155cb7e1c8dSLaurent Pinchart #define IMX290_NUM_SUPPLIES 3 156828dbc29SManivannan Sadhasivam 157828dbc29SManivannan Sadhasivam struct imx290_regval { 158e70abe88SLaurent Pinchart u32 reg; 15979d99ae8SLaurent Pinchart u32 val; 160828dbc29SManivannan Sadhasivam }; 161828dbc29SManivannan Sadhasivam 162828dbc29SManivannan Sadhasivam struct imx290_mode { 163828dbc29SManivannan Sadhasivam u32 width; 164828dbc29SManivannan Sadhasivam u32 height; 16597589ad6SManivannan Sadhasivam u32 hmax; 16698e0500eSManivannan Sadhasivam u8 link_freq_index; 167828dbc29SManivannan Sadhasivam 168828dbc29SManivannan Sadhasivam const struct imx290_regval *data; 169828dbc29SManivannan Sadhasivam u32 data_size; 170828dbc29SManivannan Sadhasivam }; 171828dbc29SManivannan Sadhasivam 172828dbc29SManivannan Sadhasivam struct imx290 { 173828dbc29SManivannan Sadhasivam struct device *dev; 174828dbc29SManivannan Sadhasivam struct clk *xclk; 175828dbc29SManivannan Sadhasivam struct regmap *regmap; 17697589ad6SManivannan Sadhasivam u8 nlanes; 177c566ac01SManivannan Sadhasivam u8 bpp; 178828dbc29SManivannan Sadhasivam 179828dbc29SManivannan Sadhasivam struct v4l2_subdev sd; 180828dbc29SManivannan Sadhasivam struct media_pad pad; 181828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt current_format; 182828dbc29SManivannan Sadhasivam const struct imx290_mode *current_mode; 183828dbc29SManivannan Sadhasivam 184828dbc29SManivannan Sadhasivam struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES]; 185828dbc29SManivannan Sadhasivam struct gpio_desc *rst_gpio; 186828dbc29SManivannan Sadhasivam 187828dbc29SManivannan Sadhasivam struct v4l2_ctrl_handler ctrls; 188828dbc29SManivannan Sadhasivam struct v4l2_ctrl *link_freq; 189828dbc29SManivannan Sadhasivam struct v4l2_ctrl *pixel_rate; 1900c3b56c9SLaurent Pinchart struct v4l2_ctrl *hblank; 1910c3b56c9SLaurent Pinchart struct v4l2_ctrl *vblank; 192828dbc29SManivannan Sadhasivam 193828dbc29SManivannan Sadhasivam struct mutex lock; 194828dbc29SManivannan Sadhasivam }; 195828dbc29SManivannan Sadhasivam 196cb7e1c8dSLaurent Pinchart static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) 197cb7e1c8dSLaurent Pinchart { 198cb7e1c8dSLaurent Pinchart return container_of(_sd, struct imx290, sd); 199cb7e1c8dSLaurent Pinchart } 200828dbc29SManivannan Sadhasivam 201cb7e1c8dSLaurent Pinchart /* ----------------------------------------------------------------------------- 202cb7e1c8dSLaurent Pinchart * Modes and formats 203cb7e1c8dSLaurent Pinchart */ 204a58df1f9SManivannan Sadhasivam 205828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_global_init_settings[] = { 20679d99ae8SLaurent Pinchart { IMX290_CTRL_07, IMX290_WINMODE_1080P }, 207827c7e69SLaurent Pinchart { IMX290_VMAX, IMX290_VMAX_DEFAULT }, 20879d99ae8SLaurent Pinchart { IMX290_EXTCK_FREQ, 0x2520 }, 20979d99ae8SLaurent Pinchart { IMX290_WINWV_OB, 12 }, 21079d99ae8SLaurent Pinchart { IMX290_WINPH, 0 }, 21179d99ae8SLaurent Pinchart { IMX290_WINPV, 0 }, 21279d99ae8SLaurent Pinchart { IMX290_WINWH, 1948 }, 21379d99ae8SLaurent Pinchart { IMX290_WINWV, 1097 }, 21479d99ae8SLaurent Pinchart { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | 21579d99ae8SLaurent Pinchart IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, 216e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x300f), 0x00 }, 217e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3010), 0x21 }, 218e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3012), 0x64 }, 2190b274ef2SLaurent Pinchart { IMX290_REG_8BIT(0x3013), 0x00 }, 220e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3016), 0x09 }, 221e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3070), 0x02 }, 222e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3071), 0x11 }, 223e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x309b), 0x10 }, 224e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x309c), 0x22 }, 225e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30a2), 0x02 }, 226e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30a6), 0x20 }, 227e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30a8), 0x20 }, 228e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30aa), 0x20 }, 229e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30ac), 0x20 }, 230e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30b0), 0x43 }, 231e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3119), 0x9e }, 232e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x311c), 0x1e }, 233e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x311e), 0x08 }, 234e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3128), 0x05 }, 235e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x313d), 0x83 }, 236e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3150), 0x03 }, 237e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x317e), 0x00 }, 238e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32b8), 0x50 }, 239e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32b9), 0x10 }, 240e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32ba), 0x00 }, 241e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32bb), 0x04 }, 242e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32c8), 0x50 }, 243e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32c9), 0x10 }, 244e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32ca), 0x00 }, 245e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32cb), 0x04 }, 246e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x332c), 0xd3 }, 247e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x332d), 0x10 }, 248e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x332e), 0x0d }, 249e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3358), 0x06 }, 250e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3359), 0xe1 }, 251e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x335a), 0x11 }, 252e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3360), 0x1e }, 253e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3361), 0x61 }, 254e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3362), 0x10 }, 255e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x33b0), 0x50 }, 256e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x33b2), 0x1a }, 257e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x33b3), 0x04 }, 2580b274ef2SLaurent Pinchart { IMX290_REG_8BIT(0x3480), 0x49 }, 259828dbc29SManivannan Sadhasivam }; 260828dbc29SManivannan Sadhasivam 261828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_1080p_settings[] = { 262828dbc29SManivannan Sadhasivam /* mode settings */ 26379d99ae8SLaurent Pinchart { IMX290_CTRL_07, IMX290_WINMODE_1080P }, 26479d99ae8SLaurent Pinchart { IMX290_WINWV_OB, 12 }, 26579d99ae8SLaurent Pinchart { IMX290_OPB_SIZE_V, 10 }, 26679d99ae8SLaurent Pinchart { IMX290_X_OUT_SIZE, 1920 }, 26779d99ae8SLaurent Pinchart { IMX290_Y_OUT_SIZE, 1080 }, 26879d99ae8SLaurent Pinchart { IMX290_INCKSEL1, 0x18 }, 26979d99ae8SLaurent Pinchart { IMX290_INCKSEL2, 0x03 }, 27079d99ae8SLaurent Pinchart { IMX290_INCKSEL3, 0x20 }, 27179d99ae8SLaurent Pinchart { IMX290_INCKSEL4, 0x01 }, 27279d99ae8SLaurent Pinchart { IMX290_INCKSEL5, 0x1a }, 27379d99ae8SLaurent Pinchart { IMX290_INCKSEL6, 0x1a }, 274828dbc29SManivannan Sadhasivam /* data rate settings */ 27579d99ae8SLaurent Pinchart { IMX290_REPETITION, 0x10 }, 27679d99ae8SLaurent Pinchart { IMX290_TCLKPOST, 87 }, 27779d99ae8SLaurent Pinchart { IMX290_THSZERO, 55 }, 27879d99ae8SLaurent Pinchart { IMX290_THSPREPARE, 31 }, 27979d99ae8SLaurent Pinchart { IMX290_TCLKTRAIL, 31 }, 28079d99ae8SLaurent Pinchart { IMX290_THSTRAIL, 31 }, 28179d99ae8SLaurent Pinchart { IMX290_TCLKZERO, 119 }, 28279d99ae8SLaurent Pinchart { IMX290_TCLKPREPARE, 31 }, 28379d99ae8SLaurent Pinchart { IMX290_TLPX, 23 }, 284828dbc29SManivannan Sadhasivam }; 285828dbc29SManivannan Sadhasivam 286828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_720p_settings[] = { 287828dbc29SManivannan Sadhasivam /* mode settings */ 28879d99ae8SLaurent Pinchart { IMX290_CTRL_07, IMX290_WINMODE_720P }, 28979d99ae8SLaurent Pinchart { IMX290_WINWV_OB, 6 }, 29079d99ae8SLaurent Pinchart { IMX290_OPB_SIZE_V, 4 }, 29179d99ae8SLaurent Pinchart { IMX290_X_OUT_SIZE, 1280 }, 29279d99ae8SLaurent Pinchart { IMX290_Y_OUT_SIZE, 720 }, 29379d99ae8SLaurent Pinchart { IMX290_INCKSEL1, 0x20 }, 29479d99ae8SLaurent Pinchart { IMX290_INCKSEL2, 0x00 }, 29579d99ae8SLaurent Pinchart { IMX290_INCKSEL3, 0x20 }, 29679d99ae8SLaurent Pinchart { IMX290_INCKSEL4, 0x01 }, 29779d99ae8SLaurent Pinchart { IMX290_INCKSEL5, 0x1a }, 29879d99ae8SLaurent Pinchart { IMX290_INCKSEL6, 0x1a }, 299828dbc29SManivannan Sadhasivam /* data rate settings */ 30079d99ae8SLaurent Pinchart { IMX290_REPETITION, 0x10 }, 30179d99ae8SLaurent Pinchart { IMX290_TCLKPOST, 79 }, 30279d99ae8SLaurent Pinchart { IMX290_THSZERO, 47 }, 30379d99ae8SLaurent Pinchart { IMX290_THSPREPARE, 23 }, 30479d99ae8SLaurent Pinchart { IMX290_TCLKTRAIL, 23 }, 30579d99ae8SLaurent Pinchart { IMX290_THSTRAIL, 23 }, 30679d99ae8SLaurent Pinchart { IMX290_TCLKZERO, 87 }, 30779d99ae8SLaurent Pinchart { IMX290_TCLKPREPARE, 23 }, 30879d99ae8SLaurent Pinchart { IMX290_TLPX, 23 }, 309828dbc29SManivannan Sadhasivam }; 310828dbc29SManivannan Sadhasivam 311828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_10bit_settings[] = { 31279d99ae8SLaurent Pinchart { IMX290_ADBIT, IMX290_ADBIT_10BIT }, 31379d99ae8SLaurent Pinchart { IMX290_OUT_CTRL, IMX290_ODBIT_10BIT }, 31479d99ae8SLaurent Pinchart { IMX290_ADBIT1, IMX290_ADBIT1_10BIT }, 31579d99ae8SLaurent Pinchart { IMX290_ADBIT2, IMX290_ADBIT2_10BIT }, 31679d99ae8SLaurent Pinchart { IMX290_ADBIT3, IMX290_ADBIT3_10BIT }, 31779d99ae8SLaurent Pinchart { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 }, 31879d99ae8SLaurent Pinchart { IMX290_BLKLEVEL, 60 }, 319828dbc29SManivannan Sadhasivam }; 320828dbc29SManivannan Sadhasivam 321c566ac01SManivannan Sadhasivam static const struct imx290_regval imx290_12bit_settings[] = { 32279d99ae8SLaurent Pinchart { IMX290_ADBIT, IMX290_ADBIT_12BIT }, 32379d99ae8SLaurent Pinchart { IMX290_OUT_CTRL, IMX290_ODBIT_12BIT }, 32479d99ae8SLaurent Pinchart { IMX290_ADBIT1, IMX290_ADBIT1_12BIT }, 32579d99ae8SLaurent Pinchart { IMX290_ADBIT2, IMX290_ADBIT2_12BIT }, 32679d99ae8SLaurent Pinchart { IMX290_ADBIT3, IMX290_ADBIT3_12BIT }, 32779d99ae8SLaurent Pinchart { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 }, 32879d99ae8SLaurent Pinchart { IMX290_BLKLEVEL, 240 }, 329c566ac01SManivannan Sadhasivam }; 330c566ac01SManivannan Sadhasivam 331828dbc29SManivannan Sadhasivam /* supported link frequencies */ 33298e0500eSManivannan Sadhasivam #define FREQ_INDEX_1080P 0 33398e0500eSManivannan Sadhasivam #define FREQ_INDEX_720P 1 33498e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_2lanes[] = { 33598e0500eSManivannan Sadhasivam [FREQ_INDEX_1080P] = 445500000, 33698e0500eSManivannan Sadhasivam [FREQ_INDEX_720P] = 297000000, 337828dbc29SManivannan Sadhasivam }; 33898e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_4lanes[] = { 33998e0500eSManivannan Sadhasivam [FREQ_INDEX_1080P] = 222750000, 34098e0500eSManivannan Sadhasivam [FREQ_INDEX_720P] = 148500000, 34198e0500eSManivannan Sadhasivam }; 34298e0500eSManivannan Sadhasivam 34398e0500eSManivannan Sadhasivam /* 34498e0500eSManivannan Sadhasivam * In this function and in the similar ones below We rely on imx290_probe() 34598e0500eSManivannan Sadhasivam * to ensure that nlanes is either 2 or 4. 34698e0500eSManivannan Sadhasivam */ 34798e0500eSManivannan Sadhasivam static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290) 34898e0500eSManivannan Sadhasivam { 34998e0500eSManivannan Sadhasivam if (imx290->nlanes == 2) 35098e0500eSManivannan Sadhasivam return imx290_link_freq_2lanes; 35198e0500eSManivannan Sadhasivam else 35298e0500eSManivannan Sadhasivam return imx290_link_freq_4lanes; 35398e0500eSManivannan Sadhasivam } 35498e0500eSManivannan Sadhasivam 35598e0500eSManivannan Sadhasivam static inline int imx290_link_freqs_num(const struct imx290 *imx290) 35698e0500eSManivannan Sadhasivam { 35798e0500eSManivannan Sadhasivam if (imx290->nlanes == 2) 35898e0500eSManivannan Sadhasivam return ARRAY_SIZE(imx290_link_freq_2lanes); 35998e0500eSManivannan Sadhasivam else 36098e0500eSManivannan Sadhasivam return ARRAY_SIZE(imx290_link_freq_4lanes); 36198e0500eSManivannan Sadhasivam } 362828dbc29SManivannan Sadhasivam 363828dbc29SManivannan Sadhasivam /* Mode configs */ 36497589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_2lanes[] = { 365828dbc29SManivannan Sadhasivam { 366828dbc29SManivannan Sadhasivam .width = 1920, 367828dbc29SManivannan Sadhasivam .height = 1080, 36872825bc6SLaurent Pinchart .hmax = 4400, 36998e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_1080P, 370828dbc29SManivannan Sadhasivam .data = imx290_1080p_settings, 371828dbc29SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_1080p_settings), 372828dbc29SManivannan Sadhasivam }, 373828dbc29SManivannan Sadhasivam { 374828dbc29SManivannan Sadhasivam .width = 1280, 375828dbc29SManivannan Sadhasivam .height = 720, 37672825bc6SLaurent Pinchart .hmax = 6600, 37798e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_720P, 378828dbc29SManivannan Sadhasivam .data = imx290_720p_settings, 379828dbc29SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_720p_settings), 380828dbc29SManivannan Sadhasivam }, 381828dbc29SManivannan Sadhasivam }; 382828dbc29SManivannan Sadhasivam 38397589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_4lanes[] = { 38497589ad6SManivannan Sadhasivam { 38597589ad6SManivannan Sadhasivam .width = 1920, 38697589ad6SManivannan Sadhasivam .height = 1080, 38772825bc6SLaurent Pinchart .hmax = 2200, 38898e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_1080P, 38997589ad6SManivannan Sadhasivam .data = imx290_1080p_settings, 39097589ad6SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_1080p_settings), 39197589ad6SManivannan Sadhasivam }, 39297589ad6SManivannan Sadhasivam { 39397589ad6SManivannan Sadhasivam .width = 1280, 39497589ad6SManivannan Sadhasivam .height = 720, 39572825bc6SLaurent Pinchart .hmax = 3300, 39698e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_720P, 39797589ad6SManivannan Sadhasivam .data = imx290_720p_settings, 39897589ad6SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_720p_settings), 39997589ad6SManivannan Sadhasivam }, 40097589ad6SManivannan Sadhasivam }; 40197589ad6SManivannan Sadhasivam 40297589ad6SManivannan Sadhasivam static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) 40397589ad6SManivannan Sadhasivam { 40497589ad6SManivannan Sadhasivam if (imx290->nlanes == 2) 40597589ad6SManivannan Sadhasivam return imx290_modes_2lanes; 40697589ad6SManivannan Sadhasivam else 40797589ad6SManivannan Sadhasivam return imx290_modes_4lanes; 40897589ad6SManivannan Sadhasivam } 40997589ad6SManivannan Sadhasivam 41097589ad6SManivannan Sadhasivam static inline int imx290_modes_num(const struct imx290 *imx290) 41197589ad6SManivannan Sadhasivam { 41297589ad6SManivannan Sadhasivam if (imx290->nlanes == 2) 41397589ad6SManivannan Sadhasivam return ARRAY_SIZE(imx290_modes_2lanes); 41497589ad6SManivannan Sadhasivam else 41597589ad6SManivannan Sadhasivam return ARRAY_SIZE(imx290_modes_4lanes); 41697589ad6SManivannan Sadhasivam } 41797589ad6SManivannan Sadhasivam 418cb7e1c8dSLaurent Pinchart struct imx290_pixfmt { 419cb7e1c8dSLaurent Pinchart u32 code; 420cb7e1c8dSLaurent Pinchart u8 bpp; 421cb7e1c8dSLaurent Pinchart }; 422cb7e1c8dSLaurent Pinchart 423cb7e1c8dSLaurent Pinchart static const struct imx290_pixfmt imx290_formats[] = { 424cb7e1c8dSLaurent Pinchart { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 425cb7e1c8dSLaurent Pinchart { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 426cb7e1c8dSLaurent Pinchart }; 427cb7e1c8dSLaurent Pinchart 428cb7e1c8dSLaurent Pinchart /* ----------------------------------------------------------------------------- 429cb7e1c8dSLaurent Pinchart * Register access 430cb7e1c8dSLaurent Pinchart */ 431828dbc29SManivannan Sadhasivam 432e611f3daSLaurent Pinchart static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value) 433828dbc29SManivannan Sadhasivam { 434e70abe88SLaurent Pinchart u8 data[3] = { 0, 0, 0 }; 435828dbc29SManivannan Sadhasivam int ret; 436828dbc29SManivannan Sadhasivam 437e70abe88SLaurent Pinchart ret = regmap_raw_read(imx290->regmap, addr & IMX290_REG_ADDR_MASK, 438e70abe88SLaurent Pinchart data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); 439e70abe88SLaurent Pinchart if (ret < 0) { 440e70abe88SLaurent Pinchart dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", 441e70abe88SLaurent Pinchart ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, 442e70abe88SLaurent Pinchart addr & IMX290_REG_ADDR_MASK, ret); 443828dbc29SManivannan Sadhasivam return ret; 444828dbc29SManivannan Sadhasivam } 445828dbc29SManivannan Sadhasivam 446e70abe88SLaurent Pinchart *value = (data[2] << 16) | (data[1] << 8) | data[0]; 447828dbc29SManivannan Sadhasivam return 0; 448828dbc29SManivannan Sadhasivam } 449828dbc29SManivannan Sadhasivam 450e611f3daSLaurent Pinchart static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) 451828dbc29SManivannan Sadhasivam { 452e70abe88SLaurent Pinchart u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 }; 453828dbc29SManivannan Sadhasivam int ret; 454828dbc29SManivannan Sadhasivam 455e611f3daSLaurent Pinchart if (err && *err) 456e611f3daSLaurent Pinchart return *err; 457e611f3daSLaurent Pinchart 458e70abe88SLaurent Pinchart ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, 459e70abe88SLaurent Pinchart data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); 460e611f3daSLaurent Pinchart if (ret < 0) { 461e70abe88SLaurent Pinchart dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", 462e70abe88SLaurent Pinchart ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, 463e70abe88SLaurent Pinchart addr & IMX290_REG_ADDR_MASK, ret); 464e611f3daSLaurent Pinchart if (err) 465e611f3daSLaurent Pinchart *err = ret; 466e611f3daSLaurent Pinchart } 467828dbc29SManivannan Sadhasivam 468828dbc29SManivannan Sadhasivam return ret; 469828dbc29SManivannan Sadhasivam } 470828dbc29SManivannan Sadhasivam 471828dbc29SManivannan Sadhasivam static int imx290_set_register_array(struct imx290 *imx290, 472828dbc29SManivannan Sadhasivam const struct imx290_regval *settings, 473828dbc29SManivannan Sadhasivam unsigned int num_settings) 474828dbc29SManivannan Sadhasivam { 475828dbc29SManivannan Sadhasivam unsigned int i; 476828dbc29SManivannan Sadhasivam int ret; 477828dbc29SManivannan Sadhasivam 478828dbc29SManivannan Sadhasivam for (i = 0; i < num_settings; ++i, ++settings) { 479e611f3daSLaurent Pinchart ret = imx290_write(imx290, settings->reg, settings->val, NULL); 480828dbc29SManivannan Sadhasivam if (ret < 0) 481828dbc29SManivannan Sadhasivam return ret; 482828dbc29SManivannan Sadhasivam } 483828dbc29SManivannan Sadhasivam 4846544af9bSManivannan Sadhasivam /* Provide 10ms settle time */ 4858e5652aeSAndrey Konovalov usleep_range(10000, 11000); 4866544af9bSManivannan Sadhasivam 487828dbc29SManivannan Sadhasivam return 0; 488828dbc29SManivannan Sadhasivam } 489828dbc29SManivannan Sadhasivam 490cb7e1c8dSLaurent Pinchart static int imx290_set_data_lanes(struct imx290 *imx290) 491828dbc29SManivannan Sadhasivam { 492cb7e1c8dSLaurent Pinchart int ret = 0, laneval, frsel; 493828dbc29SManivannan Sadhasivam 494cb7e1c8dSLaurent Pinchart switch (imx290->nlanes) { 495cb7e1c8dSLaurent Pinchart case 2: 496cb7e1c8dSLaurent Pinchart laneval = 0x01; 497cb7e1c8dSLaurent Pinchart frsel = 0x02; 498cb7e1c8dSLaurent Pinchart break; 499cb7e1c8dSLaurent Pinchart case 4: 500cb7e1c8dSLaurent Pinchart laneval = 0x03; 501cb7e1c8dSLaurent Pinchart frsel = 0x01; 502cb7e1c8dSLaurent Pinchart break; 503cb7e1c8dSLaurent Pinchart default: 504cb7e1c8dSLaurent Pinchart /* 505cb7e1c8dSLaurent Pinchart * We should never hit this since the data lane count is 506cb7e1c8dSLaurent Pinchart * validated in probe itself 507cb7e1c8dSLaurent Pinchart */ 508cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Lane configuration not supported\n"); 509cb7e1c8dSLaurent Pinchart return -EINVAL; 510828dbc29SManivannan Sadhasivam } 511828dbc29SManivannan Sadhasivam 512cb7e1c8dSLaurent Pinchart imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret); 513cb7e1c8dSLaurent Pinchart imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret); 514cb7e1c8dSLaurent Pinchart imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); 515cb7e1c8dSLaurent Pinchart 516cb7e1c8dSLaurent Pinchart return ret; 517cb7e1c8dSLaurent Pinchart } 518cb7e1c8dSLaurent Pinchart 519cb7e1c8dSLaurent Pinchart static int imx290_write_current_format(struct imx290 *imx290) 520cb7e1c8dSLaurent Pinchart { 521cb7e1c8dSLaurent Pinchart int ret; 522cb7e1c8dSLaurent Pinchart 523cb7e1c8dSLaurent Pinchart switch (imx290->current_format.code) { 524cb7e1c8dSLaurent Pinchart case MEDIA_BUS_FMT_SRGGB10_1X10: 525cb7e1c8dSLaurent Pinchart ret = imx290_set_register_array(imx290, imx290_10bit_settings, 526cb7e1c8dSLaurent Pinchart ARRAY_SIZE( 527cb7e1c8dSLaurent Pinchart imx290_10bit_settings)); 528cb7e1c8dSLaurent Pinchart if (ret < 0) { 529cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Could not set format registers\n"); 530cb7e1c8dSLaurent Pinchart return ret; 531cb7e1c8dSLaurent Pinchart } 532cb7e1c8dSLaurent Pinchart break; 533cb7e1c8dSLaurent Pinchart case MEDIA_BUS_FMT_SRGGB12_1X12: 534cb7e1c8dSLaurent Pinchart ret = imx290_set_register_array(imx290, imx290_12bit_settings, 535cb7e1c8dSLaurent Pinchart ARRAY_SIZE( 536cb7e1c8dSLaurent Pinchart imx290_12bit_settings)); 537cb7e1c8dSLaurent Pinchart if (ret < 0) { 538cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Could not set format registers\n"); 539cb7e1c8dSLaurent Pinchart return ret; 540cb7e1c8dSLaurent Pinchart } 541cb7e1c8dSLaurent Pinchart break; 542cb7e1c8dSLaurent Pinchart default: 543cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Unknown pixel format\n"); 544cb7e1c8dSLaurent Pinchart return -EINVAL; 545cb7e1c8dSLaurent Pinchart } 546cb7e1c8dSLaurent Pinchart 547cb7e1c8dSLaurent Pinchart return 0; 548cb7e1c8dSLaurent Pinchart } 549cb7e1c8dSLaurent Pinchart 550cb7e1c8dSLaurent Pinchart static s64 imx290_get_link_freq(struct imx290 *imx290) 551cb7e1c8dSLaurent Pinchart { 552*70bbf56aSLaurent Pinchart u8 index = imx290->current_mode->link_freq_index; 553cb7e1c8dSLaurent Pinchart 554cb7e1c8dSLaurent Pinchart return *(imx290_link_freqs_ptr(imx290) + index); 555cb7e1c8dSLaurent Pinchart } 556cb7e1c8dSLaurent Pinchart 557cb7e1c8dSLaurent Pinchart static u64 imx290_calc_pixel_rate(struct imx290 *imx290) 558cb7e1c8dSLaurent Pinchart { 559cb7e1c8dSLaurent Pinchart s64 link_freq = imx290_get_link_freq(imx290); 560cb7e1c8dSLaurent Pinchart u8 nlanes = imx290->nlanes; 561cb7e1c8dSLaurent Pinchart u64 pixel_rate; 562cb7e1c8dSLaurent Pinchart 563cb7e1c8dSLaurent Pinchart /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ 564cb7e1c8dSLaurent Pinchart pixel_rate = link_freq * 2 * nlanes; 565cb7e1c8dSLaurent Pinchart do_div(pixel_rate, imx290->bpp); 566cb7e1c8dSLaurent Pinchart return pixel_rate; 567cb7e1c8dSLaurent Pinchart } 568cb7e1c8dSLaurent Pinchart 569cb7e1c8dSLaurent Pinchart /* ---------------------------------------------------------------------------- 570cb7e1c8dSLaurent Pinchart * Controls 571cb7e1c8dSLaurent Pinchart */ 572cb7e1c8dSLaurent Pinchart 573828dbc29SManivannan Sadhasivam static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) 574828dbc29SManivannan Sadhasivam { 575828dbc29SManivannan Sadhasivam struct imx290 *imx290 = container_of(ctrl->handler, 576828dbc29SManivannan Sadhasivam struct imx290, ctrls); 577828dbc29SManivannan Sadhasivam int ret = 0; 578828dbc29SManivannan Sadhasivam 579828dbc29SManivannan Sadhasivam /* V4L2 controls values will be applied only when power is already up */ 580828dbc29SManivannan Sadhasivam if (!pm_runtime_get_if_in_use(imx290->dev)) 581828dbc29SManivannan Sadhasivam return 0; 582828dbc29SManivannan Sadhasivam 583828dbc29SManivannan Sadhasivam switch (ctrl->id) { 5843dd10515SLaurent Pinchart case V4L2_CID_ANALOGUE_GAIN: 585e611f3daSLaurent Pinchart ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); 586828dbc29SManivannan Sadhasivam break; 587827c7e69SLaurent Pinchart 588827c7e69SLaurent Pinchart case V4L2_CID_EXPOSURE: 589827c7e69SLaurent Pinchart ret = imx290_write(imx290, IMX290_SHS1, 590827c7e69SLaurent Pinchart IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL); 591827c7e69SLaurent Pinchart break; 592827c7e69SLaurent Pinchart 593a58df1f9SManivannan Sadhasivam case V4L2_CID_TEST_PATTERN: 594a58df1f9SManivannan Sadhasivam if (ctrl->val) { 595e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret); 5968e5652aeSAndrey Konovalov usleep_range(10000, 11000); 597e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_PGCTRL, 598a58df1f9SManivannan Sadhasivam (u8)(IMX290_PGCTRL_REGEN | 599a58df1f9SManivannan Sadhasivam IMX290_PGCTRL_THRU | 600e611f3daSLaurent Pinchart IMX290_PGCTRL_MODE(ctrl->val)), &ret); 601a58df1f9SManivannan Sadhasivam } else { 602e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret); 6038e5652aeSAndrey Konovalov usleep_range(10000, 11000); 604c566ac01SManivannan Sadhasivam if (imx290->bpp == 10) 605e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_BLKLEVEL, 0x3c, 606e611f3daSLaurent Pinchart &ret); 607c566ac01SManivannan Sadhasivam else /* 12 bits per pixel */ 608e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_BLKLEVEL, 0xf0, 609e611f3daSLaurent Pinchart &ret); 610a58df1f9SManivannan Sadhasivam } 611a58df1f9SManivannan Sadhasivam break; 612828dbc29SManivannan Sadhasivam default: 613828dbc29SManivannan Sadhasivam ret = -EINVAL; 614828dbc29SManivannan Sadhasivam break; 615828dbc29SManivannan Sadhasivam } 616828dbc29SManivannan Sadhasivam 617828dbc29SManivannan Sadhasivam pm_runtime_put(imx290->dev); 618828dbc29SManivannan Sadhasivam 619828dbc29SManivannan Sadhasivam return ret; 620828dbc29SManivannan Sadhasivam } 621828dbc29SManivannan Sadhasivam 622828dbc29SManivannan Sadhasivam static const struct v4l2_ctrl_ops imx290_ctrl_ops = { 623828dbc29SManivannan Sadhasivam .s_ctrl = imx290_set_ctrl, 624828dbc29SManivannan Sadhasivam }; 625828dbc29SManivannan Sadhasivam 626cb7e1c8dSLaurent Pinchart static const char * const imx290_test_pattern_menu[] = { 627cb7e1c8dSLaurent Pinchart "Disabled", 628cb7e1c8dSLaurent Pinchart "Sequence Pattern 1", 629cb7e1c8dSLaurent Pinchart "Horizontal Color-bar Chart", 630cb7e1c8dSLaurent Pinchart "Vertical Color-bar Chart", 631cb7e1c8dSLaurent Pinchart "Sequence Pattern 2", 632cb7e1c8dSLaurent Pinchart "Gradation Pattern 1", 633cb7e1c8dSLaurent Pinchart "Gradation Pattern 2", 634cb7e1c8dSLaurent Pinchart "000/555h Toggle Pattern", 635cb7e1c8dSLaurent Pinchart }; 636cb7e1c8dSLaurent Pinchart 637a7941da3SLaurent Pinchart static void imx290_ctrl_update(struct imx290 *imx290, 638a7941da3SLaurent Pinchart const struct imx290_mode *mode) 639a7941da3SLaurent Pinchart { 640a7941da3SLaurent Pinchart unsigned int hblank = mode->hmax - mode->width; 641a7941da3SLaurent Pinchart unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; 642a7941da3SLaurent Pinchart 643a7941da3SLaurent Pinchart /* 644a7941da3SLaurent Pinchart * This function may be called from imx290_set_fmt() before controls 645a7941da3SLaurent Pinchart * get created by imx290_ctrl_init(). Return immediately in that case. 646a7941da3SLaurent Pinchart */ 647a7941da3SLaurent Pinchart if (!imx290->ctrls.lock) 648a7941da3SLaurent Pinchart return; 649a7941da3SLaurent Pinchart 650*70bbf56aSLaurent Pinchart __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); 651a7941da3SLaurent Pinchart __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, 652a7941da3SLaurent Pinchart imx290_calc_pixel_rate(imx290)); 653a7941da3SLaurent Pinchart 654a7941da3SLaurent Pinchart __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank); 655a7941da3SLaurent Pinchart __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank); 656a7941da3SLaurent Pinchart } 657a7941da3SLaurent Pinchart 658cb7e1c8dSLaurent Pinchart static int imx290_ctrl_init(struct imx290 *imx290) 659cb7e1c8dSLaurent Pinchart { 660cb7e1c8dSLaurent Pinchart struct v4l2_fwnode_device_properties props; 661cb7e1c8dSLaurent Pinchart unsigned int blank; 662cb7e1c8dSLaurent Pinchart int ret; 663cb7e1c8dSLaurent Pinchart 664cb7e1c8dSLaurent Pinchart ret = v4l2_fwnode_device_parse(imx290->dev, &props); 665cb7e1c8dSLaurent Pinchart if (ret < 0) 666cb7e1c8dSLaurent Pinchart return ret; 667cb7e1c8dSLaurent Pinchart 668cb7e1c8dSLaurent Pinchart v4l2_ctrl_handler_init(&imx290->ctrls, 9); 669cb7e1c8dSLaurent Pinchart imx290->ctrls.lock = &imx290->lock; 670cb7e1c8dSLaurent Pinchart 671cb7e1c8dSLaurent Pinchart /* 672cb7e1c8dSLaurent Pinchart * The sensor has an analog gain and a digital gain, both controlled 673cb7e1c8dSLaurent Pinchart * through a single gain value, expressed in 0.3dB increments. Values 674cb7e1c8dSLaurent Pinchart * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values 675cb7e1c8dSLaurent Pinchart * up to 72.0dB (240) add further digital gain. Limit the range to 676cb7e1c8dSLaurent Pinchart * analog gain only, support for digital gain can be added separately 677cb7e1c8dSLaurent Pinchart * if needed. 678cb7e1c8dSLaurent Pinchart * 679cb7e1c8dSLaurent Pinchart * The IMX327 and IMX462 are largely compatible with the IMX290, but 680cb7e1c8dSLaurent Pinchart * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital 681cb7e1c8dSLaurent Pinchart * gain. When support for those sensors gets added to the driver, the 682cb7e1c8dSLaurent Pinchart * gain control should be adjusted accordingly. 683cb7e1c8dSLaurent Pinchart */ 684cb7e1c8dSLaurent Pinchart v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 685cb7e1c8dSLaurent Pinchart V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); 686cb7e1c8dSLaurent Pinchart 687cb7e1c8dSLaurent Pinchart v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 688cb7e1c8dSLaurent Pinchart V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, 689cb7e1c8dSLaurent Pinchart IMX290_VMAX_DEFAULT - 2); 690cb7e1c8dSLaurent Pinchart 691cb7e1c8dSLaurent Pinchart imx290->link_freq = 692cb7e1c8dSLaurent Pinchart v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, 693cb7e1c8dSLaurent Pinchart V4L2_CID_LINK_FREQ, 694cb7e1c8dSLaurent Pinchart imx290_link_freqs_num(imx290) - 1, 0, 695cb7e1c8dSLaurent Pinchart imx290_link_freqs_ptr(imx290)); 696cb7e1c8dSLaurent Pinchart if (imx290->link_freq) 697cb7e1c8dSLaurent Pinchart imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; 698cb7e1c8dSLaurent Pinchart 699cb7e1c8dSLaurent Pinchart imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 700cb7e1c8dSLaurent Pinchart V4L2_CID_PIXEL_RATE, 701cb7e1c8dSLaurent Pinchart 1, INT_MAX, 1, 702cb7e1c8dSLaurent Pinchart imx290_calc_pixel_rate(imx290)); 703cb7e1c8dSLaurent Pinchart 704cb7e1c8dSLaurent Pinchart v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, 705cb7e1c8dSLaurent Pinchart V4L2_CID_TEST_PATTERN, 706cb7e1c8dSLaurent Pinchart ARRAY_SIZE(imx290_test_pattern_menu) - 1, 707cb7e1c8dSLaurent Pinchart 0, 0, imx290_test_pattern_menu); 708cb7e1c8dSLaurent Pinchart 709cb7e1c8dSLaurent Pinchart blank = imx290->current_mode->hmax - imx290->current_mode->width; 710cb7e1c8dSLaurent Pinchart imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 711cb7e1c8dSLaurent Pinchart V4L2_CID_HBLANK, blank, blank, 1, 712cb7e1c8dSLaurent Pinchart blank); 713cb7e1c8dSLaurent Pinchart if (imx290->hblank) 714cb7e1c8dSLaurent Pinchart imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 715cb7e1c8dSLaurent Pinchart 716cb7e1c8dSLaurent Pinchart blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height; 717cb7e1c8dSLaurent Pinchart imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 718cb7e1c8dSLaurent Pinchart V4L2_CID_VBLANK, blank, blank, 1, 719cb7e1c8dSLaurent Pinchart blank); 720cb7e1c8dSLaurent Pinchart if (imx290->vblank) 721cb7e1c8dSLaurent Pinchart imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 722cb7e1c8dSLaurent Pinchart 723cb7e1c8dSLaurent Pinchart v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, 724cb7e1c8dSLaurent Pinchart &props); 725cb7e1c8dSLaurent Pinchart 726cb7e1c8dSLaurent Pinchart imx290->sd.ctrl_handler = &imx290->ctrls; 727cb7e1c8dSLaurent Pinchart 728cb7e1c8dSLaurent Pinchart if (imx290->ctrls.error) { 729cb7e1c8dSLaurent Pinchart ret = imx290->ctrls.error; 730cb7e1c8dSLaurent Pinchart v4l2_ctrl_handler_free(&imx290->ctrls); 731cb7e1c8dSLaurent Pinchart return ret; 732cb7e1c8dSLaurent Pinchart } 733cb7e1c8dSLaurent Pinchart 734cb7e1c8dSLaurent Pinchart return 0; 735cb7e1c8dSLaurent Pinchart } 736cb7e1c8dSLaurent Pinchart 737cb7e1c8dSLaurent Pinchart /* ---------------------------------------------------------------------------- 738cb7e1c8dSLaurent Pinchart * Subdev operations 739cb7e1c8dSLaurent Pinchart */ 740cb7e1c8dSLaurent Pinchart 741cb7e1c8dSLaurent Pinchart /* Start streaming */ 742cb7e1c8dSLaurent Pinchart static int imx290_start_streaming(struct imx290 *imx290) 743cb7e1c8dSLaurent Pinchart { 744cb7e1c8dSLaurent Pinchart int ret; 745cb7e1c8dSLaurent Pinchart 746cb7e1c8dSLaurent Pinchart /* Set init register settings */ 747cb7e1c8dSLaurent Pinchart ret = imx290_set_register_array(imx290, imx290_global_init_settings, 748cb7e1c8dSLaurent Pinchart ARRAY_SIZE( 749cb7e1c8dSLaurent Pinchart imx290_global_init_settings)); 750cb7e1c8dSLaurent Pinchart if (ret < 0) { 751cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Could not set init registers\n"); 752cb7e1c8dSLaurent Pinchart return ret; 753cb7e1c8dSLaurent Pinchart } 754cb7e1c8dSLaurent Pinchart 755cb7e1c8dSLaurent Pinchart /* Apply the register values related to current frame format */ 756cb7e1c8dSLaurent Pinchart ret = imx290_write_current_format(imx290); 757cb7e1c8dSLaurent Pinchart if (ret < 0) { 758cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Could not set frame format\n"); 759cb7e1c8dSLaurent Pinchart return ret; 760cb7e1c8dSLaurent Pinchart } 761cb7e1c8dSLaurent Pinchart 762cb7e1c8dSLaurent Pinchart /* Apply default values of current mode */ 763cb7e1c8dSLaurent Pinchart ret = imx290_set_register_array(imx290, imx290->current_mode->data, 764cb7e1c8dSLaurent Pinchart imx290->current_mode->data_size); 765cb7e1c8dSLaurent Pinchart if (ret < 0) { 766cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Could not set current mode\n"); 767cb7e1c8dSLaurent Pinchart return ret; 768cb7e1c8dSLaurent Pinchart } 769cb7e1c8dSLaurent Pinchart 770cb7e1c8dSLaurent Pinchart ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax, 771cb7e1c8dSLaurent Pinchart NULL); 772cb7e1c8dSLaurent Pinchart if (ret) 773cb7e1c8dSLaurent Pinchart return ret; 774cb7e1c8dSLaurent Pinchart 775cb7e1c8dSLaurent Pinchart /* Apply customized values from user */ 776cb7e1c8dSLaurent Pinchart ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); 777cb7e1c8dSLaurent Pinchart if (ret) { 778cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Could not sync v4l2 controls\n"); 779cb7e1c8dSLaurent Pinchart return ret; 780cb7e1c8dSLaurent Pinchart } 781cb7e1c8dSLaurent Pinchart 782cb7e1c8dSLaurent Pinchart imx290_write(imx290, IMX290_STANDBY, 0x00, &ret); 783cb7e1c8dSLaurent Pinchart 784cb7e1c8dSLaurent Pinchart msleep(30); 785cb7e1c8dSLaurent Pinchart 786cb7e1c8dSLaurent Pinchart /* Start streaming */ 787cb7e1c8dSLaurent Pinchart return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret); 788cb7e1c8dSLaurent Pinchart } 789cb7e1c8dSLaurent Pinchart 790cb7e1c8dSLaurent Pinchart /* Stop streaming */ 791cb7e1c8dSLaurent Pinchart static int imx290_stop_streaming(struct imx290 *imx290) 792cb7e1c8dSLaurent Pinchart { 793cb7e1c8dSLaurent Pinchart int ret = 0; 794cb7e1c8dSLaurent Pinchart 795cb7e1c8dSLaurent Pinchart imx290_write(imx290, IMX290_STANDBY, 0x01, &ret); 796cb7e1c8dSLaurent Pinchart 797cb7e1c8dSLaurent Pinchart msleep(30); 798cb7e1c8dSLaurent Pinchart 799cb7e1c8dSLaurent Pinchart return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret); 800cb7e1c8dSLaurent Pinchart } 801cb7e1c8dSLaurent Pinchart 802cb7e1c8dSLaurent Pinchart static int imx290_set_stream(struct v4l2_subdev *sd, int enable) 803cb7e1c8dSLaurent Pinchart { 804cb7e1c8dSLaurent Pinchart struct imx290 *imx290 = to_imx290(sd); 805cb7e1c8dSLaurent Pinchart int ret = 0; 806cb7e1c8dSLaurent Pinchart 807cb7e1c8dSLaurent Pinchart if (enable) { 808cb7e1c8dSLaurent Pinchart ret = pm_runtime_resume_and_get(imx290->dev); 809cb7e1c8dSLaurent Pinchart if (ret < 0) 810cb7e1c8dSLaurent Pinchart goto unlock_and_return; 811cb7e1c8dSLaurent Pinchart 812cb7e1c8dSLaurent Pinchart ret = imx290_start_streaming(imx290); 813cb7e1c8dSLaurent Pinchart if (ret) { 814cb7e1c8dSLaurent Pinchart dev_err(imx290->dev, "Start stream failed\n"); 815cb7e1c8dSLaurent Pinchart pm_runtime_put(imx290->dev); 816cb7e1c8dSLaurent Pinchart goto unlock_and_return; 817cb7e1c8dSLaurent Pinchart } 818cb7e1c8dSLaurent Pinchart } else { 819cb7e1c8dSLaurent Pinchart imx290_stop_streaming(imx290); 820cb7e1c8dSLaurent Pinchart pm_runtime_put(imx290->dev); 821cb7e1c8dSLaurent Pinchart } 822cb7e1c8dSLaurent Pinchart 823cb7e1c8dSLaurent Pinchart unlock_and_return: 824cb7e1c8dSLaurent Pinchart 825cb7e1c8dSLaurent Pinchart return ret; 826cb7e1c8dSLaurent Pinchart } 827cb7e1c8dSLaurent Pinchart 828b25537efSLaurent Pinchart static struct v4l2_mbus_framefmt * 829b25537efSLaurent Pinchart imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state *state, 830b25537efSLaurent Pinchart u32 which) 831b25537efSLaurent Pinchart { 832b25537efSLaurent Pinchart if (which == V4L2_SUBDEV_FORMAT_ACTIVE) 833b25537efSLaurent Pinchart return &imx290->current_format; 834b25537efSLaurent Pinchart else 835b25537efSLaurent Pinchart return v4l2_subdev_get_try_format(&imx290->sd, state, 0); 836b25537efSLaurent Pinchart } 837b25537efSLaurent Pinchart 838828dbc29SManivannan Sadhasivam static int imx290_enum_mbus_code(struct v4l2_subdev *sd, 8390d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 840828dbc29SManivannan Sadhasivam struct v4l2_subdev_mbus_code_enum *code) 841828dbc29SManivannan Sadhasivam { 842828dbc29SManivannan Sadhasivam if (code->index >= ARRAY_SIZE(imx290_formats)) 843828dbc29SManivannan Sadhasivam return -EINVAL; 844828dbc29SManivannan Sadhasivam 845828dbc29SManivannan Sadhasivam code->code = imx290_formats[code->index].code; 846828dbc29SManivannan Sadhasivam 847828dbc29SManivannan Sadhasivam return 0; 848828dbc29SManivannan Sadhasivam } 849828dbc29SManivannan Sadhasivam 8503b867fb6SManivannan Sadhasivam static int imx290_enum_frame_size(struct v4l2_subdev *sd, 8510d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 8523b867fb6SManivannan Sadhasivam struct v4l2_subdev_frame_size_enum *fse) 8533b867fb6SManivannan Sadhasivam { 8543b867fb6SManivannan Sadhasivam const struct imx290 *imx290 = to_imx290(sd); 8553b867fb6SManivannan Sadhasivam const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); 8563b867fb6SManivannan Sadhasivam 8573b867fb6SManivannan Sadhasivam if ((fse->code != imx290_formats[0].code) && 8583b867fb6SManivannan Sadhasivam (fse->code != imx290_formats[1].code)) 8593b867fb6SManivannan Sadhasivam return -EINVAL; 8603b867fb6SManivannan Sadhasivam 8613b867fb6SManivannan Sadhasivam if (fse->index >= imx290_modes_num(imx290)) 8623b867fb6SManivannan Sadhasivam return -EINVAL; 8633b867fb6SManivannan Sadhasivam 8643b867fb6SManivannan Sadhasivam fse->min_width = imx290_modes[fse->index].width; 8653b867fb6SManivannan Sadhasivam fse->max_width = imx290_modes[fse->index].width; 8663b867fb6SManivannan Sadhasivam fse->min_height = imx290_modes[fse->index].height; 8673b867fb6SManivannan Sadhasivam fse->max_height = imx290_modes[fse->index].height; 8683b867fb6SManivannan Sadhasivam 8693b867fb6SManivannan Sadhasivam return 0; 8703b867fb6SManivannan Sadhasivam } 8713b867fb6SManivannan Sadhasivam 872828dbc29SManivannan Sadhasivam static int imx290_get_fmt(struct v4l2_subdev *sd, 8730d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 874828dbc29SManivannan Sadhasivam struct v4l2_subdev_format *fmt) 875828dbc29SManivannan Sadhasivam { 876828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 877828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt *framefmt; 878828dbc29SManivannan Sadhasivam 879828dbc29SManivannan Sadhasivam mutex_lock(&imx290->lock); 880828dbc29SManivannan Sadhasivam 881b25537efSLaurent Pinchart framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which); 882828dbc29SManivannan Sadhasivam fmt->format = *framefmt; 883828dbc29SManivannan Sadhasivam 884828dbc29SManivannan Sadhasivam mutex_unlock(&imx290->lock); 885828dbc29SManivannan Sadhasivam 886828dbc29SManivannan Sadhasivam return 0; 887828dbc29SManivannan Sadhasivam } 888828dbc29SManivannan Sadhasivam 889828dbc29SManivannan Sadhasivam static int imx290_set_fmt(struct v4l2_subdev *sd, 8900d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 891828dbc29SManivannan Sadhasivam struct v4l2_subdev_format *fmt) 892828dbc29SManivannan Sadhasivam { 893828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 894828dbc29SManivannan Sadhasivam const struct imx290_mode *mode; 895828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt *format; 896828dbc29SManivannan Sadhasivam unsigned int i; 897828dbc29SManivannan Sadhasivam 898828dbc29SManivannan Sadhasivam mutex_lock(&imx290->lock); 899828dbc29SManivannan Sadhasivam 90097589ad6SManivannan Sadhasivam mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290), 90197589ad6SManivannan Sadhasivam imx290_modes_num(imx290), width, height, 902828dbc29SManivannan Sadhasivam fmt->format.width, fmt->format.height); 903828dbc29SManivannan Sadhasivam 904828dbc29SManivannan Sadhasivam fmt->format.width = mode->width; 905828dbc29SManivannan Sadhasivam fmt->format.height = mode->height; 906828dbc29SManivannan Sadhasivam 907828dbc29SManivannan Sadhasivam for (i = 0; i < ARRAY_SIZE(imx290_formats); i++) 908828dbc29SManivannan Sadhasivam if (imx290_formats[i].code == fmt->format.code) 909828dbc29SManivannan Sadhasivam break; 910828dbc29SManivannan Sadhasivam 911828dbc29SManivannan Sadhasivam if (i >= ARRAY_SIZE(imx290_formats)) 912828dbc29SManivannan Sadhasivam i = 0; 913828dbc29SManivannan Sadhasivam 914828dbc29SManivannan Sadhasivam fmt->format.code = imx290_formats[i].code; 915828dbc29SManivannan Sadhasivam fmt->format.field = V4L2_FIELD_NONE; 916828dbc29SManivannan Sadhasivam 917b25537efSLaurent Pinchart format = imx290_get_pad_format(imx290, sd_state, fmt->which); 918b25537efSLaurent Pinchart 919b25537efSLaurent Pinchart if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 920828dbc29SManivannan Sadhasivam imx290->current_mode = mode; 921c566ac01SManivannan Sadhasivam imx290->bpp = imx290_formats[i].bpp; 92298e0500eSManivannan Sadhasivam 923a7941da3SLaurent Pinchart imx290_ctrl_update(imx290, mode); 924828dbc29SManivannan Sadhasivam } 925828dbc29SManivannan Sadhasivam 926828dbc29SManivannan Sadhasivam *format = fmt->format; 927828dbc29SManivannan Sadhasivam 928828dbc29SManivannan Sadhasivam mutex_unlock(&imx290->lock); 929828dbc29SManivannan Sadhasivam 930828dbc29SManivannan Sadhasivam return 0; 931828dbc29SManivannan Sadhasivam } 932828dbc29SManivannan Sadhasivam 933b4ab57b0SLaurent Pinchart static int imx290_get_selection(struct v4l2_subdev *sd, 934b4ab57b0SLaurent Pinchart struct v4l2_subdev_state *sd_state, 935b4ab57b0SLaurent Pinchart struct v4l2_subdev_selection *sel) 936b4ab57b0SLaurent Pinchart { 937b4ab57b0SLaurent Pinchart struct imx290 *imx290 = to_imx290(sd); 938b4ab57b0SLaurent Pinchart struct v4l2_mbus_framefmt *format; 939b4ab57b0SLaurent Pinchart 940b4ab57b0SLaurent Pinchart switch (sel->target) { 941b4ab57b0SLaurent Pinchart case V4L2_SEL_TGT_CROP: { 942b4ab57b0SLaurent Pinchart format = imx290_get_pad_format(imx290, sd_state, sel->which); 943b4ab57b0SLaurent Pinchart 944b4ab57b0SLaurent Pinchart mutex_lock(&imx290->lock); 945b4ab57b0SLaurent Pinchart 946b4ab57b0SLaurent Pinchart sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP 947b4ab57b0SLaurent Pinchart + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2; 948b4ab57b0SLaurent Pinchart sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT 949b4ab57b0SLaurent Pinchart + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2; 950b4ab57b0SLaurent Pinchart sel->r.width = format->width; 951b4ab57b0SLaurent Pinchart sel->r.height = format->height; 952b4ab57b0SLaurent Pinchart 953b4ab57b0SLaurent Pinchart mutex_unlock(&imx290->lock); 954b4ab57b0SLaurent Pinchart return 0; 955b4ab57b0SLaurent Pinchart } 956b4ab57b0SLaurent Pinchart 957b4ab57b0SLaurent Pinchart case V4L2_SEL_TGT_NATIVE_SIZE: 958b4ab57b0SLaurent Pinchart case V4L2_SEL_TGT_CROP_BOUNDS: 959b4ab57b0SLaurent Pinchart sel->r.top = 0; 960b4ab57b0SLaurent Pinchart sel->r.left = 0; 961b4ab57b0SLaurent Pinchart sel->r.width = IMX290_PIXEL_ARRAY_WIDTH; 962b4ab57b0SLaurent Pinchart sel->r.height = IMX290_PIXEL_ARRAY_HEIGHT; 963b4ab57b0SLaurent Pinchart 964b4ab57b0SLaurent Pinchart return 0; 965b4ab57b0SLaurent Pinchart 966b4ab57b0SLaurent Pinchart case V4L2_SEL_TGT_CROP_DEFAULT: 967b4ab57b0SLaurent Pinchart sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP; 968b4ab57b0SLaurent Pinchart sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT; 969b4ab57b0SLaurent Pinchart sel->r.width = IMX290_PIXEL_ARRAY_RECORDING_WIDTH; 970b4ab57b0SLaurent Pinchart sel->r.height = IMX290_PIXEL_ARRAY_RECORDING_HEIGHT; 971b4ab57b0SLaurent Pinchart 972b4ab57b0SLaurent Pinchart return 0; 973b4ab57b0SLaurent Pinchart 974b4ab57b0SLaurent Pinchart default: 975b4ab57b0SLaurent Pinchart return -EINVAL; 976b4ab57b0SLaurent Pinchart } 977b4ab57b0SLaurent Pinchart } 978b4ab57b0SLaurent Pinchart 979828dbc29SManivannan Sadhasivam static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, 9800d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state) 981828dbc29SManivannan Sadhasivam { 982828dbc29SManivannan Sadhasivam struct v4l2_subdev_format fmt = { 0 }; 983828dbc29SManivannan Sadhasivam 9840d346d2aSTomi Valkeinen fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; 985828dbc29SManivannan Sadhasivam fmt.format.width = 1920; 986828dbc29SManivannan Sadhasivam fmt.format.height = 1080; 987828dbc29SManivannan Sadhasivam 9880d346d2aSTomi Valkeinen imx290_set_fmt(subdev, sd_state, &fmt); 989828dbc29SManivannan Sadhasivam 990828dbc29SManivannan Sadhasivam return 0; 991828dbc29SManivannan Sadhasivam } 992828dbc29SManivannan Sadhasivam 993cb7e1c8dSLaurent Pinchart static const struct v4l2_subdev_video_ops imx290_video_ops = { 994cb7e1c8dSLaurent Pinchart .s_stream = imx290_set_stream, 995cb7e1c8dSLaurent Pinchart }; 996828dbc29SManivannan Sadhasivam 997cb7e1c8dSLaurent Pinchart static const struct v4l2_subdev_pad_ops imx290_pad_ops = { 998cb7e1c8dSLaurent Pinchart .init_cfg = imx290_entity_init_cfg, 999cb7e1c8dSLaurent Pinchart .enum_mbus_code = imx290_enum_mbus_code, 1000cb7e1c8dSLaurent Pinchart .enum_frame_size = imx290_enum_frame_size, 1001cb7e1c8dSLaurent Pinchart .get_fmt = imx290_get_fmt, 1002cb7e1c8dSLaurent Pinchart .set_fmt = imx290_set_fmt, 1003cb7e1c8dSLaurent Pinchart .get_selection = imx290_get_selection, 1004cb7e1c8dSLaurent Pinchart }; 1005828dbc29SManivannan Sadhasivam 1006cb7e1c8dSLaurent Pinchart static const struct v4l2_subdev_ops imx290_subdev_ops = { 1007cb7e1c8dSLaurent Pinchart .video = &imx290_video_ops, 1008cb7e1c8dSLaurent Pinchart .pad = &imx290_pad_ops, 1009cb7e1c8dSLaurent Pinchart }; 1010828dbc29SManivannan Sadhasivam 1011cb7e1c8dSLaurent Pinchart static const struct media_entity_operations imx290_subdev_entity_ops = { 1012cb7e1c8dSLaurent Pinchart .link_validate = v4l2_subdev_link_validate, 1013cb7e1c8dSLaurent Pinchart }; 1014828dbc29SManivannan Sadhasivam 1015dfb704daSLaurent Pinchart static int imx290_subdev_init(struct imx290 *imx290) 1016dfb704daSLaurent Pinchart { 1017dfb704daSLaurent Pinchart struct i2c_client *client = to_i2c_client(imx290->dev); 1018dfb704daSLaurent Pinchart int ret; 1019dfb704daSLaurent Pinchart 1020dfb704daSLaurent Pinchart /* 1021dfb704daSLaurent Pinchart * Initialize the frame format. In particular, imx290->current_mode 1022dfb704daSLaurent Pinchart * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call 1023dfb704daSLaurent Pinchart * below relies on these fields. 1024dfb704daSLaurent Pinchart */ 1025dfb704daSLaurent Pinchart imx290_entity_init_cfg(&imx290->sd, NULL); 1026dfb704daSLaurent Pinchart 1027dfb704daSLaurent Pinchart ret = imx290_ctrl_init(imx290); 1028dfb704daSLaurent Pinchart if (ret < 0) { 1029dfb704daSLaurent Pinchart dev_err(imx290->dev, "Control initialization error %d\n", ret); 1030dfb704daSLaurent Pinchart return ret; 1031dfb704daSLaurent Pinchart } 1032dfb704daSLaurent Pinchart 1033dfb704daSLaurent Pinchart v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); 1034dfb704daSLaurent Pinchart imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1035dfb704daSLaurent Pinchart imx290->sd.dev = imx290->dev; 1036dfb704daSLaurent Pinchart imx290->sd.entity.ops = &imx290_subdev_entity_ops; 1037dfb704daSLaurent Pinchart imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 1038dfb704daSLaurent Pinchart 1039dfb704daSLaurent Pinchart imx290->pad.flags = MEDIA_PAD_FL_SOURCE; 1040dfb704daSLaurent Pinchart ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad); 1041dfb704daSLaurent Pinchart if (ret < 0) { 1042dfb704daSLaurent Pinchart dev_err(imx290->dev, "Could not register media entity\n"); 1043dfb704daSLaurent Pinchart v4l2_ctrl_handler_free(&imx290->ctrls); 1044dfb704daSLaurent Pinchart return ret; 1045dfb704daSLaurent Pinchart } 1046dfb704daSLaurent Pinchart 1047dfb704daSLaurent Pinchart return 0; 1048dfb704daSLaurent Pinchart } 1049dfb704daSLaurent Pinchart 1050dfb704daSLaurent Pinchart static void imx290_subdev_cleanup(struct imx290 *imx290) 1051dfb704daSLaurent Pinchart { 1052dfb704daSLaurent Pinchart media_entity_cleanup(&imx290->sd.entity); 1053dfb704daSLaurent Pinchart v4l2_ctrl_handler_free(&imx290->ctrls); 1054dfb704daSLaurent Pinchart } 1055dfb704daSLaurent Pinchart 1056cb7e1c8dSLaurent Pinchart /* ---------------------------------------------------------------------------- 1057cb7e1c8dSLaurent Pinchart * Power management 105897589ad6SManivannan Sadhasivam */ 105997589ad6SManivannan Sadhasivam 1060828dbc29SManivannan Sadhasivam static int imx290_power_on(struct device *dev) 1061828dbc29SManivannan Sadhasivam { 1062b50ce25dSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 1063828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 1064828dbc29SManivannan Sadhasivam int ret; 1065828dbc29SManivannan Sadhasivam 1066828dbc29SManivannan Sadhasivam ret = clk_prepare_enable(imx290->xclk); 1067828dbc29SManivannan Sadhasivam if (ret) { 1068b50ce25dSKrzysztof Kozlowski dev_err(dev, "Failed to enable clock\n"); 1069828dbc29SManivannan Sadhasivam return ret; 1070828dbc29SManivannan Sadhasivam } 1071828dbc29SManivannan Sadhasivam 10722548df53SLaurent Pinchart ret = regulator_bulk_enable(ARRAY_SIZE(imx290->supplies), 10732548df53SLaurent Pinchart imx290->supplies); 1074828dbc29SManivannan Sadhasivam if (ret) { 1075b50ce25dSKrzysztof Kozlowski dev_err(dev, "Failed to enable regulators\n"); 1076828dbc29SManivannan Sadhasivam clk_disable_unprepare(imx290->xclk); 1077828dbc29SManivannan Sadhasivam return ret; 1078828dbc29SManivannan Sadhasivam } 1079828dbc29SManivannan Sadhasivam 1080828dbc29SManivannan Sadhasivam usleep_range(1, 2); 10813909a92dSAndrey Konovalov gpiod_set_value_cansleep(imx290->rst_gpio, 0); 1082828dbc29SManivannan Sadhasivam usleep_range(30000, 31000); 1083828dbc29SManivannan Sadhasivam 108497589ad6SManivannan Sadhasivam /* Set data lane count */ 108597589ad6SManivannan Sadhasivam imx290_set_data_lanes(imx290); 108697589ad6SManivannan Sadhasivam 1087828dbc29SManivannan Sadhasivam return 0; 1088828dbc29SManivannan Sadhasivam } 1089828dbc29SManivannan Sadhasivam 1090828dbc29SManivannan Sadhasivam static int imx290_power_off(struct device *dev) 1091828dbc29SManivannan Sadhasivam { 1092b50ce25dSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 1093828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 1094828dbc29SManivannan Sadhasivam 1095828dbc29SManivannan Sadhasivam clk_disable_unprepare(imx290->xclk); 10963909a92dSAndrey Konovalov gpiod_set_value_cansleep(imx290->rst_gpio, 1); 10972548df53SLaurent Pinchart regulator_bulk_disable(ARRAY_SIZE(imx290->supplies), imx290->supplies); 1098828dbc29SManivannan Sadhasivam 1099828dbc29SManivannan Sadhasivam return 0; 1100828dbc29SManivannan Sadhasivam } 1101828dbc29SManivannan Sadhasivam 1102828dbc29SManivannan Sadhasivam static const struct dev_pm_ops imx290_pm_ops = { 11038d2d1bedSAndrey Konovalov SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) 1104828dbc29SManivannan Sadhasivam }; 1105828dbc29SManivannan Sadhasivam 1106cb7e1c8dSLaurent Pinchart /* ---------------------------------------------------------------------------- 1107cb7e1c8dSLaurent Pinchart * Probe & remove 110872c87b7aSLaurent Pinchart */ 110972c87b7aSLaurent Pinchart 1110cb7e1c8dSLaurent Pinchart static const struct regmap_config imx290_regmap_config = { 1111cb7e1c8dSLaurent Pinchart .reg_bits = 16, 1112cb7e1c8dSLaurent Pinchart .val_bits = 8, 1113cb7e1c8dSLaurent Pinchart }; 111472c87b7aSLaurent Pinchart 1115cb7e1c8dSLaurent Pinchart static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = { 1116cb7e1c8dSLaurent Pinchart "vdda", 1117cb7e1c8dSLaurent Pinchart "vddd", 1118cb7e1c8dSLaurent Pinchart "vdddo", 1119cb7e1c8dSLaurent Pinchart }; 112072c87b7aSLaurent Pinchart 1121cb7e1c8dSLaurent Pinchart static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) 1122cb7e1c8dSLaurent Pinchart { 1123cb7e1c8dSLaurent Pinchart unsigned int i; 112472c87b7aSLaurent Pinchart 1125cb7e1c8dSLaurent Pinchart for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++) 1126cb7e1c8dSLaurent Pinchart imx290->supplies[i].supply = imx290_supply_name[i]; 112772c87b7aSLaurent Pinchart 1128cb7e1c8dSLaurent Pinchart return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies), 1129cb7e1c8dSLaurent Pinchart imx290->supplies); 113072c87b7aSLaurent Pinchart } 113172c87b7aSLaurent Pinchart 113298e0500eSManivannan Sadhasivam /* 113398e0500eSManivannan Sadhasivam * Returns 0 if all link frequencies used by the driver for the given number 113498e0500eSManivannan Sadhasivam * of MIPI data lanes are mentioned in the device tree, or the value of the 113598e0500eSManivannan Sadhasivam * first missing frequency otherwise. 113698e0500eSManivannan Sadhasivam */ 1137a2706758SAndrey Konovalov static s64 imx290_check_link_freqs(const struct imx290 *imx290, 1138a2706758SAndrey Konovalov const struct v4l2_fwnode_endpoint *ep) 113998e0500eSManivannan Sadhasivam { 114098e0500eSManivannan Sadhasivam int i, j; 114198e0500eSManivannan Sadhasivam const s64 *freqs = imx290_link_freqs_ptr(imx290); 114298e0500eSManivannan Sadhasivam int freqs_count = imx290_link_freqs_num(imx290); 114398e0500eSManivannan Sadhasivam 114498e0500eSManivannan Sadhasivam for (i = 0; i < freqs_count; i++) { 1145a2706758SAndrey Konovalov for (j = 0; j < ep->nr_of_link_frequencies; j++) 1146a2706758SAndrey Konovalov if (freqs[i] == ep->link_frequencies[j]) 114798e0500eSManivannan Sadhasivam break; 1148a2706758SAndrey Konovalov if (j == ep->nr_of_link_frequencies) 114998e0500eSManivannan Sadhasivam return freqs[i]; 115098e0500eSManivannan Sadhasivam } 115198e0500eSManivannan Sadhasivam return 0; 115298e0500eSManivannan Sadhasivam } 115398e0500eSManivannan Sadhasivam 1154828dbc29SManivannan Sadhasivam static int imx290_probe(struct i2c_client *client) 1155828dbc29SManivannan Sadhasivam { 1156828dbc29SManivannan Sadhasivam struct device *dev = &client->dev; 1157828dbc29SManivannan Sadhasivam struct fwnode_handle *endpoint; 1158a2706758SAndrey Konovalov /* Only CSI2 is supported for now: */ 1159a2706758SAndrey Konovalov struct v4l2_fwnode_endpoint ep = { 1160a2706758SAndrey Konovalov .bus_type = V4L2_MBUS_CSI2_DPHY 1161a2706758SAndrey Konovalov }; 1162828dbc29SManivannan Sadhasivam struct imx290 *imx290; 1163828dbc29SManivannan Sadhasivam u32 xclk_freq; 116498e0500eSManivannan Sadhasivam s64 fq; 1165828dbc29SManivannan Sadhasivam int ret; 1166828dbc29SManivannan Sadhasivam 1167828dbc29SManivannan Sadhasivam imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); 1168828dbc29SManivannan Sadhasivam if (!imx290) 1169828dbc29SManivannan Sadhasivam return -ENOMEM; 1170828dbc29SManivannan Sadhasivam 1171828dbc29SManivannan Sadhasivam imx290->dev = dev; 1172828dbc29SManivannan Sadhasivam imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config); 1173828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->regmap)) { 1174828dbc29SManivannan Sadhasivam dev_err(dev, "Unable to initialize I2C\n"); 1175828dbc29SManivannan Sadhasivam return -ENODEV; 1176828dbc29SManivannan Sadhasivam } 1177828dbc29SManivannan Sadhasivam 1178828dbc29SManivannan Sadhasivam endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); 1179828dbc29SManivannan Sadhasivam if (!endpoint) { 1180828dbc29SManivannan Sadhasivam dev_err(dev, "Endpoint node not found\n"); 1181828dbc29SManivannan Sadhasivam return -EINVAL; 1182828dbc29SManivannan Sadhasivam } 1183828dbc29SManivannan Sadhasivam 1184a2706758SAndrey Konovalov ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); 1185828dbc29SManivannan Sadhasivam fwnode_handle_put(endpoint); 1186a2706758SAndrey Konovalov if (ret == -ENXIO) { 1187a2706758SAndrey Konovalov dev_err(dev, "Unsupported bus type, should be CSI2\n"); 1188dfb704daSLaurent Pinchart goto err_endpoint; 1189a2706758SAndrey Konovalov } else if (ret) { 1190828dbc29SManivannan Sadhasivam dev_err(dev, "Parsing endpoint node failed\n"); 1191dfb704daSLaurent Pinchart goto err_endpoint; 1192828dbc29SManivannan Sadhasivam } 1193828dbc29SManivannan Sadhasivam 119497589ad6SManivannan Sadhasivam /* Get number of data lanes */ 1195a2706758SAndrey Konovalov imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes; 119697589ad6SManivannan Sadhasivam if (imx290->nlanes != 2 && imx290->nlanes != 4) { 119797589ad6SManivannan Sadhasivam dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); 119897589ad6SManivannan Sadhasivam ret = -EINVAL; 1199dfb704daSLaurent Pinchart goto err_endpoint; 120097589ad6SManivannan Sadhasivam } 120197589ad6SManivannan Sadhasivam 120297589ad6SManivannan Sadhasivam dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); 120397589ad6SManivannan Sadhasivam 1204a2706758SAndrey Konovalov if (!ep.nr_of_link_frequencies) { 1205828dbc29SManivannan Sadhasivam dev_err(dev, "link-frequency property not found in DT\n"); 1206828dbc29SManivannan Sadhasivam ret = -EINVAL; 1207dfb704daSLaurent Pinchart goto err_endpoint; 1208828dbc29SManivannan Sadhasivam } 1209828dbc29SManivannan Sadhasivam 121098e0500eSManivannan Sadhasivam /* Check that link frequences for all the modes are in device tree */ 1211a2706758SAndrey Konovalov fq = imx290_check_link_freqs(imx290, &ep); 121298e0500eSManivannan Sadhasivam if (fq) { 121398e0500eSManivannan Sadhasivam dev_err(dev, "Link frequency of %lld is not supported\n", fq); 1214828dbc29SManivannan Sadhasivam ret = -EINVAL; 1215dfb704daSLaurent Pinchart goto err_endpoint; 1216828dbc29SManivannan Sadhasivam } 1217828dbc29SManivannan Sadhasivam 1218828dbc29SManivannan Sadhasivam /* get system clock (xclk) */ 1219828dbc29SManivannan Sadhasivam imx290->xclk = devm_clk_get(dev, "xclk"); 1220828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->xclk)) { 1221828dbc29SManivannan Sadhasivam dev_err(dev, "Could not get xclk"); 1222828dbc29SManivannan Sadhasivam ret = PTR_ERR(imx290->xclk); 1223dfb704daSLaurent Pinchart goto err_endpoint; 1224828dbc29SManivannan Sadhasivam } 1225828dbc29SManivannan Sadhasivam 1226828dbc29SManivannan Sadhasivam ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", 1227828dbc29SManivannan Sadhasivam &xclk_freq); 1228828dbc29SManivannan Sadhasivam if (ret) { 1229828dbc29SManivannan Sadhasivam dev_err(dev, "Could not get xclk frequency\n"); 1230dfb704daSLaurent Pinchart goto err_endpoint; 1231828dbc29SManivannan Sadhasivam } 1232828dbc29SManivannan Sadhasivam 1233828dbc29SManivannan Sadhasivam /* external clock must be 37.125 MHz */ 1234828dbc29SManivannan Sadhasivam if (xclk_freq != 37125000) { 1235828dbc29SManivannan Sadhasivam dev_err(dev, "External clock frequency %u is not supported\n", 1236828dbc29SManivannan Sadhasivam xclk_freq); 1237828dbc29SManivannan Sadhasivam ret = -EINVAL; 1238dfb704daSLaurent Pinchart goto err_endpoint; 1239828dbc29SManivannan Sadhasivam } 1240828dbc29SManivannan Sadhasivam 1241828dbc29SManivannan Sadhasivam ret = clk_set_rate(imx290->xclk, xclk_freq); 1242828dbc29SManivannan Sadhasivam if (ret) { 1243828dbc29SManivannan Sadhasivam dev_err(dev, "Could not set xclk frequency\n"); 1244dfb704daSLaurent Pinchart goto err_endpoint; 1245828dbc29SManivannan Sadhasivam } 1246828dbc29SManivannan Sadhasivam 1247828dbc29SManivannan Sadhasivam ret = imx290_get_regulators(dev, imx290); 1248828dbc29SManivannan Sadhasivam if (ret < 0) { 1249828dbc29SManivannan Sadhasivam dev_err(dev, "Cannot get regulators\n"); 1250dfb704daSLaurent Pinchart goto err_endpoint; 1251828dbc29SManivannan Sadhasivam } 1252828dbc29SManivannan Sadhasivam 12533909a92dSAndrey Konovalov imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", 12543909a92dSAndrey Konovalov GPIOD_OUT_HIGH); 1255828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->rst_gpio)) { 1256828dbc29SManivannan Sadhasivam dev_err(dev, "Cannot get reset gpio\n"); 1257828dbc29SManivannan Sadhasivam ret = PTR_ERR(imx290->rst_gpio); 1258dfb704daSLaurent Pinchart goto err_endpoint; 1259828dbc29SManivannan Sadhasivam } 1260828dbc29SManivannan Sadhasivam 1261828dbc29SManivannan Sadhasivam mutex_init(&imx290->lock); 1262828dbc29SManivannan Sadhasivam 1263dfb704daSLaurent Pinchart ret = imx290_subdev_init(imx290); 1264dfb704daSLaurent Pinchart if (ret) 1265dfb704daSLaurent Pinchart goto err_mutex; 1266828dbc29SManivannan Sadhasivam 1267828dbc29SManivannan Sadhasivam ret = v4l2_async_register_subdev(&imx290->sd); 1268828dbc29SManivannan Sadhasivam if (ret < 0) { 1269828dbc29SManivannan Sadhasivam dev_err(dev, "Could not register v4l2 device\n"); 1270dfb704daSLaurent Pinchart goto err_subdev; 1271828dbc29SManivannan Sadhasivam } 1272828dbc29SManivannan Sadhasivam 1273828dbc29SManivannan Sadhasivam /* Power on the device to match runtime PM state below */ 1274828dbc29SManivannan Sadhasivam ret = imx290_power_on(dev); 1275828dbc29SManivannan Sadhasivam if (ret < 0) { 1276828dbc29SManivannan Sadhasivam dev_err(dev, "Could not power on the device\n"); 1277dfb704daSLaurent Pinchart goto err_subdev; 1278828dbc29SManivannan Sadhasivam } 1279828dbc29SManivannan Sadhasivam 1280828dbc29SManivannan Sadhasivam pm_runtime_set_active(dev); 1281828dbc29SManivannan Sadhasivam pm_runtime_enable(dev); 1282828dbc29SManivannan Sadhasivam pm_runtime_idle(dev); 1283828dbc29SManivannan Sadhasivam 1284a2706758SAndrey Konovalov v4l2_fwnode_endpoint_free(&ep); 1285828dbc29SManivannan Sadhasivam 1286828dbc29SManivannan Sadhasivam return 0; 1287828dbc29SManivannan Sadhasivam 1288dfb704daSLaurent Pinchart err_subdev: 1289dfb704daSLaurent Pinchart imx290_subdev_cleanup(imx290); 1290dfb704daSLaurent Pinchart err_mutex: 1291828dbc29SManivannan Sadhasivam mutex_destroy(&imx290->lock); 1292dfb704daSLaurent Pinchart err_endpoint: 1293a2706758SAndrey Konovalov v4l2_fwnode_endpoint_free(&ep); 1294828dbc29SManivannan Sadhasivam 1295828dbc29SManivannan Sadhasivam return ret; 1296828dbc29SManivannan Sadhasivam } 1297828dbc29SManivannan Sadhasivam 1298ed5c2f5fSUwe Kleine-König static void imx290_remove(struct i2c_client *client) 1299828dbc29SManivannan Sadhasivam { 1300828dbc29SManivannan Sadhasivam struct v4l2_subdev *sd = i2c_get_clientdata(client); 1301828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 1302828dbc29SManivannan Sadhasivam 1303828dbc29SManivannan Sadhasivam v4l2_async_unregister_subdev(sd); 1304dfb704daSLaurent Pinchart imx290_subdev_cleanup(imx290); 1305828dbc29SManivannan Sadhasivam 1306828dbc29SManivannan Sadhasivam mutex_destroy(&imx290->lock); 1307828dbc29SManivannan Sadhasivam 1308828dbc29SManivannan Sadhasivam pm_runtime_disable(imx290->dev); 1309828dbc29SManivannan Sadhasivam if (!pm_runtime_status_suspended(imx290->dev)) 1310828dbc29SManivannan Sadhasivam imx290_power_off(imx290->dev); 1311828dbc29SManivannan Sadhasivam pm_runtime_set_suspended(imx290->dev); 1312828dbc29SManivannan Sadhasivam } 1313828dbc29SManivannan Sadhasivam 1314828dbc29SManivannan Sadhasivam static const struct of_device_id imx290_of_match[] = { 1315828dbc29SManivannan Sadhasivam { .compatible = "sony,imx290" }, 1316828dbc29SManivannan Sadhasivam { /* sentinel */ } 1317828dbc29SManivannan Sadhasivam }; 1318828dbc29SManivannan Sadhasivam MODULE_DEVICE_TABLE(of, imx290_of_match); 1319828dbc29SManivannan Sadhasivam 1320828dbc29SManivannan Sadhasivam static struct i2c_driver imx290_i2c_driver = { 1321828dbc29SManivannan Sadhasivam .probe_new = imx290_probe, 1322828dbc29SManivannan Sadhasivam .remove = imx290_remove, 1323828dbc29SManivannan Sadhasivam .driver = { 1324828dbc29SManivannan Sadhasivam .name = "imx290", 1325828dbc29SManivannan Sadhasivam .pm = &imx290_pm_ops, 1326828dbc29SManivannan Sadhasivam .of_match_table = of_match_ptr(imx290_of_match), 1327828dbc29SManivannan Sadhasivam }, 1328828dbc29SManivannan Sadhasivam }; 1329828dbc29SManivannan Sadhasivam 1330828dbc29SManivannan Sadhasivam module_i2c_driver(imx290_i2c_driver); 1331828dbc29SManivannan Sadhasivam 1332828dbc29SManivannan Sadhasivam MODULE_DESCRIPTION("Sony IMX290 CMOS Image Sensor Driver"); 1333828dbc29SManivannan Sadhasivam MODULE_AUTHOR("FRAMOS GmbH"); 1334828dbc29SManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 1335828dbc29SManivannan Sadhasivam MODULE_LICENSE("GPL v2"); 1336