diff options
Diffstat (limited to 'drivers/media/i2c/imx290.c')
-rw-r--r-- | drivers/media/i2c/imx290.c | 602 |
1 files changed, 480 insertions, 122 deletions
diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 49d6c8bdec41..5ea25b7acc55 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -13,12 +13,17 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> + +#include <asm/unaligned.h> + #include <media/media-entity.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> @@ -44,7 +49,9 @@ #define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a) #define IMX290_GAIN IMX290_REG_8BIT(0x3014) #define IMX290_VMAX IMX290_REG_24BIT(0x3018) +#define IMX290_VMAX_MAX 0x3ffff #define IMX290_HMAX IMX290_REG_16BIT(0x301c) +#define IMX290_HMAX_MAX 0xffff #define IMX290_SHS1 IMX290_REG_24BIT(0x3020) #define IMX290_WINWV_OB IMX290_REG_8BIT(0x303a) #define IMX290_WINPV IMX290_REG_16BIT(0x303c) @@ -98,13 +105,16 @@ #define IMX290_TCLKPREPARE IMX290_REG_16BIT(0x3452) #define IMX290_TLPX IMX290_REG_16BIT(0x3454) #define IMX290_X_OUT_SIZE IMX290_REG_16BIT(0x3472) +#define IMX290_INCKSEL7 IMX290_REG_8BIT(0x3480) #define IMX290_PGCTRL_REGEN BIT(0) #define IMX290_PGCTRL_THRU BIT(1) #define IMX290_PGCTRL_MODE(n) ((n) << 4) -#define IMX290_VMAX_DEFAULT 1125 +/* Number of lines by which exposure must be less than VMAX */ +#define IMX290_EXPOSURE_OFFSET 2 +#define IMX290_PIXEL_RATE 148500000 /* * The IMX290 pixel array is organized as follows: @@ -157,26 +167,81 @@ #define IMX290_NUM_SUPPLIES 3 +enum imx290_colour_variant { + IMX290_VARIANT_COLOUR, + IMX290_VARIANT_MONO, + IMX290_VARIANT_MAX +}; + +enum imx290_model { + IMX290_MODEL_IMX290LQR, + IMX290_MODEL_IMX290LLR, + IMX290_MODEL_IMX327LQR, +}; + +struct imx290_model_info { + enum imx290_colour_variant colour_variant; + const struct imx290_regval *init_regs; + size_t init_regs_num; + const char *name; +}; + +enum imx290_clk_freq { + IMX290_CLK_37_125, + IMX290_CLK_74_25, + IMX290_NUM_CLK +}; + struct imx290_regval { u32 reg; u32 val; }; +/* + * Clock configuration for registers INCKSEL1 to INCKSEL6. + */ +struct imx290_clk_cfg { + u8 incksel1; + u8 incksel2; + u8 incksel3; + u8 incksel4; + u8 incksel5; + u8 incksel6; +}; + struct imx290_mode { u32 width; u32 height; - u32 hmax; + u32 hmax_min; + u32 vmax_min; u8 link_freq_index; + u8 ctrl_07; const struct imx290_regval *data; u32 data_size; + + const struct imx290_clk_cfg *clk_cfg; +}; + +struct imx290_csi_cfg { + u16 repetition; + u16 tclkpost; + u16 thszero; + u16 thsprepare; + u16 tclktrail; + u16 thstrail; + u16 tclkzero; + u16 tclkprepare; + u16 tlpx; }; struct imx290 { struct device *dev; struct clk *xclk; struct regmap *regmap; + enum imx290_clk_freq xclk_idx; u8 nlanes; + const struct imx290_model_info *model; struct v4l2_subdev sd; struct media_pad pad; @@ -188,9 +253,13 @@ struct imx290 { struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; }; static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) @@ -203,9 +272,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) */ static const struct imx290_regval imx290_global_init_settings[] = { - { IMX290_CTRL_07, IMX290_WINMODE_1080P }, - { IMX290_VMAX, IMX290_VMAX_DEFAULT }, - { IMX290_EXTCK_FREQ, 0x2520 }, { IMX290_WINWV_OB, 12 }, { IMX290_WINPH, 0 }, { IMX290_WINPV, 0 }, @@ -213,10 +279,14 @@ static const struct imx290_regval imx290_global_init_settings[] = { { IMX290_WINWV, 1097 }, { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, - { IMX290_REG_8BIT(0x300f), 0x00 }, - { IMX290_REG_8BIT(0x3010), 0x21 }, + { IMX290_REG_8BIT(0x3011), 0x02 }, { IMX290_REG_8BIT(0x3012), 0x64 }, { IMX290_REG_8BIT(0x3013), 0x00 }, +}; + +static const struct imx290_regval imx290_global_init_settings_290[] = { + { IMX290_REG_8BIT(0x300f), 0x00 }, + { IMX290_REG_8BIT(0x3010), 0x21 }, { IMX290_REG_8BIT(0x3016), 0x09 }, { IMX290_REG_8BIT(0x3070), 0x02 }, { IMX290_REG_8BIT(0x3071), 0x11 }, @@ -255,57 +325,40 @@ static const struct imx290_regval imx290_global_init_settings[] = { { IMX290_REG_8BIT(0x33b0), 0x50 }, { IMX290_REG_8BIT(0x33b2), 0x1a }, { IMX290_REG_8BIT(0x33b3), 0x04 }, - { IMX290_REG_8BIT(0x3480), 0x49 }, +}; + +#define IMX290_NUM_CLK_REGS 2 +static const struct imx290_regval xclk_regs[][IMX290_NUM_CLK_REGS] = { + [IMX290_CLK_37_125] = { + { IMX290_EXTCK_FREQ, (37125 * 256) / 1000 }, + { IMX290_INCKSEL7, 0x49 }, + }, + [IMX290_CLK_74_25] = { + { IMX290_EXTCK_FREQ, (74250 * 256) / 1000 }, + { IMX290_INCKSEL7, 0x92 }, + }, +}; + +static const struct imx290_regval imx290_global_init_settings_327[] = { + { IMX290_REG_8BIT(0x309e), 0x4A }, + { IMX290_REG_8BIT(0x309f), 0x4A }, + { IMX290_REG_8BIT(0x313b), 0x61 }, }; static const struct imx290_regval imx290_1080p_settings[] = { /* mode settings */ - { IMX290_CTRL_07, IMX290_WINMODE_1080P }, { IMX290_WINWV_OB, 12 }, { IMX290_OPB_SIZE_V, 10 }, { IMX290_X_OUT_SIZE, 1920 }, { IMX290_Y_OUT_SIZE, 1080 }, - { IMX290_INCKSEL1, 0x18 }, - { IMX290_INCKSEL2, 0x03 }, - { IMX290_INCKSEL3, 0x20 }, - { IMX290_INCKSEL4, 0x01 }, - { IMX290_INCKSEL5, 0x1a }, - { IMX290_INCKSEL6, 0x1a }, - /* data rate settings */ - { IMX290_REPETITION, 0x10 }, - { IMX290_TCLKPOST, 87 }, - { IMX290_THSZERO, 55 }, - { IMX290_THSPREPARE, 31 }, - { IMX290_TCLKTRAIL, 31 }, - { IMX290_THSTRAIL, 31 }, - { IMX290_TCLKZERO, 119 }, - { IMX290_TCLKPREPARE, 31 }, - { IMX290_TLPX, 23 }, }; static const struct imx290_regval imx290_720p_settings[] = { /* mode settings */ - { IMX290_CTRL_07, IMX290_WINMODE_720P }, { IMX290_WINWV_OB, 6 }, { IMX290_OPB_SIZE_V, 4 }, { IMX290_X_OUT_SIZE, 1280 }, { IMX290_Y_OUT_SIZE, 720 }, - { IMX290_INCKSEL1, 0x20 }, - { IMX290_INCKSEL2, 0x00 }, - { IMX290_INCKSEL3, 0x20 }, - { IMX290_INCKSEL4, 0x01 }, - { IMX290_INCKSEL5, 0x1a }, - { IMX290_INCKSEL6, 0x1a }, - /* data rate settings */ - { IMX290_REPETITION, 0x10 }, - { IMX290_TCLKPOST, 79 }, - { IMX290_THSZERO, 47 }, - { IMX290_THSPREPARE, 23 }, - { IMX290_TCLKTRAIL, 23 }, - { IMX290_THSTRAIL, 23 }, - { IMX290_TCLKZERO, 87 }, - { IMX290_TCLKPREPARE, 23 }, - { IMX290_TLPX, 23 }, }; static const struct imx290_regval imx290_10bit_settings[] = { @@ -326,6 +379,58 @@ static const struct imx290_regval imx290_12bit_settings[] = { { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 }, }; +static const struct imx290_csi_cfg imx290_csi_222_75mhz = { + /* 222.75MHz or 445.5Mbit/s per lane */ + .repetition = 0x10, + .tclkpost = 87, + .thszero = 55, + .thsprepare = 31, + .tclktrail = 31, + .thstrail = 31, + .tclkzero = 119, + .tclkprepare = 31, + .tlpx = 23, +}; + +static const struct imx290_csi_cfg imx290_csi_445_5mhz = { + /* 445.5MHz or 891Mbit/s per lane */ + .repetition = 0x00, + .tclkpost = 119, + .thszero = 103, + .thsprepare = 71, + .tclktrail = 55, + .thstrail = 63, + .tclkzero = 255, + .tclkprepare = 63, + .tlpx = 55, +}; + +static const struct imx290_csi_cfg imx290_csi_148_5mhz = { + /* 148.5MHz or 297Mbit/s per lane */ + .repetition = 0x10, + .tclkpost = 79, + .thszero = 47, + .thsprepare = 23, + .tclktrail = 23, + .thstrail = 23, + .tclkzero = 87, + .tclkprepare = 23, + .tlpx = 23, +}; + +static const struct imx290_csi_cfg imx290_csi_297mhz = { + /* 297MHz or 594Mbit/s per lane */ + .repetition = 0x00, + .tclkpost = 103, + .thszero = 87, + .thsprepare = 47, + .tclktrail = 39, + .thstrail = 47, + .tclkzero = 191, + .tclkprepare = 47, + .tlpx = 39, +}; + /* supported link frequencies */ #define FREQ_INDEX_1080P 0 #define FREQ_INDEX_720P 1 @@ -333,6 +438,7 @@ static const s64 imx290_link_freq_2lanes[] = { [FREQ_INDEX_1080P] = 445500000, [FREQ_INDEX_720P] = 297000000, }; + static const s64 imx290_link_freq_4lanes[] = { [FREQ_INDEX_1080P] = 222750000, [FREQ_INDEX_720P] = 148500000, @@ -358,23 +464,71 @@ static inline int imx290_link_freqs_num(const struct imx290 *imx290) return ARRAY_SIZE(imx290_link_freq_4lanes); } +static const struct imx290_clk_cfg imx290_1080p_clock_config[] = { + [IMX290_CLK_37_125] = { + /* 37.125MHz clock config */ + .incksel1 = 0x18, + .incksel2 = 0x03, + .incksel3 = 0x20, + .incksel4 = 0x01, + .incksel5 = 0x1a, + .incksel6 = 0x1a, + }, + [IMX290_CLK_74_25] = { + /* 74.25MHz clock config */ + .incksel1 = 0x0c, + .incksel2 = 0x03, + .incksel3 = 0x10, + .incksel4 = 0x01, + .incksel5 = 0x1b, + .incksel6 = 0x1b, + }, +}; + +static const struct imx290_clk_cfg imx290_720p_clock_config[] = { + [IMX290_CLK_37_125] = { + /* 37.125MHz clock config */ + .incksel1 = 0x20, + .incksel2 = 0x00, + .incksel3 = 0x20, + .incksel4 = 0x01, + .incksel5 = 0x1a, + .incksel6 = 0x1a, + }, + [IMX290_CLK_74_25] = { + /* 74.25MHz clock config */ + .incksel1 = 0x10, + .incksel2 = 0x00, + .incksel3 = 0x10, + .incksel4 = 0x01, + .incksel5 = 0x1b, + .incksel6 = 0x1b, + }, +}; + /* Mode configs */ static const struct imx290_mode imx290_modes_2lanes[] = { { .width = 1920, .height = 1080, - .hmax = 4400, + .hmax_min = 2200, + .vmax_min = 1125, .link_freq_index = FREQ_INDEX_1080P, + .ctrl_07 = IMX290_WINMODE_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), + .clk_cfg = imx290_1080p_clock_config, }, { .width = 1280, .height = 720, - .hmax = 6600, + .hmax_min = 3300, + .vmax_min = 750, .link_freq_index = FREQ_INDEX_720P, + .ctrl_07 = IMX290_WINMODE_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), + .clk_cfg = imx290_720p_clock_config, }, }; @@ -382,18 +536,24 @@ static const struct imx290_mode imx290_modes_4lanes[] = { { .width = 1920, .height = 1080, - .hmax = 2200, + .hmax_min = 2200, + .vmax_min = 1125, .link_freq_index = FREQ_INDEX_1080P, + .ctrl_07 = IMX290_WINMODE_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), + .clk_cfg = imx290_1080p_clock_config, }, { .width = 1280, .height = 720, - .hmax = 3300, + .hmax_min = 3300, + .vmax_min = 750, .link_freq_index = FREQ_INDEX_720P, + .ctrl_07 = IMX290_WINMODE_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), + .clk_cfg = imx290_720p_clock_config, }, }; @@ -414,7 +574,7 @@ static inline int imx290_modes_num(const struct imx290 *imx290) } struct imx290_format_info { - u32 code; + u32 code[IMX290_VARIANT_MAX]; u8 bpp; const struct imx290_regval *regs; unsigned int num_regs; @@ -422,26 +582,33 @@ struct imx290_format_info { static const struct imx290_format_info imx290_formats[] = { { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .code = { + [IMX290_VARIANT_COLOUR] = MEDIA_BUS_FMT_SRGGB10_1X10, + [IMX290_VARIANT_MONO] = MEDIA_BUS_FMT_Y10_1X10 + }, .bpp = 10, .regs = imx290_10bit_settings, .num_regs = ARRAY_SIZE(imx290_10bit_settings), }, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .code = { + [IMX290_VARIANT_COLOUR] = MEDIA_BUS_FMT_SRGGB12_1X12, + [IMX290_VARIANT_MONO] = MEDIA_BUS_FMT_Y12_1X12 + }, .bpp = 12, .regs = imx290_12bit_settings, .num_regs = ARRAY_SIZE(imx290_12bit_settings), } }; -static const struct imx290_format_info *imx290_format_info(u32 code) +static const struct imx290_format_info * +imx290_format_info(const struct imx290 *imx290, u32 code) { unsigned int i; for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) { const struct imx290_format_info *info = &imx290_formats[i]; - if (info->code == code) + if (info->code[imx290->model->colour_variant] == code) return info; } @@ -461,28 +628,30 @@ static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *val data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); if (ret < 0) { dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, + ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, addr & IMX290_REG_ADDR_MASK, ret); return ret; } - *value = (data[2] << 16) | (data[1] << 8) | data[0]; + *value = get_unaligned_le24(data); return 0; } static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) { - u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 }; + u8 data[3]; int ret; if (err && *err) return *err; + put_unaligned_le24(value, data); + ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); if (ret < 0) { dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, + ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, addr & IMX290_REG_ADDR_MASK, ret); if (err) *err = ret; @@ -510,24 +679,33 @@ static int imx290_set_register_array(struct imx290 *imx290, return 0; } +static int imx290_set_clock(struct imx290 *imx290) +{ + const struct imx290_mode *mode = imx290->current_mode; + enum imx290_clk_freq clk_idx = imx290->xclk_idx; + const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx]; + int ret; + + ret = imx290_set_register_array(imx290, xclk_regs[clk_idx], + IMX290_NUM_CLK_REGS); + + imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret); + imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret); + imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret); + imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret); + imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret); + imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret); + + return ret; +} + static int imx290_set_data_lanes(struct imx290 *imx290) { int ret = 0; - u32 frsel; - - switch (imx290->nlanes) { - case 2: - default: - frsel = 0x02; - break; - case 4: - frsel = 0x01; - break; - } imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret); imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret); - imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); + imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret); return ret; } @@ -536,19 +714,55 @@ static int imx290_set_black_level(struct imx290 *imx290, const struct v4l2_mbus_framefmt *format, unsigned int black_level, int *err) { - unsigned int bpp = imx290_format_info(format->code)->bpp; + unsigned int bpp = imx290_format_info(imx290, format->code)->bpp; return imx290_write(imx290, IMX290_BLKLEVEL, black_level >> (16 - bpp), err); } +static int imx290_set_csi_config(struct imx290 *imx290) +{ + const s64 *link_freqs = imx290_link_freqs_ptr(imx290); + const struct imx290_csi_cfg *csi_cfg; + int ret = 0; + + switch (link_freqs[imx290->current_mode->link_freq_index]) { + case 445500000: + csi_cfg = &imx290_csi_445_5mhz; + break; + case 297000000: + csi_cfg = &imx290_csi_297mhz; + break; + case 222750000: + csi_cfg = &imx290_csi_222_75mhz; + break; + case 148500000: + csi_cfg = &imx290_csi_148_5mhz; + break; + default: + return -EINVAL; + } + + imx290_write(imx290, IMX290_REPETITION, csi_cfg->repetition, &ret); + imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret); + imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret); + imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret); + imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret); + imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret); + imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret); + imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, &ret); + imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret); + + return ret; +} + static int imx290_setup_format(struct imx290 *imx290, const struct v4l2_mbus_framefmt *format) { const struct imx290_format_info *info; int ret; - info = imx290_format_info(format->code); + info = imx290_format_info(imx290, format->code); ret = imx290_set_register_array(imx290, info->regs, info->num_regs); if (ret < 0) { @@ -563,6 +777,16 @@ static int imx290_setup_format(struct imx290 *imx290, /* ---------------------------------------------------------------------------- * Controls */ +static void imx290_exposure_update(struct imx290 *imx290, + const struct imx290_mode *mode) +{ + unsigned int exposure_max; + + exposure_max = imx290->vblank->val + mode->height - + IMX290_EXPOSURE_OFFSET; + __v4l2_ctrl_modify_range(imx290->exposure, 1, exposure_max, 1, + exposure_max); +} static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) { @@ -570,7 +794,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) struct imx290, ctrls); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; - int ret = 0; + int ret = 0, vmax; /* * Return immediately for controls that don't need to be applied to the @@ -579,6 +803,11 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) return 0; + if (ctrl->id == V4L2_CID_VBLANK) { + /* Changing vblank changes the allowed range for exposure. */ + imx290_exposure_update(imx290, imx290->current_mode); + } + /* V4L2 controls values will be applied only when power is already up */ if (!pm_runtime_get_if_in_use(imx290->dev)) return 0; @@ -591,9 +820,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); break; + case V4L2_CID_VBLANK: + ret = imx290_write(imx290, IMX290_VMAX, + ctrl->val + imx290->current_mode->height, + NULL); + /* + * Due to the way that exposure is programmed in this sensor in + * relation to VMAX, we have to reprogramme it whenever VMAX is + * changed. + * Update ctrl so that the V4L2_CID_EXPOSURE case can refer to + * it. + */ + ctrl = imx290->exposure; + fallthrough; case V4L2_CID_EXPOSURE: + vmax = imx290->vblank->val + imx290->current_mode->height; ret = imx290_write(imx290, IMX290_SHS1, - IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL); + vmax - ctrl->val - 1, NULL); break; case V4L2_CID_TEST_PATTERN: @@ -612,6 +855,26 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) } break; + case V4L2_CID_HBLANK: + ret = imx290_write(imx290, IMX290_HMAX, + ctrl->val + imx290->current_mode->width, + NULL); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + { + u32 reg; + + reg = imx290->current_mode->ctrl_07; + if (imx290->hflip->val) + reg |= IMX290_HREVERSE; + if (imx290->vflip->val) + reg |= IMX290_VREVERSE; + ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL); + break; + } + default: ret = -EINVAL; break; @@ -642,20 +905,17 @@ static void imx290_ctrl_update(struct imx290 *imx290, const struct v4l2_mbus_framefmt *format, const struct imx290_mode *mode) { - unsigned int hblank = mode->hmax - mode->width; - unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; - s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index]; - u64 pixel_rate; - - /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = link_freq * 2 * imx290->nlanes; - do_div(pixel_rate, imx290_format_info(format->code)->bpp); + unsigned int hblank_min = mode->hmax_min - mode->width; + unsigned int hblank_max = IMX290_HMAX_MAX - mode->width; + unsigned int vblank_min = mode->vmax_min - mode->height; + unsigned int vblank_max = IMX290_VMAX_MAX - mode->height; __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate); - __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank); - __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank); + __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1, + hblank_min); + __v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1, + vblank_min); } static int imx290_ctrl_init(struct imx290 *imx290) @@ -667,7 +927,7 @@ static int imx290_ctrl_init(struct imx290 *imx290) if (ret < 0) return ret; - v4l2_ctrl_handler_init(&imx290->ctrls, 9); + v4l2_ctrl_handler_init(&imx290->ctrls, 11); /* * The sensor has an analog gain and a digital gain, both controlled @@ -685,9 +945,13 @@ static int imx290_ctrl_init(struct imx290 *imx290) v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); - v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, - IMX290_VMAX_DEFAULT - 2); + /* + * Correct range will be determined through imx290_ctrl_update setting + * V4L2_CID_VBLANK. + */ + imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 65535, 1, + 65535); /* * Set the link frequency, pixel rate, horizontal blanking and vertical @@ -702,24 +966,29 @@ static int imx290_ctrl_init(struct imx290 *imx290) if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, 1); + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE, + IMX290_PIXEL_RATE, IMX290_PIXEL_RATE, 1, + IMX290_PIXEL_RATE); v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(imx290_test_pattern_menu) - 1, 0, 0, imx290_test_pattern_menu); + /* + * Actual range will be set from imx290_ctrl_update later in the probe. + */ imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_HBLANK, 1, 1, 1, 1); - if (imx290->hblank) - imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_VBLANK, 1, 1, 1, 1); - if (imx290->vblank) - imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &imx290->hflip); v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, &props); @@ -748,17 +1017,37 @@ static int imx290_start_streaming(struct imx290 *imx290, /* Set init register settings */ ret = imx290_set_register_array(imx290, imx290_global_init_settings, - ARRAY_SIZE( - imx290_global_init_settings)); + ARRAY_SIZE(imx290_global_init_settings)); if (ret < 0) { dev_err(imx290->dev, "Could not set init registers\n"); return ret; } + /* Set mdel specific init register settings */ + ret = imx290_set_register_array(imx290, imx290->model->init_regs, + imx290->model->init_regs_num); + if (ret < 0) { + dev_err(imx290->dev, "Could not set model specific init registers\n"); + return ret; + } + + /* Set clock parameters based on mode and xclk */ + ret = imx290_set_clock(imx290); + if (ret < 0) { + dev_err(imx290->dev, "Could not set clocks - %d\n", ret); + return ret; + } + /* Set data lane count */ ret = imx290_set_data_lanes(imx290); if (ret < 0) { - dev_err(imx290->dev, "Could not set data lanes\n"); + dev_err(imx290->dev, "Could not set data lanes - %d\n", ret); + return ret; + } + + ret = imx290_set_csi_config(imx290); + if (ret < 0) { + dev_err(imx290->dev, "Could not set csi cfg - %d\n", ret); return ret; } @@ -766,7 +1055,7 @@ static int imx290_start_streaming(struct imx290 *imx290, format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); ret = imx290_setup_format(imx290, format); if (ret < 0) { - dev_err(imx290->dev, "Could not set frame format\n"); + dev_err(imx290->dev, "Could not set frame format - %d\n", ret); return ret; } @@ -774,19 +1063,14 @@ static int imx290_start_streaming(struct imx290 *imx290, ret = imx290_set_register_array(imx290, imx290->current_mode->data, imx290->current_mode->data_size); if (ret < 0) { - dev_err(imx290->dev, "Could not set current mode\n"); + dev_err(imx290->dev, "Could not set current mode - %d\n", ret); return ret; } - ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax, - NULL); - if (ret) - return ret; - /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); if (ret) { - dev_err(imx290->dev, "Could not sync v4l2 controls\n"); + dev_err(imx290->dev, "Could not sync v4l2 controls - %d\n", ret); return ret; } @@ -835,6 +1119,13 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put_autosuspend(imx290->dev); } + /* + * vflip and hflip should not be changed during streaming as the sensor + * will produce an invalid frame. + */ + __v4l2_ctrl_grab(imx290->vflip, enable); + __v4l2_ctrl_grab(imx290->hflip, enable); + unlock: v4l2_subdev_unlock_state(state); return ret; @@ -844,10 +1135,12 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { + const struct imx290 *imx290 = to_imx290(sd); + if (code->index >= ARRAY_SIZE(imx290_formats)) return -EINVAL; - code->code = imx290_formats[code->index].code; + code->code = imx290_formats[code->index].code[imx290->model->colour_variant]; return 0; } @@ -859,7 +1152,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd, const struct imx290 *imx290 = to_imx290(sd); const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); - if (!imx290_format_info(fse->code)) + if (!imx290_format_info(imx290, fse->code)) return -EINVAL; if (fse->index >= imx290_modes_num(imx290)) @@ -888,10 +1181,14 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, fmt->format.width = mode->width; fmt->format.height = mode->height; - if (!imx290_format_info(fmt->format.code)) - fmt->format.code = imx290_formats[0].code; + if (!imx290_format_info(imx290, fmt->format.code)) + fmt->format.code = imx290_formats[0].code[imx290->model->colour_variant]; fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; format = v4l2_subdev_get_pad_format(sd, sd_state, 0); @@ -899,6 +1196,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, imx290->current_mode = mode; imx290_ctrl_update(imx290, &fmt->format, mode); + imx290_exposure_update(imx290, mode); } *format = fmt->format; @@ -910,16 +1208,23 @@ static int imx290_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { + struct imx290 *imx290 = to_imx290(sd); struct v4l2_mbus_framefmt *format; switch (sel->target) { case V4L2_SEL_TGT_CROP: { format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + /* + * The sensor moves the readout by 1 pixel based on flips to + * keep the Bayer order the same. + */ sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP - + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2; + + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2 + + imx290->vflip->val; sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT - + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2; + + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2 + + imx290->hflip->val; sel->r.width = format->width; sel->r.height = format->height; @@ -964,6 +1269,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, return 0; } +static const struct v4l2_subdev_core_ops imx290_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + static const struct v4l2_subdev_video_ops imx290_video_ops = { .s_stream = imx290_set_stream, }; @@ -978,6 +1288,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops = { }; static const struct v4l2_subdev_ops imx290_subdev_ops = { + .core = &imx290_core_ops, .video = &imx290_video_ops, .pad = &imx290_pad_ops, }; @@ -996,7 +1307,8 @@ static int imx290_subdev_init(struct imx290 *imx290) imx290->current_mode = &imx290_modes_ptr(imx290)[0]; v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); - imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; imx290->sd.dev = imx290->dev; imx290->sd.entity.ops = &imx290_subdev_entity_ops; imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1098,7 +1410,7 @@ static int imx290_runtime_suspend(struct device *dev) } static const struct dev_pm_ops imx290_pm_ops = { - SET_RUNTIME_PM_OPS(imx290_runtime_suspend, imx290_runtime_resume, NULL) + RUNTIME_PM_OPS(imx290_runtime_suspend, imx290_runtime_resume, NULL) }; /* ---------------------------------------------------------------------------- @@ -1132,15 +1444,22 @@ static int imx290_init_clk(struct imx290 *imx290) u32 xclk_freq; int ret; - ret = fwnode_property_read_u32(dev_fwnode(imx290->dev), - "clock-frequency", &xclk_freq); + ret = device_property_read_u32(imx290->dev, "clock-frequency", + &xclk_freq); if (ret) { dev_err(imx290->dev, "Could not get xclk frequency\n"); return ret; } - /* external clock must be 37.125 MHz */ - if (xclk_freq != 37125000) { + /* external clock must be 37.125 MHz or 74.25MHz */ + switch (xclk_freq) { + case 37125000: + imx290->xclk_idx = IMX290_CLK_37_125; + break; + case 74250000: + imx290->xclk_idx = IMX290_CLK_74_25; + break; + default: dev_err(imx290->dev, "External clock frequency %u is not supported\n", xclk_freq); return -EINVAL; @@ -1177,6 +1496,27 @@ static s64 imx290_check_link_freqs(const struct imx290 *imx290, return 0; } +static const struct imx290_model_info imx290_models[] = { + [IMX290_MODEL_IMX290LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .name = "imx290", + }, + [IMX290_MODEL_IMX290LLR] = { + .colour_variant = IMX290_VARIANT_MONO, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .name = "imx290", + }, + [IMX290_MODEL_IMX327LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_327, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_327), + .name = "imx327", + }, +}; + static int imx290_parse_dt(struct imx290 *imx290) { /* Only CSI2 is supported for now: */ @@ -1187,6 +1527,8 @@ static int imx290_parse_dt(struct imx290 *imx290) int ret; s64 fq; + imx290->model = of_device_get_match_data(imx290->dev); + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL); if (!endpoint) { dev_err(imx290->dev, "Endpoint node not found\n"); @@ -1260,7 +1602,7 @@ static int imx290_probe(struct i2c_client *client) imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) return dev_err_probe(dev, PTR_ERR(imx290->xclk), - "Could not get xclk"); + "Could not get xclk\n"); ret = imx290_get_regulators(dev, imx290); if (ret < 0) @@ -1304,6 +1646,9 @@ static int imx290_probe(struct i2c_client *client) if (ret) goto err_pm; + v4l2_i2c_subdev_set_name(&imx290->sd, client, + imx290->model->name, NULL); + /* * Finally, register the V4L2 subdev. This must be done after * initializing everything as the subdev can be used immediately after @@ -1352,8 +1697,21 @@ static void imx290_remove(struct i2c_client *client) } static const struct of_device_id imx290_of_match[] = { - { .compatible = "sony,imx290" }, - { /* sentinel */ } + { + /* Deprecated - synonym for "sony,imx290lqr" */ + .compatible = "sony,imx290", + .data = &imx290_models[IMX290_MODEL_IMX290LQR], + }, { + .compatible = "sony,imx290lqr", + .data = &imx290_models[IMX290_MODEL_IMX290LQR], + }, { + .compatible = "sony,imx290llr", + .data = &imx290_models[IMX290_MODEL_IMX290LLR], + }, { + .compatible = "sony,imx327lqr", + .data = &imx290_models[IMX290_MODEL_IMX327LQR], + }, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, imx290_of_match); @@ -1362,8 +1720,8 @@ static struct i2c_driver imx290_i2c_driver = { .remove = imx290_remove, .driver = { .name = "imx290", - .pm = &imx290_pm_ops, - .of_match_table = of_match_ptr(imx290_of_match), + .pm = pm_ptr(&imx290_pm_ops), + .of_match_table = imx290_of_match, }, }; |