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 108828dbc29SManivannan Sadhasivam static const char * const imx290_supply_name[] = { 109828dbc29SManivannan Sadhasivam "vdda", 110828dbc29SManivannan Sadhasivam "vddd", 111828dbc29SManivannan Sadhasivam "vdddo", 112828dbc29SManivannan Sadhasivam }; 113828dbc29SManivannan Sadhasivam 114828dbc29SManivannan Sadhasivam #define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name) 115828dbc29SManivannan Sadhasivam 116828dbc29SManivannan Sadhasivam struct imx290_regval { 117e70abe88SLaurent Pinchart u32 reg; 11879d99ae8SLaurent Pinchart u32 val; 119828dbc29SManivannan Sadhasivam }; 120828dbc29SManivannan Sadhasivam 121828dbc29SManivannan Sadhasivam struct imx290_mode { 122828dbc29SManivannan Sadhasivam u32 width; 123828dbc29SManivannan Sadhasivam u32 height; 12497589ad6SManivannan Sadhasivam u32 hmax; 12598e0500eSManivannan Sadhasivam u8 link_freq_index; 126828dbc29SManivannan Sadhasivam 127828dbc29SManivannan Sadhasivam const struct imx290_regval *data; 128828dbc29SManivannan Sadhasivam u32 data_size; 129828dbc29SManivannan Sadhasivam }; 130828dbc29SManivannan Sadhasivam 131828dbc29SManivannan Sadhasivam struct imx290 { 132828dbc29SManivannan Sadhasivam struct device *dev; 133828dbc29SManivannan Sadhasivam struct clk *xclk; 134828dbc29SManivannan Sadhasivam struct regmap *regmap; 13597589ad6SManivannan Sadhasivam u8 nlanes; 136c566ac01SManivannan Sadhasivam u8 bpp; 137828dbc29SManivannan Sadhasivam 138828dbc29SManivannan Sadhasivam struct v4l2_subdev sd; 139828dbc29SManivannan Sadhasivam struct media_pad pad; 140828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt current_format; 141828dbc29SManivannan Sadhasivam const struct imx290_mode *current_mode; 142828dbc29SManivannan Sadhasivam 143828dbc29SManivannan Sadhasivam struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES]; 144828dbc29SManivannan Sadhasivam struct gpio_desc *rst_gpio; 145828dbc29SManivannan Sadhasivam 146828dbc29SManivannan Sadhasivam struct v4l2_ctrl_handler ctrls; 147828dbc29SManivannan Sadhasivam struct v4l2_ctrl *link_freq; 148828dbc29SManivannan Sadhasivam struct v4l2_ctrl *pixel_rate; 1490c3b56c9SLaurent Pinchart struct v4l2_ctrl *hblank; 1500c3b56c9SLaurent Pinchart struct v4l2_ctrl *vblank; 151828dbc29SManivannan Sadhasivam 152828dbc29SManivannan Sadhasivam struct mutex lock; 153828dbc29SManivannan Sadhasivam }; 154828dbc29SManivannan Sadhasivam 155828dbc29SManivannan Sadhasivam struct imx290_pixfmt { 156828dbc29SManivannan Sadhasivam u32 code; 157c566ac01SManivannan Sadhasivam u8 bpp; 158828dbc29SManivannan Sadhasivam }; 159828dbc29SManivannan Sadhasivam 160828dbc29SManivannan Sadhasivam static const struct imx290_pixfmt imx290_formats[] = { 161c566ac01SManivannan Sadhasivam { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 162c566ac01SManivannan Sadhasivam { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 163828dbc29SManivannan Sadhasivam }; 164828dbc29SManivannan Sadhasivam 165828dbc29SManivannan Sadhasivam static const struct regmap_config imx290_regmap_config = { 166828dbc29SManivannan Sadhasivam .reg_bits = 16, 167828dbc29SManivannan Sadhasivam .val_bits = 8, 168828dbc29SManivannan Sadhasivam }; 169828dbc29SManivannan Sadhasivam 170a58df1f9SManivannan Sadhasivam static const char * const imx290_test_pattern_menu[] = { 171a58df1f9SManivannan Sadhasivam "Disabled", 172a58df1f9SManivannan Sadhasivam "Sequence Pattern 1", 173a58df1f9SManivannan Sadhasivam "Horizontal Color-bar Chart", 174a58df1f9SManivannan Sadhasivam "Vertical Color-bar Chart", 175a58df1f9SManivannan Sadhasivam "Sequence Pattern 2", 176a58df1f9SManivannan Sadhasivam "Gradation Pattern 1", 177a58df1f9SManivannan Sadhasivam "Gradation Pattern 2", 178a58df1f9SManivannan Sadhasivam "000/555h Toggle Pattern", 179a58df1f9SManivannan Sadhasivam }; 180a58df1f9SManivannan Sadhasivam 181828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_global_init_settings[] = { 18279d99ae8SLaurent Pinchart { IMX290_CTRL_07, IMX290_WINMODE_1080P }, 183827c7e69SLaurent Pinchart { IMX290_VMAX, IMX290_VMAX_DEFAULT }, 18479d99ae8SLaurent Pinchart { IMX290_EXTCK_FREQ, 0x2520 }, 18579d99ae8SLaurent Pinchart { IMX290_WINWV_OB, 12 }, 18679d99ae8SLaurent Pinchart { IMX290_WINPH, 0 }, 18779d99ae8SLaurent Pinchart { IMX290_WINPV, 0 }, 18879d99ae8SLaurent Pinchart { IMX290_WINWH, 1948 }, 18979d99ae8SLaurent Pinchart { IMX290_WINWV, 1097 }, 19079d99ae8SLaurent Pinchart { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | 19179d99ae8SLaurent Pinchart IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, 192e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x300f), 0x00 }, 193e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3010), 0x21 }, 194e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3012), 0x64 }, 195*0b274ef2SLaurent Pinchart { IMX290_REG_8BIT(0x3013), 0x00 }, 196e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3016), 0x09 }, 197e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3070), 0x02 }, 198e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3071), 0x11 }, 199e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x309b), 0x10 }, 200e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x309c), 0x22 }, 201e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30a2), 0x02 }, 202e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30a6), 0x20 }, 203e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30a8), 0x20 }, 204e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30aa), 0x20 }, 205e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30ac), 0x20 }, 206e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x30b0), 0x43 }, 207e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3119), 0x9e }, 208e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x311c), 0x1e }, 209e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x311e), 0x08 }, 210e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3128), 0x05 }, 211e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x313d), 0x83 }, 212e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3150), 0x03 }, 213e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x317e), 0x00 }, 214e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32b8), 0x50 }, 215e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32b9), 0x10 }, 216e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32ba), 0x00 }, 217e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32bb), 0x04 }, 218e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32c8), 0x50 }, 219e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32c9), 0x10 }, 220e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32ca), 0x00 }, 221e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x32cb), 0x04 }, 222e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x332c), 0xd3 }, 223e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x332d), 0x10 }, 224e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x332e), 0x0d }, 225e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3358), 0x06 }, 226e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3359), 0xe1 }, 227e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x335a), 0x11 }, 228e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3360), 0x1e }, 229e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3361), 0x61 }, 230e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x3362), 0x10 }, 231e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x33b0), 0x50 }, 232e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x33b2), 0x1a }, 233e70abe88SLaurent Pinchart { IMX290_REG_8BIT(0x33b3), 0x04 }, 234*0b274ef2SLaurent Pinchart { IMX290_REG_8BIT(0x3480), 0x49 }, 235828dbc29SManivannan Sadhasivam }; 236828dbc29SManivannan Sadhasivam 237828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_1080p_settings[] = { 238828dbc29SManivannan Sadhasivam /* mode settings */ 23979d99ae8SLaurent Pinchart { IMX290_CTRL_07, IMX290_WINMODE_1080P }, 24079d99ae8SLaurent Pinchart { IMX290_WINWV_OB, 12 }, 24179d99ae8SLaurent Pinchart { IMX290_OPB_SIZE_V, 10 }, 24279d99ae8SLaurent Pinchart { IMX290_X_OUT_SIZE, 1920 }, 24379d99ae8SLaurent Pinchart { IMX290_Y_OUT_SIZE, 1080 }, 24479d99ae8SLaurent Pinchart { IMX290_INCKSEL1, 0x18 }, 24579d99ae8SLaurent Pinchart { IMX290_INCKSEL2, 0x03 }, 24679d99ae8SLaurent Pinchart { IMX290_INCKSEL3, 0x20 }, 24779d99ae8SLaurent Pinchart { IMX290_INCKSEL4, 0x01 }, 24879d99ae8SLaurent Pinchart { IMX290_INCKSEL5, 0x1a }, 24979d99ae8SLaurent Pinchart { IMX290_INCKSEL6, 0x1a }, 250828dbc29SManivannan Sadhasivam /* data rate settings */ 25179d99ae8SLaurent Pinchart { IMX290_REPETITION, 0x10 }, 25279d99ae8SLaurent Pinchart { IMX290_TCLKPOST, 87 }, 25379d99ae8SLaurent Pinchart { IMX290_THSZERO, 55 }, 25479d99ae8SLaurent Pinchart { IMX290_THSPREPARE, 31 }, 25579d99ae8SLaurent Pinchart { IMX290_TCLKTRAIL, 31 }, 25679d99ae8SLaurent Pinchart { IMX290_THSTRAIL, 31 }, 25779d99ae8SLaurent Pinchart { IMX290_TCLKZERO, 119 }, 25879d99ae8SLaurent Pinchart { IMX290_TCLKPREPARE, 31 }, 25979d99ae8SLaurent Pinchart { IMX290_TLPX, 23 }, 260828dbc29SManivannan Sadhasivam }; 261828dbc29SManivannan Sadhasivam 262828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_720p_settings[] = { 263828dbc29SManivannan Sadhasivam /* mode settings */ 26479d99ae8SLaurent Pinchart { IMX290_CTRL_07, IMX290_WINMODE_720P }, 26579d99ae8SLaurent Pinchart { IMX290_WINWV_OB, 6 }, 26679d99ae8SLaurent Pinchart { IMX290_OPB_SIZE_V, 4 }, 26779d99ae8SLaurent Pinchart { IMX290_X_OUT_SIZE, 1280 }, 26879d99ae8SLaurent Pinchart { IMX290_Y_OUT_SIZE, 720 }, 26979d99ae8SLaurent Pinchart { IMX290_INCKSEL1, 0x20 }, 27079d99ae8SLaurent Pinchart { IMX290_INCKSEL2, 0x00 }, 27179d99ae8SLaurent Pinchart { IMX290_INCKSEL3, 0x20 }, 27279d99ae8SLaurent Pinchart { IMX290_INCKSEL4, 0x01 }, 27379d99ae8SLaurent Pinchart { IMX290_INCKSEL5, 0x1a }, 27479d99ae8SLaurent Pinchart { IMX290_INCKSEL6, 0x1a }, 275828dbc29SManivannan Sadhasivam /* data rate settings */ 27679d99ae8SLaurent Pinchart { IMX290_REPETITION, 0x10 }, 27779d99ae8SLaurent Pinchart { IMX290_TCLKPOST, 79 }, 27879d99ae8SLaurent Pinchart { IMX290_THSZERO, 47 }, 27979d99ae8SLaurent Pinchart { IMX290_THSPREPARE, 23 }, 28079d99ae8SLaurent Pinchart { IMX290_TCLKTRAIL, 23 }, 28179d99ae8SLaurent Pinchart { IMX290_THSTRAIL, 23 }, 28279d99ae8SLaurent Pinchart { IMX290_TCLKZERO, 87 }, 28379d99ae8SLaurent Pinchart { IMX290_TCLKPREPARE, 23 }, 28479d99ae8SLaurent Pinchart { IMX290_TLPX, 23 }, 285828dbc29SManivannan Sadhasivam }; 286828dbc29SManivannan Sadhasivam 287828dbc29SManivannan Sadhasivam static const struct imx290_regval imx290_10bit_settings[] = { 28879d99ae8SLaurent Pinchart { IMX290_ADBIT, IMX290_ADBIT_10BIT }, 28979d99ae8SLaurent Pinchart { IMX290_OUT_CTRL, IMX290_ODBIT_10BIT }, 29079d99ae8SLaurent Pinchart { IMX290_ADBIT1, IMX290_ADBIT1_10BIT }, 29179d99ae8SLaurent Pinchart { IMX290_ADBIT2, IMX290_ADBIT2_10BIT }, 29279d99ae8SLaurent Pinchart { IMX290_ADBIT3, IMX290_ADBIT3_10BIT }, 29379d99ae8SLaurent Pinchart { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 }, 29479d99ae8SLaurent Pinchart { IMX290_BLKLEVEL, 60 }, 295828dbc29SManivannan Sadhasivam }; 296828dbc29SManivannan Sadhasivam 297c566ac01SManivannan Sadhasivam static const struct imx290_regval imx290_12bit_settings[] = { 29879d99ae8SLaurent Pinchart { IMX290_ADBIT, IMX290_ADBIT_12BIT }, 29979d99ae8SLaurent Pinchart { IMX290_OUT_CTRL, IMX290_ODBIT_12BIT }, 30079d99ae8SLaurent Pinchart { IMX290_ADBIT1, IMX290_ADBIT1_12BIT }, 30179d99ae8SLaurent Pinchart { IMX290_ADBIT2, IMX290_ADBIT2_12BIT }, 30279d99ae8SLaurent Pinchart { IMX290_ADBIT3, IMX290_ADBIT3_12BIT }, 30379d99ae8SLaurent Pinchart { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 }, 30479d99ae8SLaurent Pinchart { IMX290_BLKLEVEL, 240 }, 305c566ac01SManivannan Sadhasivam }; 306c566ac01SManivannan Sadhasivam 307828dbc29SManivannan Sadhasivam /* supported link frequencies */ 30898e0500eSManivannan Sadhasivam #define FREQ_INDEX_1080P 0 30998e0500eSManivannan Sadhasivam #define FREQ_INDEX_720P 1 31098e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_2lanes[] = { 31198e0500eSManivannan Sadhasivam [FREQ_INDEX_1080P] = 445500000, 31298e0500eSManivannan Sadhasivam [FREQ_INDEX_720P] = 297000000, 313828dbc29SManivannan Sadhasivam }; 31498e0500eSManivannan Sadhasivam static const s64 imx290_link_freq_4lanes[] = { 31598e0500eSManivannan Sadhasivam [FREQ_INDEX_1080P] = 222750000, 31698e0500eSManivannan Sadhasivam [FREQ_INDEX_720P] = 148500000, 31798e0500eSManivannan Sadhasivam }; 31898e0500eSManivannan Sadhasivam 31998e0500eSManivannan Sadhasivam /* 32098e0500eSManivannan Sadhasivam * In this function and in the similar ones below We rely on imx290_probe() 32198e0500eSManivannan Sadhasivam * to ensure that nlanes is either 2 or 4. 32298e0500eSManivannan Sadhasivam */ 32398e0500eSManivannan Sadhasivam static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290) 32498e0500eSManivannan Sadhasivam { 32598e0500eSManivannan Sadhasivam if (imx290->nlanes == 2) 32698e0500eSManivannan Sadhasivam return imx290_link_freq_2lanes; 32798e0500eSManivannan Sadhasivam else 32898e0500eSManivannan Sadhasivam return imx290_link_freq_4lanes; 32998e0500eSManivannan Sadhasivam } 33098e0500eSManivannan Sadhasivam 33198e0500eSManivannan Sadhasivam static inline int imx290_link_freqs_num(const struct imx290 *imx290) 33298e0500eSManivannan Sadhasivam { 33398e0500eSManivannan Sadhasivam if (imx290->nlanes == 2) 33498e0500eSManivannan Sadhasivam return ARRAY_SIZE(imx290_link_freq_2lanes); 33598e0500eSManivannan Sadhasivam else 33698e0500eSManivannan Sadhasivam return ARRAY_SIZE(imx290_link_freq_4lanes); 33798e0500eSManivannan Sadhasivam } 338828dbc29SManivannan Sadhasivam 339828dbc29SManivannan Sadhasivam /* Mode configs */ 34097589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_2lanes[] = { 341828dbc29SManivannan Sadhasivam { 342828dbc29SManivannan Sadhasivam .width = 1920, 343828dbc29SManivannan Sadhasivam .height = 1080, 34472825bc6SLaurent Pinchart .hmax = 4400, 34598e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_1080P, 346828dbc29SManivannan Sadhasivam .data = imx290_1080p_settings, 347828dbc29SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_1080p_settings), 348828dbc29SManivannan Sadhasivam }, 349828dbc29SManivannan Sadhasivam { 350828dbc29SManivannan Sadhasivam .width = 1280, 351828dbc29SManivannan Sadhasivam .height = 720, 35272825bc6SLaurent Pinchart .hmax = 6600, 35398e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_720P, 354828dbc29SManivannan Sadhasivam .data = imx290_720p_settings, 355828dbc29SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_720p_settings), 356828dbc29SManivannan Sadhasivam }, 357828dbc29SManivannan Sadhasivam }; 358828dbc29SManivannan Sadhasivam 35997589ad6SManivannan Sadhasivam static const struct imx290_mode imx290_modes_4lanes[] = { 36097589ad6SManivannan Sadhasivam { 36197589ad6SManivannan Sadhasivam .width = 1920, 36297589ad6SManivannan Sadhasivam .height = 1080, 36372825bc6SLaurent Pinchart .hmax = 2200, 36498e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_1080P, 36597589ad6SManivannan Sadhasivam .data = imx290_1080p_settings, 36697589ad6SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_1080p_settings), 36797589ad6SManivannan Sadhasivam }, 36897589ad6SManivannan Sadhasivam { 36997589ad6SManivannan Sadhasivam .width = 1280, 37097589ad6SManivannan Sadhasivam .height = 720, 37172825bc6SLaurent Pinchart .hmax = 3300, 37298e0500eSManivannan Sadhasivam .link_freq_index = FREQ_INDEX_720P, 37397589ad6SManivannan Sadhasivam .data = imx290_720p_settings, 37497589ad6SManivannan Sadhasivam .data_size = ARRAY_SIZE(imx290_720p_settings), 37597589ad6SManivannan Sadhasivam }, 37697589ad6SManivannan Sadhasivam }; 37797589ad6SManivannan Sadhasivam 37897589ad6SManivannan Sadhasivam static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) 37997589ad6SManivannan Sadhasivam { 38097589ad6SManivannan Sadhasivam if (imx290->nlanes == 2) 38197589ad6SManivannan Sadhasivam return imx290_modes_2lanes; 38297589ad6SManivannan Sadhasivam else 38397589ad6SManivannan Sadhasivam return imx290_modes_4lanes; 38497589ad6SManivannan Sadhasivam } 38597589ad6SManivannan Sadhasivam 38697589ad6SManivannan Sadhasivam static inline int imx290_modes_num(const struct imx290 *imx290) 38797589ad6SManivannan Sadhasivam { 38897589ad6SManivannan Sadhasivam if (imx290->nlanes == 2) 38997589ad6SManivannan Sadhasivam return ARRAY_SIZE(imx290_modes_2lanes); 39097589ad6SManivannan Sadhasivam else 39197589ad6SManivannan Sadhasivam return ARRAY_SIZE(imx290_modes_4lanes); 39297589ad6SManivannan Sadhasivam } 39397589ad6SManivannan Sadhasivam 394828dbc29SManivannan Sadhasivam static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) 395828dbc29SManivannan Sadhasivam { 396828dbc29SManivannan Sadhasivam return container_of(_sd, struct imx290, sd); 397828dbc29SManivannan Sadhasivam } 398828dbc29SManivannan Sadhasivam 399e611f3daSLaurent Pinchart static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value) 400828dbc29SManivannan Sadhasivam { 401e70abe88SLaurent Pinchart u8 data[3] = { 0, 0, 0 }; 402828dbc29SManivannan Sadhasivam int ret; 403828dbc29SManivannan Sadhasivam 404e70abe88SLaurent Pinchart ret = regmap_raw_read(imx290->regmap, addr & IMX290_REG_ADDR_MASK, 405e70abe88SLaurent Pinchart data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); 406e70abe88SLaurent Pinchart if (ret < 0) { 407e70abe88SLaurent Pinchart dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", 408e70abe88SLaurent Pinchart ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, 409e70abe88SLaurent Pinchart addr & IMX290_REG_ADDR_MASK, ret); 410828dbc29SManivannan Sadhasivam return ret; 411828dbc29SManivannan Sadhasivam } 412828dbc29SManivannan Sadhasivam 413e70abe88SLaurent Pinchart *value = (data[2] << 16) | (data[1] << 8) | data[0]; 414828dbc29SManivannan Sadhasivam return 0; 415828dbc29SManivannan Sadhasivam } 416828dbc29SManivannan Sadhasivam 417e611f3daSLaurent Pinchart static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) 418828dbc29SManivannan Sadhasivam { 419e70abe88SLaurent Pinchart u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 }; 420828dbc29SManivannan Sadhasivam int ret; 421828dbc29SManivannan Sadhasivam 422e611f3daSLaurent Pinchart if (err && *err) 423e611f3daSLaurent Pinchart return *err; 424e611f3daSLaurent Pinchart 425e70abe88SLaurent Pinchart ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, 426e70abe88SLaurent Pinchart data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); 427e611f3daSLaurent Pinchart if (ret < 0) { 428e70abe88SLaurent Pinchart dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", 429e70abe88SLaurent Pinchart ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, 430e70abe88SLaurent Pinchart addr & IMX290_REG_ADDR_MASK, ret); 431e611f3daSLaurent Pinchart if (err) 432e611f3daSLaurent Pinchart *err = ret; 433e611f3daSLaurent Pinchart } 434828dbc29SManivannan Sadhasivam 435828dbc29SManivannan Sadhasivam return ret; 436828dbc29SManivannan Sadhasivam } 437828dbc29SManivannan Sadhasivam 438828dbc29SManivannan Sadhasivam static int imx290_set_register_array(struct imx290 *imx290, 439828dbc29SManivannan Sadhasivam const struct imx290_regval *settings, 440828dbc29SManivannan Sadhasivam unsigned int num_settings) 441828dbc29SManivannan Sadhasivam { 442828dbc29SManivannan Sadhasivam unsigned int i; 443828dbc29SManivannan Sadhasivam int ret; 444828dbc29SManivannan Sadhasivam 445828dbc29SManivannan Sadhasivam for (i = 0; i < num_settings; ++i, ++settings) { 446e611f3daSLaurent Pinchart ret = imx290_write(imx290, settings->reg, settings->val, NULL); 447828dbc29SManivannan Sadhasivam if (ret < 0) 448828dbc29SManivannan Sadhasivam return ret; 449828dbc29SManivannan Sadhasivam } 450828dbc29SManivannan Sadhasivam 4516544af9bSManivannan Sadhasivam /* Provide 10ms settle time */ 4528e5652aeSAndrey Konovalov usleep_range(10000, 11000); 4536544af9bSManivannan Sadhasivam 454828dbc29SManivannan Sadhasivam return 0; 455828dbc29SManivannan Sadhasivam } 456828dbc29SManivannan Sadhasivam 457828dbc29SManivannan Sadhasivam /* Stop streaming */ 458828dbc29SManivannan Sadhasivam static int imx290_stop_streaming(struct imx290 *imx290) 459828dbc29SManivannan Sadhasivam { 460e611f3daSLaurent Pinchart int ret = 0; 461828dbc29SManivannan Sadhasivam 462e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_STANDBY, 0x01, &ret); 463828dbc29SManivannan Sadhasivam 464828dbc29SManivannan Sadhasivam msleep(30); 465828dbc29SManivannan Sadhasivam 466e611f3daSLaurent Pinchart return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret); 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: 481e611f3daSLaurent Pinchart ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); 482828dbc29SManivannan Sadhasivam break; 483827c7e69SLaurent Pinchart 484827c7e69SLaurent Pinchart case V4L2_CID_EXPOSURE: 485827c7e69SLaurent Pinchart ret = imx290_write(imx290, IMX290_SHS1, 486827c7e69SLaurent Pinchart IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL); 487827c7e69SLaurent Pinchart break; 488827c7e69SLaurent Pinchart 489a58df1f9SManivannan Sadhasivam case V4L2_CID_TEST_PATTERN: 490a58df1f9SManivannan Sadhasivam if (ctrl->val) { 491e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret); 4928e5652aeSAndrey Konovalov usleep_range(10000, 11000); 493e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_PGCTRL, 494a58df1f9SManivannan Sadhasivam (u8)(IMX290_PGCTRL_REGEN | 495a58df1f9SManivannan Sadhasivam IMX290_PGCTRL_THRU | 496e611f3daSLaurent Pinchart IMX290_PGCTRL_MODE(ctrl->val)), &ret); 497a58df1f9SManivannan Sadhasivam } else { 498e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret); 4998e5652aeSAndrey Konovalov usleep_range(10000, 11000); 500c566ac01SManivannan Sadhasivam if (imx290->bpp == 10) 501e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_BLKLEVEL, 0x3c, 502e611f3daSLaurent Pinchart &ret); 503c566ac01SManivannan Sadhasivam else /* 12 bits per pixel */ 504e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_BLKLEVEL, 0xf0, 505e611f3daSLaurent Pinchart &ret); 506a58df1f9SManivannan Sadhasivam } 507a58df1f9SManivannan Sadhasivam break; 508828dbc29SManivannan Sadhasivam default: 509828dbc29SManivannan Sadhasivam ret = -EINVAL; 510828dbc29SManivannan Sadhasivam break; 511828dbc29SManivannan Sadhasivam } 512828dbc29SManivannan Sadhasivam 513828dbc29SManivannan Sadhasivam pm_runtime_put(imx290->dev); 514828dbc29SManivannan Sadhasivam 515828dbc29SManivannan Sadhasivam return ret; 516828dbc29SManivannan Sadhasivam } 517828dbc29SManivannan Sadhasivam 518828dbc29SManivannan Sadhasivam static const struct v4l2_ctrl_ops imx290_ctrl_ops = { 519828dbc29SManivannan Sadhasivam .s_ctrl = imx290_set_ctrl, 520828dbc29SManivannan Sadhasivam }; 521828dbc29SManivannan Sadhasivam 522828dbc29SManivannan Sadhasivam static int imx290_enum_mbus_code(struct v4l2_subdev *sd, 5230d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 524828dbc29SManivannan Sadhasivam struct v4l2_subdev_mbus_code_enum *code) 525828dbc29SManivannan Sadhasivam { 526828dbc29SManivannan Sadhasivam if (code->index >= ARRAY_SIZE(imx290_formats)) 527828dbc29SManivannan Sadhasivam return -EINVAL; 528828dbc29SManivannan Sadhasivam 529828dbc29SManivannan Sadhasivam code->code = imx290_formats[code->index].code; 530828dbc29SManivannan Sadhasivam 531828dbc29SManivannan Sadhasivam return 0; 532828dbc29SManivannan Sadhasivam } 533828dbc29SManivannan Sadhasivam 5343b867fb6SManivannan Sadhasivam static int imx290_enum_frame_size(struct v4l2_subdev *sd, 5350d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 5363b867fb6SManivannan Sadhasivam struct v4l2_subdev_frame_size_enum *fse) 5373b867fb6SManivannan Sadhasivam { 5383b867fb6SManivannan Sadhasivam const struct imx290 *imx290 = to_imx290(sd); 5393b867fb6SManivannan Sadhasivam const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); 5403b867fb6SManivannan Sadhasivam 5413b867fb6SManivannan Sadhasivam if ((fse->code != imx290_formats[0].code) && 5423b867fb6SManivannan Sadhasivam (fse->code != imx290_formats[1].code)) 5433b867fb6SManivannan Sadhasivam return -EINVAL; 5443b867fb6SManivannan Sadhasivam 5453b867fb6SManivannan Sadhasivam if (fse->index >= imx290_modes_num(imx290)) 5463b867fb6SManivannan Sadhasivam return -EINVAL; 5473b867fb6SManivannan Sadhasivam 5483b867fb6SManivannan Sadhasivam fse->min_width = imx290_modes[fse->index].width; 5493b867fb6SManivannan Sadhasivam fse->max_width = imx290_modes[fse->index].width; 5503b867fb6SManivannan Sadhasivam fse->min_height = imx290_modes[fse->index].height; 5513b867fb6SManivannan Sadhasivam fse->max_height = imx290_modes[fse->index].height; 5523b867fb6SManivannan Sadhasivam 5533b867fb6SManivannan Sadhasivam return 0; 5543b867fb6SManivannan Sadhasivam } 5553b867fb6SManivannan Sadhasivam 556828dbc29SManivannan Sadhasivam static int imx290_get_fmt(struct v4l2_subdev *sd, 5570d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 558828dbc29SManivannan Sadhasivam struct v4l2_subdev_format *fmt) 559828dbc29SManivannan Sadhasivam { 560828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 561828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt *framefmt; 562828dbc29SManivannan Sadhasivam 563828dbc29SManivannan Sadhasivam mutex_lock(&imx290->lock); 564828dbc29SManivannan Sadhasivam 565828dbc29SManivannan Sadhasivam if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 5660d346d2aSTomi Valkeinen framefmt = v4l2_subdev_get_try_format(&imx290->sd, sd_state, 567828dbc29SManivannan Sadhasivam fmt->pad); 568828dbc29SManivannan Sadhasivam else 569828dbc29SManivannan Sadhasivam framefmt = &imx290->current_format; 570828dbc29SManivannan Sadhasivam 571828dbc29SManivannan Sadhasivam fmt->format = *framefmt; 572828dbc29SManivannan Sadhasivam 573828dbc29SManivannan Sadhasivam mutex_unlock(&imx290->lock); 574828dbc29SManivannan Sadhasivam 575828dbc29SManivannan Sadhasivam return 0; 576828dbc29SManivannan Sadhasivam } 577828dbc29SManivannan Sadhasivam 57898e0500eSManivannan Sadhasivam static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) 57998e0500eSManivannan Sadhasivam { 58098e0500eSManivannan Sadhasivam return imx290->current_mode->link_freq_index; 58198e0500eSManivannan Sadhasivam } 58298e0500eSManivannan Sadhasivam 58398e0500eSManivannan Sadhasivam static s64 imx290_get_link_freq(struct imx290 *imx290) 58498e0500eSManivannan Sadhasivam { 58598e0500eSManivannan Sadhasivam u8 index = imx290_get_link_freq_index(imx290); 58698e0500eSManivannan Sadhasivam 58798e0500eSManivannan Sadhasivam return *(imx290_link_freqs_ptr(imx290) + index); 58898e0500eSManivannan Sadhasivam } 58998e0500eSManivannan Sadhasivam 59098e0500eSManivannan Sadhasivam static u64 imx290_calc_pixel_rate(struct imx290 *imx290) 59198e0500eSManivannan Sadhasivam { 59298e0500eSManivannan Sadhasivam s64 link_freq = imx290_get_link_freq(imx290); 59398e0500eSManivannan Sadhasivam u8 nlanes = imx290->nlanes; 59498e0500eSManivannan Sadhasivam u64 pixel_rate; 59598e0500eSManivannan Sadhasivam 59698e0500eSManivannan Sadhasivam /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ 59798e0500eSManivannan Sadhasivam pixel_rate = link_freq * 2 * nlanes; 598c566ac01SManivannan Sadhasivam do_div(pixel_rate, imx290->bpp); 59998e0500eSManivannan Sadhasivam return pixel_rate; 60098e0500eSManivannan Sadhasivam } 60198e0500eSManivannan Sadhasivam 602828dbc29SManivannan Sadhasivam static int imx290_set_fmt(struct v4l2_subdev *sd, 6030d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 604828dbc29SManivannan Sadhasivam struct v4l2_subdev_format *fmt) 605828dbc29SManivannan Sadhasivam { 606828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 607828dbc29SManivannan Sadhasivam const struct imx290_mode *mode; 608828dbc29SManivannan Sadhasivam struct v4l2_mbus_framefmt *format; 609828dbc29SManivannan Sadhasivam unsigned int i; 610828dbc29SManivannan Sadhasivam 611828dbc29SManivannan Sadhasivam mutex_lock(&imx290->lock); 612828dbc29SManivannan Sadhasivam 61397589ad6SManivannan Sadhasivam mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290), 61497589ad6SManivannan Sadhasivam imx290_modes_num(imx290), width, height, 615828dbc29SManivannan Sadhasivam fmt->format.width, fmt->format.height); 616828dbc29SManivannan Sadhasivam 617828dbc29SManivannan Sadhasivam fmt->format.width = mode->width; 618828dbc29SManivannan Sadhasivam fmt->format.height = mode->height; 619828dbc29SManivannan Sadhasivam 620828dbc29SManivannan Sadhasivam for (i = 0; i < ARRAY_SIZE(imx290_formats); i++) 621828dbc29SManivannan Sadhasivam if (imx290_formats[i].code == fmt->format.code) 622828dbc29SManivannan Sadhasivam break; 623828dbc29SManivannan Sadhasivam 624828dbc29SManivannan Sadhasivam if (i >= ARRAY_SIZE(imx290_formats)) 625828dbc29SManivannan Sadhasivam i = 0; 626828dbc29SManivannan Sadhasivam 627828dbc29SManivannan Sadhasivam fmt->format.code = imx290_formats[i].code; 628828dbc29SManivannan Sadhasivam fmt->format.field = V4L2_FIELD_NONE; 629828dbc29SManivannan Sadhasivam 630828dbc29SManivannan Sadhasivam if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 6310d346d2aSTomi Valkeinen format = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); 632828dbc29SManivannan Sadhasivam } else { 633828dbc29SManivannan Sadhasivam format = &imx290->current_format; 634828dbc29SManivannan Sadhasivam imx290->current_mode = mode; 635c566ac01SManivannan Sadhasivam imx290->bpp = imx290_formats[i].bpp; 63698e0500eSManivannan Sadhasivam 63798e0500eSManivannan Sadhasivam if (imx290->link_freq) 63898e0500eSManivannan Sadhasivam __v4l2_ctrl_s_ctrl(imx290->link_freq, 63998e0500eSManivannan Sadhasivam imx290_get_link_freq_index(imx290)); 64098e0500eSManivannan Sadhasivam if (imx290->pixel_rate) 64198e0500eSManivannan Sadhasivam __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, 64298e0500eSManivannan Sadhasivam imx290_calc_pixel_rate(imx290)); 6430c3b56c9SLaurent Pinchart 6440c3b56c9SLaurent Pinchart if (imx290->hblank) { 6450c3b56c9SLaurent Pinchart unsigned int hblank = mode->hmax - mode->width; 6460c3b56c9SLaurent Pinchart 6470c3b56c9SLaurent Pinchart __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 6480c3b56c9SLaurent Pinchart 1, hblank); 6490c3b56c9SLaurent Pinchart } 6500c3b56c9SLaurent Pinchart 6510c3b56c9SLaurent Pinchart if (imx290->vblank) { 6520c3b56c9SLaurent Pinchart unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; 6530c3b56c9SLaurent Pinchart 6540c3b56c9SLaurent Pinchart __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 6550c3b56c9SLaurent Pinchart 1, vblank); 6560c3b56c9SLaurent Pinchart } 657828dbc29SManivannan Sadhasivam } 658828dbc29SManivannan Sadhasivam 659828dbc29SManivannan Sadhasivam *format = fmt->format; 660828dbc29SManivannan Sadhasivam 661828dbc29SManivannan Sadhasivam mutex_unlock(&imx290->lock); 662828dbc29SManivannan Sadhasivam 663828dbc29SManivannan Sadhasivam return 0; 664828dbc29SManivannan Sadhasivam } 665828dbc29SManivannan Sadhasivam 666828dbc29SManivannan Sadhasivam static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, 6670d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state) 668828dbc29SManivannan Sadhasivam { 669828dbc29SManivannan Sadhasivam struct v4l2_subdev_format fmt = { 0 }; 670828dbc29SManivannan Sadhasivam 6710d346d2aSTomi Valkeinen fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; 672828dbc29SManivannan Sadhasivam fmt.format.width = 1920; 673828dbc29SManivannan Sadhasivam fmt.format.height = 1080; 674828dbc29SManivannan Sadhasivam 6750d346d2aSTomi Valkeinen imx290_set_fmt(subdev, sd_state, &fmt); 676828dbc29SManivannan Sadhasivam 677828dbc29SManivannan Sadhasivam return 0; 678828dbc29SManivannan Sadhasivam } 679828dbc29SManivannan Sadhasivam 68098e0500eSManivannan Sadhasivam static int imx290_write_current_format(struct imx290 *imx290) 681828dbc29SManivannan Sadhasivam { 682828dbc29SManivannan Sadhasivam int ret; 683828dbc29SManivannan Sadhasivam 68498e0500eSManivannan Sadhasivam switch (imx290->current_format.code) { 685828dbc29SManivannan Sadhasivam case MEDIA_BUS_FMT_SRGGB10_1X10: 686828dbc29SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290_10bit_settings, 687828dbc29SManivannan Sadhasivam ARRAY_SIZE( 688828dbc29SManivannan Sadhasivam imx290_10bit_settings)); 689828dbc29SManivannan Sadhasivam if (ret < 0) { 690828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set format registers\n"); 691828dbc29SManivannan Sadhasivam return ret; 692828dbc29SManivannan Sadhasivam } 693828dbc29SManivannan Sadhasivam break; 694c566ac01SManivannan Sadhasivam case MEDIA_BUS_FMT_SRGGB12_1X12: 695c566ac01SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290_12bit_settings, 696c566ac01SManivannan Sadhasivam ARRAY_SIZE( 697c566ac01SManivannan Sadhasivam imx290_12bit_settings)); 698c566ac01SManivannan Sadhasivam if (ret < 0) { 699c566ac01SManivannan Sadhasivam dev_err(imx290->dev, "Could not set format registers\n"); 700c566ac01SManivannan Sadhasivam return ret; 701c566ac01SManivannan Sadhasivam } 702c566ac01SManivannan Sadhasivam break; 703828dbc29SManivannan Sadhasivam default: 704828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Unknown pixel format\n"); 705828dbc29SManivannan Sadhasivam return -EINVAL; 706828dbc29SManivannan Sadhasivam } 707828dbc29SManivannan Sadhasivam 708828dbc29SManivannan Sadhasivam return 0; 709828dbc29SManivannan Sadhasivam } 710828dbc29SManivannan Sadhasivam 711828dbc29SManivannan Sadhasivam /* Start streaming */ 712828dbc29SManivannan Sadhasivam static int imx290_start_streaming(struct imx290 *imx290) 713828dbc29SManivannan Sadhasivam { 714828dbc29SManivannan Sadhasivam int ret; 715828dbc29SManivannan Sadhasivam 716828dbc29SManivannan Sadhasivam /* Set init register settings */ 717828dbc29SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290_global_init_settings, 718828dbc29SManivannan Sadhasivam ARRAY_SIZE( 719828dbc29SManivannan Sadhasivam imx290_global_init_settings)); 720828dbc29SManivannan Sadhasivam if (ret < 0) { 721828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set init registers\n"); 722828dbc29SManivannan Sadhasivam return ret; 723828dbc29SManivannan Sadhasivam } 724828dbc29SManivannan Sadhasivam 72598e0500eSManivannan Sadhasivam /* Apply the register values related to current frame format */ 72698e0500eSManivannan Sadhasivam ret = imx290_write_current_format(imx290); 727828dbc29SManivannan Sadhasivam if (ret < 0) { 728828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set frame format\n"); 729828dbc29SManivannan Sadhasivam return ret; 730828dbc29SManivannan Sadhasivam } 731828dbc29SManivannan Sadhasivam 732828dbc29SManivannan Sadhasivam /* Apply default values of current mode */ 733828dbc29SManivannan Sadhasivam ret = imx290_set_register_array(imx290, imx290->current_mode->data, 734828dbc29SManivannan Sadhasivam imx290->current_mode->data_size); 735828dbc29SManivannan Sadhasivam if (ret < 0) { 736828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not set current mode\n"); 737828dbc29SManivannan Sadhasivam return ret; 738828dbc29SManivannan Sadhasivam } 739454a86f3SLaurent Pinchart 740e611f3daSLaurent Pinchart ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax, 741e611f3daSLaurent Pinchart NULL); 742454a86f3SLaurent Pinchart if (ret) 74397589ad6SManivannan Sadhasivam return ret; 744828dbc29SManivannan Sadhasivam 745828dbc29SManivannan Sadhasivam /* Apply customized values from user */ 746828dbc29SManivannan Sadhasivam ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); 747828dbc29SManivannan Sadhasivam if (ret) { 748828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Could not sync v4l2 controls\n"); 749828dbc29SManivannan Sadhasivam return ret; 750828dbc29SManivannan Sadhasivam } 751828dbc29SManivannan Sadhasivam 752e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_STANDBY, 0x00, &ret); 753828dbc29SManivannan Sadhasivam 754828dbc29SManivannan Sadhasivam msleep(30); 755828dbc29SManivannan Sadhasivam 756828dbc29SManivannan Sadhasivam /* Start streaming */ 757e611f3daSLaurent Pinchart return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret); 758828dbc29SManivannan Sadhasivam } 759828dbc29SManivannan Sadhasivam 760828dbc29SManivannan Sadhasivam static int imx290_set_stream(struct v4l2_subdev *sd, int enable) 761828dbc29SManivannan Sadhasivam { 762828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 763828dbc29SManivannan Sadhasivam int ret = 0; 764828dbc29SManivannan Sadhasivam 765828dbc29SManivannan Sadhasivam if (enable) { 766739d9c64SMauro Carvalho Chehab ret = pm_runtime_resume_and_get(imx290->dev); 767739d9c64SMauro Carvalho Chehab if (ret < 0) 768828dbc29SManivannan Sadhasivam goto unlock_and_return; 769828dbc29SManivannan Sadhasivam 770828dbc29SManivannan Sadhasivam ret = imx290_start_streaming(imx290); 771828dbc29SManivannan Sadhasivam if (ret) { 772828dbc29SManivannan Sadhasivam dev_err(imx290->dev, "Start stream failed\n"); 773828dbc29SManivannan Sadhasivam pm_runtime_put(imx290->dev); 774828dbc29SManivannan Sadhasivam goto unlock_and_return; 775828dbc29SManivannan Sadhasivam } 776828dbc29SManivannan Sadhasivam } else { 777828dbc29SManivannan Sadhasivam imx290_stop_streaming(imx290); 778828dbc29SManivannan Sadhasivam pm_runtime_put(imx290->dev); 779828dbc29SManivannan Sadhasivam } 780828dbc29SManivannan Sadhasivam 781828dbc29SManivannan Sadhasivam unlock_and_return: 782828dbc29SManivannan Sadhasivam 783828dbc29SManivannan Sadhasivam return ret; 784828dbc29SManivannan Sadhasivam } 785828dbc29SManivannan Sadhasivam 786828dbc29SManivannan Sadhasivam static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) 787828dbc29SManivannan Sadhasivam { 788828dbc29SManivannan Sadhasivam unsigned int i; 789828dbc29SManivannan Sadhasivam 7902548df53SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++) 791828dbc29SManivannan Sadhasivam imx290->supplies[i].supply = imx290_supply_name[i]; 792828dbc29SManivannan Sadhasivam 7932548df53SLaurent Pinchart return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies), 794828dbc29SManivannan Sadhasivam imx290->supplies); 795828dbc29SManivannan Sadhasivam } 796828dbc29SManivannan Sadhasivam 79797589ad6SManivannan Sadhasivam static int imx290_set_data_lanes(struct imx290 *imx290) 79897589ad6SManivannan Sadhasivam { 79997589ad6SManivannan Sadhasivam int ret = 0, laneval, frsel; 80097589ad6SManivannan Sadhasivam 80197589ad6SManivannan Sadhasivam switch (imx290->nlanes) { 80297589ad6SManivannan Sadhasivam case 2: 80397589ad6SManivannan Sadhasivam laneval = 0x01; 80497589ad6SManivannan Sadhasivam frsel = 0x02; 80597589ad6SManivannan Sadhasivam break; 80697589ad6SManivannan Sadhasivam case 4: 80797589ad6SManivannan Sadhasivam laneval = 0x03; 80897589ad6SManivannan Sadhasivam frsel = 0x01; 80997589ad6SManivannan Sadhasivam break; 81097589ad6SManivannan Sadhasivam default: 81197589ad6SManivannan Sadhasivam /* 81297589ad6SManivannan Sadhasivam * We should never hit this since the data lane count is 81397589ad6SManivannan Sadhasivam * validated in probe itself 81497589ad6SManivannan Sadhasivam */ 81597589ad6SManivannan Sadhasivam dev_err(imx290->dev, "Lane configuration not supported\n"); 816e611f3daSLaurent Pinchart return -EINVAL; 81797589ad6SManivannan Sadhasivam } 81897589ad6SManivannan Sadhasivam 819e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret); 820e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret); 821e611f3daSLaurent Pinchart imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); 82297589ad6SManivannan Sadhasivam 82397589ad6SManivannan Sadhasivam return ret; 82497589ad6SManivannan Sadhasivam } 82597589ad6SManivannan Sadhasivam 826828dbc29SManivannan Sadhasivam static int imx290_power_on(struct device *dev) 827828dbc29SManivannan Sadhasivam { 828b50ce25dSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 829828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 830828dbc29SManivannan Sadhasivam int ret; 831828dbc29SManivannan Sadhasivam 832828dbc29SManivannan Sadhasivam ret = clk_prepare_enable(imx290->xclk); 833828dbc29SManivannan Sadhasivam if (ret) { 834b50ce25dSKrzysztof Kozlowski dev_err(dev, "Failed to enable clock\n"); 835828dbc29SManivannan Sadhasivam return ret; 836828dbc29SManivannan Sadhasivam } 837828dbc29SManivannan Sadhasivam 8382548df53SLaurent Pinchart ret = regulator_bulk_enable(ARRAY_SIZE(imx290->supplies), 8392548df53SLaurent Pinchart imx290->supplies); 840828dbc29SManivannan Sadhasivam if (ret) { 841b50ce25dSKrzysztof Kozlowski dev_err(dev, "Failed to enable regulators\n"); 842828dbc29SManivannan Sadhasivam clk_disable_unprepare(imx290->xclk); 843828dbc29SManivannan Sadhasivam return ret; 844828dbc29SManivannan Sadhasivam } 845828dbc29SManivannan Sadhasivam 846828dbc29SManivannan Sadhasivam usleep_range(1, 2); 8473909a92dSAndrey Konovalov gpiod_set_value_cansleep(imx290->rst_gpio, 0); 848828dbc29SManivannan Sadhasivam usleep_range(30000, 31000); 849828dbc29SManivannan Sadhasivam 85097589ad6SManivannan Sadhasivam /* Set data lane count */ 85197589ad6SManivannan Sadhasivam imx290_set_data_lanes(imx290); 85297589ad6SManivannan Sadhasivam 853828dbc29SManivannan Sadhasivam return 0; 854828dbc29SManivannan Sadhasivam } 855828dbc29SManivannan Sadhasivam 856828dbc29SManivannan Sadhasivam static int imx290_power_off(struct device *dev) 857828dbc29SManivannan Sadhasivam { 858b50ce25dSKrzysztof Kozlowski struct v4l2_subdev *sd = dev_get_drvdata(dev); 859828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 860828dbc29SManivannan Sadhasivam 861828dbc29SManivannan Sadhasivam clk_disable_unprepare(imx290->xclk); 8623909a92dSAndrey Konovalov gpiod_set_value_cansleep(imx290->rst_gpio, 1); 8632548df53SLaurent Pinchart regulator_bulk_disable(ARRAY_SIZE(imx290->supplies), imx290->supplies); 864828dbc29SManivannan Sadhasivam 865828dbc29SManivannan Sadhasivam return 0; 866828dbc29SManivannan Sadhasivam } 867828dbc29SManivannan Sadhasivam 868828dbc29SManivannan Sadhasivam static const struct dev_pm_ops imx290_pm_ops = { 8698d2d1bedSAndrey Konovalov SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) 870828dbc29SManivannan Sadhasivam }; 871828dbc29SManivannan Sadhasivam 872828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_video_ops imx290_video_ops = { 873828dbc29SManivannan Sadhasivam .s_stream = imx290_set_stream, 874828dbc29SManivannan Sadhasivam }; 875828dbc29SManivannan Sadhasivam 876828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_pad_ops imx290_pad_ops = { 877828dbc29SManivannan Sadhasivam .init_cfg = imx290_entity_init_cfg, 878828dbc29SManivannan Sadhasivam .enum_mbus_code = imx290_enum_mbus_code, 8793b867fb6SManivannan Sadhasivam .enum_frame_size = imx290_enum_frame_size, 880828dbc29SManivannan Sadhasivam .get_fmt = imx290_get_fmt, 881828dbc29SManivannan Sadhasivam .set_fmt = imx290_set_fmt, 882828dbc29SManivannan Sadhasivam }; 883828dbc29SManivannan Sadhasivam 884828dbc29SManivannan Sadhasivam static const struct v4l2_subdev_ops imx290_subdev_ops = { 885828dbc29SManivannan Sadhasivam .video = &imx290_video_ops, 886828dbc29SManivannan Sadhasivam .pad = &imx290_pad_ops, 887828dbc29SManivannan Sadhasivam }; 888828dbc29SManivannan Sadhasivam 889828dbc29SManivannan Sadhasivam static const struct media_entity_operations imx290_subdev_entity_ops = { 890828dbc29SManivannan Sadhasivam .link_validate = v4l2_subdev_link_validate, 891828dbc29SManivannan Sadhasivam }; 892828dbc29SManivannan Sadhasivam 89372c87b7aSLaurent Pinchart static int imx290_ctrl_init(struct imx290 *imx290) 89472c87b7aSLaurent Pinchart { 8954c9c93cfSLaurent Pinchart struct v4l2_fwnode_device_properties props; 8960c3b56c9SLaurent Pinchart unsigned int blank; 89772c87b7aSLaurent Pinchart int ret; 89872c87b7aSLaurent Pinchart 8994c9c93cfSLaurent Pinchart ret = v4l2_fwnode_device_parse(imx290->dev, &props); 9004c9c93cfSLaurent Pinchart if (ret < 0) 9014c9c93cfSLaurent Pinchart return ret; 9024c9c93cfSLaurent Pinchart 9034c9c93cfSLaurent Pinchart v4l2_ctrl_handler_init(&imx290->ctrls, 9); 90472c87b7aSLaurent Pinchart imx290->ctrls.lock = &imx290->lock; 90572c87b7aSLaurent Pinchart 90672c87b7aSLaurent Pinchart /* 90772c87b7aSLaurent Pinchart * The sensor has an analog gain and a digital gain, both controlled 90872c87b7aSLaurent Pinchart * through a single gain value, expressed in 0.3dB increments. Values 90972c87b7aSLaurent Pinchart * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values 91072c87b7aSLaurent Pinchart * up to 72.0dB (240) add further digital gain. Limit the range to 91172c87b7aSLaurent Pinchart * analog gain only, support for digital gain can be added separately 91272c87b7aSLaurent Pinchart * if needed. 91372c87b7aSLaurent Pinchart * 91472c87b7aSLaurent Pinchart * The IMX327 and IMX462 are largely compatible with the IMX290, but 91572c87b7aSLaurent Pinchart * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital 91672c87b7aSLaurent Pinchart * gain. When support for those sensors gets added to the driver, the 91772c87b7aSLaurent Pinchart * gain control should be adjusted accordingly. 91872c87b7aSLaurent Pinchart */ 91972c87b7aSLaurent Pinchart v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 92072c87b7aSLaurent Pinchart V4L2_CID_GAIN, 0, 100, 1, 0); 92172c87b7aSLaurent Pinchart 92272c87b7aSLaurent Pinchart v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 92372c87b7aSLaurent Pinchart V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, 92472c87b7aSLaurent Pinchart IMX290_VMAX_DEFAULT - 2); 92572c87b7aSLaurent Pinchart 92672c87b7aSLaurent Pinchart imx290->link_freq = 92772c87b7aSLaurent Pinchart v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, 92872c87b7aSLaurent Pinchart V4L2_CID_LINK_FREQ, 92972c87b7aSLaurent Pinchart imx290_link_freqs_num(imx290) - 1, 0, 93072c87b7aSLaurent Pinchart imx290_link_freqs_ptr(imx290)); 93172c87b7aSLaurent Pinchart if (imx290->link_freq) 93272c87b7aSLaurent Pinchart imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; 93372c87b7aSLaurent Pinchart 93472c87b7aSLaurent Pinchart imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 93572c87b7aSLaurent Pinchart V4L2_CID_PIXEL_RATE, 93672c87b7aSLaurent Pinchart 1, INT_MAX, 1, 93772c87b7aSLaurent Pinchart imx290_calc_pixel_rate(imx290)); 93872c87b7aSLaurent Pinchart 93972c87b7aSLaurent Pinchart v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, 94072c87b7aSLaurent Pinchart V4L2_CID_TEST_PATTERN, 94172c87b7aSLaurent Pinchart ARRAY_SIZE(imx290_test_pattern_menu) - 1, 94272c87b7aSLaurent Pinchart 0, 0, imx290_test_pattern_menu); 94372c87b7aSLaurent Pinchart 9440c3b56c9SLaurent Pinchart blank = imx290->current_mode->hmax - imx290->current_mode->width; 9450c3b56c9SLaurent Pinchart imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 9460c3b56c9SLaurent Pinchart V4L2_CID_HBLANK, blank, blank, 1, 9470c3b56c9SLaurent Pinchart blank); 9480c3b56c9SLaurent Pinchart if (imx290->hblank) 9490c3b56c9SLaurent Pinchart imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 9500c3b56c9SLaurent Pinchart 9510c3b56c9SLaurent Pinchart blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height; 9520c3b56c9SLaurent Pinchart imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, 9530c3b56c9SLaurent Pinchart V4L2_CID_VBLANK, blank, blank, 1, 9540c3b56c9SLaurent Pinchart blank); 9550c3b56c9SLaurent Pinchart if (imx290->vblank) 9560c3b56c9SLaurent Pinchart imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; 9570c3b56c9SLaurent Pinchart 9584c9c93cfSLaurent Pinchart v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, 9594c9c93cfSLaurent Pinchart &props); 9604c9c93cfSLaurent Pinchart 96172c87b7aSLaurent Pinchart imx290->sd.ctrl_handler = &imx290->ctrls; 96272c87b7aSLaurent Pinchart 96372c87b7aSLaurent Pinchart if (imx290->ctrls.error) { 96472c87b7aSLaurent Pinchart ret = imx290->ctrls.error; 96572c87b7aSLaurent Pinchart v4l2_ctrl_handler_free(&imx290->ctrls); 96672c87b7aSLaurent Pinchart return ret; 96772c87b7aSLaurent Pinchart } 96872c87b7aSLaurent Pinchart 96972c87b7aSLaurent Pinchart return 0; 97072c87b7aSLaurent Pinchart } 97172c87b7aSLaurent Pinchart 97298e0500eSManivannan Sadhasivam /* 97398e0500eSManivannan Sadhasivam * Returns 0 if all link frequencies used by the driver for the given number 97498e0500eSManivannan Sadhasivam * of MIPI data lanes are mentioned in the device tree, or the value of the 97598e0500eSManivannan Sadhasivam * first missing frequency otherwise. 97698e0500eSManivannan Sadhasivam */ 977a2706758SAndrey Konovalov static s64 imx290_check_link_freqs(const struct imx290 *imx290, 978a2706758SAndrey Konovalov const struct v4l2_fwnode_endpoint *ep) 97998e0500eSManivannan Sadhasivam { 98098e0500eSManivannan Sadhasivam int i, j; 98198e0500eSManivannan Sadhasivam const s64 *freqs = imx290_link_freqs_ptr(imx290); 98298e0500eSManivannan Sadhasivam int freqs_count = imx290_link_freqs_num(imx290); 98398e0500eSManivannan Sadhasivam 98498e0500eSManivannan Sadhasivam for (i = 0; i < freqs_count; i++) { 985a2706758SAndrey Konovalov for (j = 0; j < ep->nr_of_link_frequencies; j++) 986a2706758SAndrey Konovalov if (freqs[i] == ep->link_frequencies[j]) 98798e0500eSManivannan Sadhasivam break; 988a2706758SAndrey Konovalov if (j == ep->nr_of_link_frequencies) 98998e0500eSManivannan Sadhasivam return freqs[i]; 99098e0500eSManivannan Sadhasivam } 99198e0500eSManivannan Sadhasivam return 0; 99298e0500eSManivannan Sadhasivam } 99398e0500eSManivannan Sadhasivam 994828dbc29SManivannan Sadhasivam static int imx290_probe(struct i2c_client *client) 995828dbc29SManivannan Sadhasivam { 996828dbc29SManivannan Sadhasivam struct device *dev = &client->dev; 997828dbc29SManivannan Sadhasivam struct fwnode_handle *endpoint; 998a2706758SAndrey Konovalov /* Only CSI2 is supported for now: */ 999a2706758SAndrey Konovalov struct v4l2_fwnode_endpoint ep = { 1000a2706758SAndrey Konovalov .bus_type = V4L2_MBUS_CSI2_DPHY 1001a2706758SAndrey Konovalov }; 1002828dbc29SManivannan Sadhasivam struct imx290 *imx290; 1003828dbc29SManivannan Sadhasivam u32 xclk_freq; 100498e0500eSManivannan Sadhasivam s64 fq; 1005828dbc29SManivannan Sadhasivam int ret; 1006828dbc29SManivannan Sadhasivam 1007828dbc29SManivannan Sadhasivam imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); 1008828dbc29SManivannan Sadhasivam if (!imx290) 1009828dbc29SManivannan Sadhasivam return -ENOMEM; 1010828dbc29SManivannan Sadhasivam 1011828dbc29SManivannan Sadhasivam imx290->dev = dev; 1012828dbc29SManivannan Sadhasivam imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config); 1013828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->regmap)) { 1014828dbc29SManivannan Sadhasivam dev_err(dev, "Unable to initialize I2C\n"); 1015828dbc29SManivannan Sadhasivam return -ENODEV; 1016828dbc29SManivannan Sadhasivam } 1017828dbc29SManivannan Sadhasivam 1018828dbc29SManivannan Sadhasivam endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); 1019828dbc29SManivannan Sadhasivam if (!endpoint) { 1020828dbc29SManivannan Sadhasivam dev_err(dev, "Endpoint node not found\n"); 1021828dbc29SManivannan Sadhasivam return -EINVAL; 1022828dbc29SManivannan Sadhasivam } 1023828dbc29SManivannan Sadhasivam 1024a2706758SAndrey Konovalov ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); 1025828dbc29SManivannan Sadhasivam fwnode_handle_put(endpoint); 1026a2706758SAndrey Konovalov if (ret == -ENXIO) { 1027a2706758SAndrey Konovalov dev_err(dev, "Unsupported bus type, should be CSI2\n"); 1028a2706758SAndrey Konovalov goto free_err; 1029a2706758SAndrey Konovalov } else if (ret) { 1030828dbc29SManivannan Sadhasivam dev_err(dev, "Parsing endpoint node failed\n"); 1031828dbc29SManivannan Sadhasivam goto free_err; 1032828dbc29SManivannan Sadhasivam } 1033828dbc29SManivannan Sadhasivam 103497589ad6SManivannan Sadhasivam /* Get number of data lanes */ 1035a2706758SAndrey Konovalov imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes; 103697589ad6SManivannan Sadhasivam if (imx290->nlanes != 2 && imx290->nlanes != 4) { 103797589ad6SManivannan Sadhasivam dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); 103897589ad6SManivannan Sadhasivam ret = -EINVAL; 103997589ad6SManivannan Sadhasivam goto free_err; 104097589ad6SManivannan Sadhasivam } 104197589ad6SManivannan Sadhasivam 104297589ad6SManivannan Sadhasivam dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); 104397589ad6SManivannan Sadhasivam 1044a2706758SAndrey Konovalov if (!ep.nr_of_link_frequencies) { 1045828dbc29SManivannan Sadhasivam dev_err(dev, "link-frequency property not found in DT\n"); 1046828dbc29SManivannan Sadhasivam ret = -EINVAL; 1047828dbc29SManivannan Sadhasivam goto free_err; 1048828dbc29SManivannan Sadhasivam } 1049828dbc29SManivannan Sadhasivam 105098e0500eSManivannan Sadhasivam /* Check that link frequences for all the modes are in device tree */ 1051a2706758SAndrey Konovalov fq = imx290_check_link_freqs(imx290, &ep); 105298e0500eSManivannan Sadhasivam if (fq) { 105398e0500eSManivannan Sadhasivam dev_err(dev, "Link frequency of %lld is not supported\n", fq); 1054828dbc29SManivannan Sadhasivam ret = -EINVAL; 1055828dbc29SManivannan Sadhasivam goto free_err; 1056828dbc29SManivannan Sadhasivam } 1057828dbc29SManivannan Sadhasivam 1058828dbc29SManivannan Sadhasivam /* get system clock (xclk) */ 1059828dbc29SManivannan Sadhasivam imx290->xclk = devm_clk_get(dev, "xclk"); 1060828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->xclk)) { 1061828dbc29SManivannan Sadhasivam dev_err(dev, "Could not get xclk"); 1062828dbc29SManivannan Sadhasivam ret = PTR_ERR(imx290->xclk); 1063828dbc29SManivannan Sadhasivam goto free_err; 1064828dbc29SManivannan Sadhasivam } 1065828dbc29SManivannan Sadhasivam 1066828dbc29SManivannan Sadhasivam ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", 1067828dbc29SManivannan Sadhasivam &xclk_freq); 1068828dbc29SManivannan Sadhasivam if (ret) { 1069828dbc29SManivannan Sadhasivam dev_err(dev, "Could not get xclk frequency\n"); 1070828dbc29SManivannan Sadhasivam goto free_err; 1071828dbc29SManivannan Sadhasivam } 1072828dbc29SManivannan Sadhasivam 1073828dbc29SManivannan Sadhasivam /* external clock must be 37.125 MHz */ 1074828dbc29SManivannan Sadhasivam if (xclk_freq != 37125000) { 1075828dbc29SManivannan Sadhasivam dev_err(dev, "External clock frequency %u is not supported\n", 1076828dbc29SManivannan Sadhasivam xclk_freq); 1077828dbc29SManivannan Sadhasivam ret = -EINVAL; 1078828dbc29SManivannan Sadhasivam goto free_err; 1079828dbc29SManivannan Sadhasivam } 1080828dbc29SManivannan Sadhasivam 1081828dbc29SManivannan Sadhasivam ret = clk_set_rate(imx290->xclk, xclk_freq); 1082828dbc29SManivannan Sadhasivam if (ret) { 1083828dbc29SManivannan Sadhasivam dev_err(dev, "Could not set xclk frequency\n"); 1084828dbc29SManivannan Sadhasivam goto free_err; 1085828dbc29SManivannan Sadhasivam } 1086828dbc29SManivannan Sadhasivam 1087828dbc29SManivannan Sadhasivam ret = imx290_get_regulators(dev, imx290); 1088828dbc29SManivannan Sadhasivam if (ret < 0) { 1089828dbc29SManivannan Sadhasivam dev_err(dev, "Cannot get regulators\n"); 1090828dbc29SManivannan Sadhasivam goto free_err; 1091828dbc29SManivannan Sadhasivam } 1092828dbc29SManivannan Sadhasivam 10933909a92dSAndrey Konovalov imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", 10943909a92dSAndrey Konovalov GPIOD_OUT_HIGH); 1095828dbc29SManivannan Sadhasivam if (IS_ERR(imx290->rst_gpio)) { 1096828dbc29SManivannan Sadhasivam dev_err(dev, "Cannot get reset gpio\n"); 1097828dbc29SManivannan Sadhasivam ret = PTR_ERR(imx290->rst_gpio); 1098828dbc29SManivannan Sadhasivam goto free_err; 1099828dbc29SManivannan Sadhasivam } 1100828dbc29SManivannan Sadhasivam 1101828dbc29SManivannan Sadhasivam mutex_init(&imx290->lock); 1102828dbc29SManivannan Sadhasivam 110398e0500eSManivannan Sadhasivam /* 110498e0500eSManivannan Sadhasivam * Initialize the frame format. In particular, imx290->current_mode 110598e0500eSManivannan Sadhasivam * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call 110698e0500eSManivannan Sadhasivam * below relies on these fields. 110798e0500eSManivannan Sadhasivam */ 110898e0500eSManivannan Sadhasivam imx290_entity_init_cfg(&imx290->sd, NULL); 110998e0500eSManivannan Sadhasivam 111072c87b7aSLaurent Pinchart ret = imx290_ctrl_init(imx290); 111172c87b7aSLaurent Pinchart if (ret < 0) { 111272c87b7aSLaurent Pinchart dev_err(dev, "Control initialization error %d\n", ret); 111372c87b7aSLaurent Pinchart goto free_mutex; 1114828dbc29SManivannan Sadhasivam } 1115828dbc29SManivannan Sadhasivam 1116828dbc29SManivannan Sadhasivam v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); 1117828dbc29SManivannan Sadhasivam imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1118828dbc29SManivannan Sadhasivam imx290->sd.dev = &client->dev; 1119828dbc29SManivannan Sadhasivam imx290->sd.entity.ops = &imx290_subdev_entity_ops; 1120828dbc29SManivannan Sadhasivam imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 1121828dbc29SManivannan Sadhasivam 1122828dbc29SManivannan Sadhasivam imx290->pad.flags = MEDIA_PAD_FL_SOURCE; 1123828dbc29SManivannan Sadhasivam ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad); 1124828dbc29SManivannan Sadhasivam if (ret < 0) { 1125828dbc29SManivannan Sadhasivam dev_err(dev, "Could not register media entity\n"); 1126828dbc29SManivannan Sadhasivam goto free_ctrl; 1127828dbc29SManivannan Sadhasivam } 1128828dbc29SManivannan Sadhasivam 1129828dbc29SManivannan Sadhasivam ret = v4l2_async_register_subdev(&imx290->sd); 1130828dbc29SManivannan Sadhasivam if (ret < 0) { 1131828dbc29SManivannan Sadhasivam dev_err(dev, "Could not register v4l2 device\n"); 1132828dbc29SManivannan Sadhasivam goto free_entity; 1133828dbc29SManivannan Sadhasivam } 1134828dbc29SManivannan Sadhasivam 1135828dbc29SManivannan Sadhasivam /* Power on the device to match runtime PM state below */ 1136828dbc29SManivannan Sadhasivam ret = imx290_power_on(dev); 1137828dbc29SManivannan Sadhasivam if (ret < 0) { 1138828dbc29SManivannan Sadhasivam dev_err(dev, "Could not power on the device\n"); 1139828dbc29SManivannan Sadhasivam goto free_entity; 1140828dbc29SManivannan Sadhasivam } 1141828dbc29SManivannan Sadhasivam 1142828dbc29SManivannan Sadhasivam pm_runtime_set_active(dev); 1143828dbc29SManivannan Sadhasivam pm_runtime_enable(dev); 1144828dbc29SManivannan Sadhasivam pm_runtime_idle(dev); 1145828dbc29SManivannan Sadhasivam 1146a2706758SAndrey Konovalov v4l2_fwnode_endpoint_free(&ep); 1147828dbc29SManivannan Sadhasivam 1148828dbc29SManivannan Sadhasivam return 0; 1149828dbc29SManivannan Sadhasivam 1150828dbc29SManivannan Sadhasivam free_entity: 1151828dbc29SManivannan Sadhasivam media_entity_cleanup(&imx290->sd.entity); 1152828dbc29SManivannan Sadhasivam free_ctrl: 1153828dbc29SManivannan Sadhasivam v4l2_ctrl_handler_free(&imx290->ctrls); 115472c87b7aSLaurent Pinchart free_mutex: 1155828dbc29SManivannan Sadhasivam mutex_destroy(&imx290->lock); 1156828dbc29SManivannan Sadhasivam free_err: 1157a2706758SAndrey Konovalov v4l2_fwnode_endpoint_free(&ep); 1158828dbc29SManivannan Sadhasivam 1159828dbc29SManivannan Sadhasivam return ret; 1160828dbc29SManivannan Sadhasivam } 1161828dbc29SManivannan Sadhasivam 1162ed5c2f5fSUwe Kleine-König static void imx290_remove(struct i2c_client *client) 1163828dbc29SManivannan Sadhasivam { 1164828dbc29SManivannan Sadhasivam struct v4l2_subdev *sd = i2c_get_clientdata(client); 1165828dbc29SManivannan Sadhasivam struct imx290 *imx290 = to_imx290(sd); 1166828dbc29SManivannan Sadhasivam 1167828dbc29SManivannan Sadhasivam v4l2_async_unregister_subdev(sd); 1168828dbc29SManivannan Sadhasivam media_entity_cleanup(&sd->entity); 1169828dbc29SManivannan Sadhasivam v4l2_ctrl_handler_free(sd->ctrl_handler); 1170828dbc29SManivannan Sadhasivam 1171828dbc29SManivannan Sadhasivam mutex_destroy(&imx290->lock); 1172828dbc29SManivannan Sadhasivam 1173828dbc29SManivannan Sadhasivam pm_runtime_disable(imx290->dev); 1174828dbc29SManivannan Sadhasivam if (!pm_runtime_status_suspended(imx290->dev)) 1175828dbc29SManivannan Sadhasivam imx290_power_off(imx290->dev); 1176828dbc29SManivannan Sadhasivam pm_runtime_set_suspended(imx290->dev); 1177828dbc29SManivannan Sadhasivam } 1178828dbc29SManivannan Sadhasivam 1179828dbc29SManivannan Sadhasivam static const struct of_device_id imx290_of_match[] = { 1180828dbc29SManivannan Sadhasivam { .compatible = "sony,imx290" }, 1181828dbc29SManivannan Sadhasivam { /* sentinel */ } 1182828dbc29SManivannan Sadhasivam }; 1183828dbc29SManivannan Sadhasivam MODULE_DEVICE_TABLE(of, imx290_of_match); 1184828dbc29SManivannan Sadhasivam 1185828dbc29SManivannan Sadhasivam static struct i2c_driver imx290_i2c_driver = { 1186828dbc29SManivannan Sadhasivam .probe_new = imx290_probe, 1187828dbc29SManivannan Sadhasivam .remove = imx290_remove, 1188828dbc29SManivannan Sadhasivam .driver = { 1189828dbc29SManivannan Sadhasivam .name = "imx290", 1190828dbc29SManivannan Sadhasivam .pm = &imx290_pm_ops, 1191828dbc29SManivannan Sadhasivam .of_match_table = of_match_ptr(imx290_of_match), 1192828dbc29SManivannan Sadhasivam }, 1193828dbc29SManivannan Sadhasivam }; 1194828dbc29SManivannan Sadhasivam 1195828dbc29SManivannan Sadhasivam module_i2c_driver(imx290_i2c_driver); 1196828dbc29SManivannan Sadhasivam 1197828dbc29SManivannan Sadhasivam MODULE_DESCRIPTION("Sony IMX290 CMOS Image Sensor Driver"); 1198828dbc29SManivannan Sadhasivam MODULE_AUTHOR("FRAMOS GmbH"); 1199828dbc29SManivannan Sadhasivam MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 1200828dbc29SManivannan Sadhasivam MODULE_LICENSE("GPL v2"); 1201