From 00233bcdf9f984e5e902b92abc30a3395da1ae1a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 4 Jun 2024 19:15:12 +0100 Subject: [PATCH 1/8] media: i2c: imx477: Clean up duplicated registers Registers 0x0342 / 0x0343 are set via IMX477_REG_LINE_LENGTH as V4L2_CID_HBLANK, so shouldn't be in the register tables. Registers 0x0340 / 0x0341 are set via IMX477_REG_FRAME_LENGTH as V4L2_CID_VBLANK so shouldn't be in the register tables. Registers 0x0112 and 0x0113 set the bit depth, so should be per mode rather than in the common table and overridden for the 10bit mode(s). Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx477.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 317f9adf9f6ba2..88236b5085d0a8 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -512,8 +512,6 @@ static const struct imx477_reg mode_common_regs[] = { {0xb21f, 0x04}, {0xb35c, 0x00}, {0xb35e, 0x08}, - {0x0112, 0x0c}, - {0x0113, 0x0c}, {0x0114, 0x01}, {0x0350, 0x00}, {0xbcf1, 0x02}, @@ -522,8 +520,8 @@ static const struct imx477_reg mode_common_regs[] = { /* 12 mpix 10fps */ static const struct imx477_reg mode_4056x3040_regs[] = { - {0x0342, 0x5d}, - {0x0343, 0xc0}, + {0x0112, 0x0c}, + {0x0113, 0x0c}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x00}, @@ -632,8 +630,8 @@ static const struct imx477_reg mode_4056x3040_regs[] = { /* 2x2 binned. 40fps */ static const struct imx477_reg mode_2028x1520_regs[] = { - {0x0342, 0x31}, - {0x0343, 0xc4}, + {0x0112, 0x0c}, + {0x0113, 0x0c}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x00}, @@ -731,8 +729,8 @@ static const struct imx477_reg mode_2028x1520_regs[] = { /* 1080p cropped mode */ static const struct imx477_reg mode_2028x1080_regs[] = { - {0x0342, 0x31}, - {0x0343, 0xc4}, + {0x0112, 0x0c}, + {0x0113, 0x0c}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x01}, @@ -830,6 +828,8 @@ static const struct imx477_reg mode_2028x1080_regs[] = { /* 4x4 binned. 120fps */ static const struct imx477_reg mode_1332x990_regs[] = { + {0x0112, 0x0a}, + {0x0113, 0x0a}, {0x420b, 0x01}, {0x990c, 0x00}, {0x990d, 0x08}, @@ -842,13 +842,6 @@ static const struct imx477_reg mode_1332x990_regs[] = { {0x9a4b, 0x06}, {0x9a4c, 0x06}, {0x9a4d, 0x06}, - {0x0112, 0x0a}, - {0x0113, 0x0a}, - {0x0114, 0x01}, - {0x0342, 0x1a}, - {0x0343, 0x08}, - {0x0340, 0x04}, - {0x0341, 0x1a}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x02}, From 457c0f42776c3666b0cc3c97a1f96b8aecee8b4e Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 5 Jun 2024 18:24:12 +0100 Subject: [PATCH 2/8] media: i2c: imx477: Extract more registers from mode tables to common There are a fair number of registers duplicated in all the mode tables, so move those into the common table. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx477.c | 215 ++++++++----------------------------- 1 file changed, 43 insertions(+), 172 deletions(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 88236b5085d0a8..fe9e6bd80e9480 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -516,6 +516,49 @@ static const struct imx477_reg mode_common_regs[] = { {0x0350, 0x00}, {0xbcf1, 0x02}, {0x3ff9, 0x01}, + {0x0220, 0x00}, + {0x0221, 0x11}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0902, 0x02}, + {0x3140, 0x02}, + {0x3c00, 0x00}, + {0x9e9a, 0x2f}, + {0x9e9b, 0x2f}, + {0x9e9c, 0x2f}, + {0x9e9d, 0x00}, + {0x9e9e, 0x00}, + {0x9e9f, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x030b, 0x02}, + {0x030d, 0x02}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, + {0x0822, 0x00}, + {0x0823, 0x00}, + {0x080a, 0x00}, + {0x080b, 0x7f}, + {0x080c, 0x00}, + {0x080d, 0x4f}, + {0x080e, 0x00}, + {0x080f, 0x77}, + {0x0810, 0x00}, + {0x0811, 0x5f}, + {0x0812, 0x00}, + {0x0813, 0x57}, + {0x0814, 0x00}, + {0x0815, 0x4f}, + {0x0816, 0x01}, + {0x0817, 0x27}, + {0x0818, 0x00}, + {0x0819, 0x3f}, + {0x3e20, 0x01}, + {0x3e37, 0x00}, + {0x3f50, 0x00}, }; /* 12 mpix 10fps */ @@ -536,17 +579,8 @@ static const struct imx477_reg mode_4056x3040_regs[] = { {0x00fd, 0x0a}, {0x00fe, 0x0a}, {0x00ff, 0x0a}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, {0x0900, 0x00}, {0x0901, 0x11}, - {0x0902, 0x02}, - {0x3140, 0x02}, - {0x3c00, 0x00}, {0x3c01, 0x03}, {0x3c02, 0xa2}, {0x3f0d, 0x01}, @@ -565,12 +599,6 @@ static const struct imx477_reg mode_4056x3040_regs[] = { {0x936d, 0x28}, {0x9304, 0x00}, {0x9305, 0x00}, - {0x9e9a, 0x2f}, - {0x9e9b, 0x2f}, - {0x9e9c, 0x2f}, - {0x9e9d, 0x00}, - {0x9e9e, 0x00}, - {0x9e9f, 0x00}, {0xa2a9, 0x60}, {0xa2b7, 0x00}, {0x0401, 0x00}, @@ -588,42 +616,14 @@ static const struct imx477_reg mode_4056x3040_regs[] = { {0x034d, 0xd8}, {0x034e, 0x0b}, {0x034f, 0xe0}, - {0x0301, 0x05}, - {0x0303, 0x02}, {0x0305, 0x04}, {0x0306, 0x01}, {0x0307, 0x5e}, {0x0309, 0x0c}, - {0x030b, 0x02}, - {0x030d, 0x02}, - {0x0310, 0x01}, - {0x0820, 0x07}, - {0x0821, 0x08}, - {0x0822, 0x00}, - {0x0823, 0x00}, - {0x080a, 0x00}, - {0x080b, 0x7f}, - {0x080c, 0x00}, - {0x080d, 0x4f}, - {0x080e, 0x00}, - {0x080f, 0x77}, - {0x0810, 0x00}, - {0x0811, 0x5f}, - {0x0812, 0x00}, - {0x0813, 0x57}, - {0x0814, 0x00}, - {0x0815, 0x4f}, - {0x0816, 0x01}, - {0x0817, 0x27}, - {0x0818, 0x00}, - {0x0819, 0x3f}, {0xe04c, 0x00}, {0xe04d, 0x7f}, {0xe04e, 0x00}, {0xe04f, 0x1f}, - {0x3e20, 0x01}, - {0x3e37, 0x00}, - {0x3f50, 0x00}, {0x3f56, 0x02}, {0x3f57, 0xae}, }; @@ -640,17 +640,8 @@ static const struct imx477_reg mode_2028x1520_regs[] = { {0x0349, 0xd7}, {0x034a, 0x0b}, {0x034b, 0xdf}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, {0x0900, 0x01}, {0x0901, 0x22}, - {0x0902, 0x02}, - {0x3140, 0x02}, - {0x3c00, 0x00}, {0x3c01, 0x03}, {0x3c02, 0xa2}, {0x3f0d, 0x01}, @@ -664,12 +655,6 @@ static const struct imx477_reg mode_2028x1520_regs[] = { {0x936d, 0x5f}, {0x9304, 0x00}, {0x9305, 0x00}, - {0x9e9a, 0x2f}, - {0x9e9b, 0x2f}, - {0x9e9c, 0x2f}, - {0x9e9d, 0x00}, - {0x9e9e, 0x00}, - {0x9e9f, 0x00}, {0xa2a9, 0x60}, {0xa2b7, 0x00}, {0x0401, 0x00}, @@ -687,42 +672,14 @@ static const struct imx477_reg mode_2028x1520_regs[] = { {0x034d, 0xec}, {0x034e, 0x05}, {0x034f, 0xf0}, - {0x0301, 0x05}, - {0x0303, 0x02}, {0x0305, 0x04}, {0x0306, 0x01}, {0x0307, 0x5e}, {0x0309, 0x0c}, - {0x030b, 0x02}, - {0x030d, 0x02}, - {0x0310, 0x01}, - {0x0820, 0x07}, - {0x0821, 0x08}, - {0x0822, 0x00}, - {0x0823, 0x00}, - {0x080a, 0x00}, - {0x080b, 0x7f}, - {0x080c, 0x00}, - {0x080d, 0x4f}, - {0x080e, 0x00}, - {0x080f, 0x77}, - {0x0810, 0x00}, - {0x0811, 0x5f}, - {0x0812, 0x00}, - {0x0813, 0x57}, - {0x0814, 0x00}, - {0x0815, 0x4f}, - {0x0816, 0x01}, - {0x0817, 0x27}, - {0x0818, 0x00}, - {0x0819, 0x3f}, {0xe04c, 0x00}, {0xe04d, 0x7f}, {0xe04e, 0x00}, {0xe04f, 0x1f}, - {0x3e20, 0x01}, - {0x3e37, 0x00}, - {0x3f50, 0x00}, {0x3f56, 0x01}, {0x3f57, 0x6c}, }; @@ -739,17 +696,8 @@ static const struct imx477_reg mode_2028x1080_regs[] = { {0x0349, 0xd7}, {0x034a, 0x0a}, {0x034b, 0x27}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, {0x0900, 0x01}, {0x0901, 0x22}, - {0x0902, 0x02}, - {0x3140, 0x02}, - {0x3c00, 0x00}, {0x3c01, 0x03}, {0x3c02, 0xa2}, {0x3f0d, 0x01}, @@ -763,12 +711,6 @@ static const struct imx477_reg mode_2028x1080_regs[] = { {0x936d, 0x5f}, {0x9304, 0x00}, {0x9305, 0x00}, - {0x9e9a, 0x2f}, - {0x9e9b, 0x2f}, - {0x9e9c, 0x2f}, - {0x9e9d, 0x00}, - {0x9e9e, 0x00}, - {0x9e9f, 0x00}, {0xa2a9, 0x60}, {0xa2b7, 0x00}, {0x0401, 0x00}, @@ -786,42 +728,14 @@ static const struct imx477_reg mode_2028x1080_regs[] = { {0x034d, 0xec}, {0x034e, 0x04}, {0x034f, 0x38}, - {0x0301, 0x05}, - {0x0303, 0x02}, {0x0305, 0x04}, {0x0306, 0x01}, {0x0307, 0x5e}, {0x0309, 0x0c}, - {0x030b, 0x02}, - {0x030d, 0x02}, - {0x0310, 0x01}, - {0x0820, 0x07}, - {0x0821, 0x08}, - {0x0822, 0x00}, - {0x0823, 0x00}, - {0x080a, 0x00}, - {0x080b, 0x7f}, - {0x080c, 0x00}, - {0x080d, 0x4f}, - {0x080e, 0x00}, - {0x080f, 0x77}, - {0x0810, 0x00}, - {0x0811, 0x5f}, - {0x0812, 0x00}, - {0x0813, 0x57}, - {0x0814, 0x00}, - {0x0815, 0x4f}, - {0x0816, 0x01}, - {0x0817, 0x27}, - {0x0818, 0x00}, - {0x0819, 0x3f}, {0xe04c, 0x00}, {0xe04d, 0x7f}, {0xe04e, 0x00}, {0xe04f, 0x1f}, - {0x3e20, 0x01}, - {0x3e37, 0x00}, - {0x3f50, 0x00}, {0x3f56, 0x01}, {0x3f57, 0x6c}, }; @@ -857,17 +771,8 @@ static const struct imx477_reg mode_1332x990_regs[] = { {0x00fe, 0x0a}, {0x00ff, 0x0a}, {0xe013, 0x00}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, {0x0900, 0x01}, {0x0901, 0x22}, - {0x0902, 0x02}, - {0x3140, 0x02}, - {0x3c00, 0x00}, {0x3c01, 0x01}, {0x3c02, 0x9c}, {0x3f0d, 0x00}, @@ -886,12 +791,6 @@ static const struct imx477_reg mode_1332x990_regs[] = { {0x936d, 0x5f}, {0x9304, 0x03}, {0x9305, 0x80}, - {0x9e9a, 0x2f}, - {0x9e9b, 0x2f}, - {0x9e9c, 0x2f}, - {0x9e9d, 0x00}, - {0x9e9e, 0x00}, - {0x9e9f, 0x00}, {0xa2a9, 0x27}, {0xa2b7, 0x03}, {0x0401, 0x00}, @@ -909,42 +808,14 @@ static const struct imx477_reg mode_1332x990_regs[] = { {0x034d, 0x34}, {0x034e, 0x03}, {0x034f, 0xde}, - {0x0301, 0x05}, - {0x0303, 0x02}, {0x0305, 0x02}, {0x0306, 0x00}, {0x0307, 0xaf}, {0x0309, 0x0a}, - {0x030b, 0x02}, - {0x030d, 0x02}, - {0x0310, 0x01}, - {0x0820, 0x07}, - {0x0821, 0x08}, - {0x0822, 0x00}, - {0x0823, 0x00}, - {0x080a, 0x00}, - {0x080b, 0x7f}, - {0x080c, 0x00}, - {0x080d, 0x4f}, - {0x080e, 0x00}, - {0x080f, 0x77}, - {0x0810, 0x00}, - {0x0811, 0x5f}, - {0x0812, 0x00}, - {0x0813, 0x57}, - {0x0814, 0x00}, - {0x0815, 0x4f}, - {0x0816, 0x01}, - {0x0817, 0x27}, - {0x0818, 0x00}, - {0x0819, 0x3f}, {0xe04c, 0x00}, {0xe04d, 0x5f}, {0xe04e, 0x00}, {0xe04f, 0x1f}, - {0x3e20, 0x01}, - {0x3e37, 0x00}, - {0x3f50, 0x00}, {0x3f56, 0x00}, {0x3f57, 0xbf}, }; From 1a4f4f7cd93ac8da849d4ccbe9a0cf1b253f4745 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 5 Jun 2024 19:19:50 +0100 Subject: [PATCH 3/8] media: i2c: imx477: Extend V4L2_CID_VBLANK range The driver was using a struct v4l2_fract for the min frame time to determine the range for V4L2_CID_VBLANK, and a second to set the default VBLANK value for each mode. However actually the sensor will accept any VBLANK value down to 1 line, and using a struct v4l2_fract to hold the default framerate (which is an integer in all cases) is rather overkill. Drop the minimum frame time, and use a simple integer to set the default. This actually increases the max frame rate in all modes slightly. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx477.c | 57 +++++++++----------------------------- 1 file changed, 13 insertions(+), 44 deletions(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index fe9e6bd80e9480..88c7c00c80efce 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -53,6 +53,7 @@ MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); /* V_TIMING internal */ #define IMX477_REG_FRAME_LENGTH 0x0340 #define IMX477_FRAME_LENGTH_MAX 0xffdc +#define IMX477_VBLANK_MIN 4 /* H_TIMING internal */ #define IMX477_REG_LINE_LENGTH 0x0342 @@ -154,11 +155,8 @@ struct imx477_mode { /* Analog crop rectangle. */ struct v4l2_rect crop; - /* Highest possible framerate. */ - struct v4l2_fract timeperframe_min; - /* Default framerate. */ - struct v4l2_fract timeperframe_default; + unsigned int framerate_default; /* Default register values */ struct imx477_reg_list reg_list; @@ -833,14 +831,7 @@ static const struct imx477_mode supported_modes_12bit[] = { .width = 4056, .height = 3040, }, - .timeperframe_min = { - .numerator = 100, - .denominator = 1000 - }, - .timeperframe_default = { - .numerator = 100, - .denominator = 1000 - }, + .framerate_default = 10, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs), .regs = mode_4056x3040_regs, @@ -857,14 +848,7 @@ static const struct imx477_mode supported_modes_12bit[] = { .width = 4056, .height = 3040, }, - .timeperframe_min = { - .numerator = 100, - .denominator = 4000 - }, - .timeperframe_default = { - .numerator = 100, - .denominator = 3000 - }, + .framerate_default = 30, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_2028x1520_regs), .regs = mode_2028x1520_regs, @@ -881,14 +865,7 @@ static const struct imx477_mode supported_modes_12bit[] = { .width = 4056, .height = 2160, }, - .timeperframe_min = { - .numerator = 100, - .denominator = 5000 - }, - .timeperframe_default = { - .numerator = 100, - .denominator = 3000 - }, + .framerate_default = 30, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_2028x1080_regs), .regs = mode_2028x1080_regs, @@ -916,14 +893,7 @@ static const struct imx477_mode supported_modes_10bit[] = { .width = 2664, .height = 1980, }, - .timeperframe_min = { - .numerator = 100, - .denominator = 12000 - }, - .timeperframe_default = { - .numerator = 100, - .denominator = 12000 - }, + .framerate_default = 120, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1332x990_regs), .regs = mode_1332x990_regs, @@ -1459,13 +1429,13 @@ static int imx477_get_pad_format(struct v4l2_subdev *sd, static unsigned int imx477_get_frame_length(const struct imx477_mode *mode, - const struct v4l2_fract *timeperframe) + unsigned int framerate_default) { u64 frame_length; - frame_length = (u64)timeperframe->numerator * IMX477_PIXEL_RATE; + frame_length = IMX477_PIXEL_RATE; do_div(frame_length, - (u64)timeperframe->denominator * mode->line_length_pix); + (u64)framerate_default * mode->line_length_pix); if (WARN_ON(frame_length > IMX477_FRAME_LENGTH_MAX)) frame_length = IMX477_FRAME_LENGTH_MAX; @@ -1475,21 +1445,20 @@ unsigned int imx477_get_frame_length(const struct imx477_mode *mode, static void imx477_set_framing_limits(struct imx477 *imx477) { - unsigned int frm_length_min, frm_length_default, hblank_min; + unsigned int frm_length_default, hblank_min; const struct imx477_mode *mode = imx477->mode; - frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min); frm_length_default = - imx477_get_frame_length(mode, &mode->timeperframe_default); + imx477_get_frame_length(mode, mode->framerate_default); /* Default to no long exposure multiplier. */ imx477->long_exp_shift = 0; /* Update limits and set FPS to default */ - __v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height, + __v4l2_ctrl_modify_range(imx477->vblank, 1, ((1 << IMX477_LONG_EXP_SHIFT_MAX) * IMX477_FRAME_LENGTH_MAX) - mode->height, - 1, frm_length_default - mode->height); + IMX477_VBLANK_MIN, frm_length_default - mode->height); /* Setting this will adjust the exposure limits as well. */ __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height); From 5c872b2bc668394adbc1bba5f50bb3b7a4f94e9d Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Jun 2024 10:08:44 +0100 Subject: [PATCH 4/8] media: i2c: imx477: Add selection of (non)continuous clock mode The register set was always selecting continuous clock mode, even though all our overlays were saying it should be non-continuous. Read the configuration from fwnode and configure the sensor accordingly. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx477.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 88c7c00c80efce..3595e474d40e60 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -113,6 +113,8 @@ MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); #define IMX477_REG_XVS_IO_CTRL 0x3040 #define IMX477_REG_EXTOUT_EN 0x4b81 +#define IMX477_REG_FRAME_BLANKSTOP_CLK 0xE000 + /* Embedded metadata stream structure */ #define IMX477_EMBEDDED_LINE_WIDTH 16384 #define IMX477_NUM_EMBEDDED_LINES 1 @@ -210,7 +212,6 @@ static const struct imx477_reg mode_common_regs[] = { {0x0136, 0x18}, {0x0137, 0x00}, {0x0138, 0x01}, - {0xe000, 0x00}, {0xe07a, 0x01}, {0x0808, 0x02}, {0x4ae9, 0x18}, @@ -1004,6 +1005,11 @@ struct imx477 { /* Streaming on/off */ bool streaming; + /* Flags field from parsing the endpoint - used for (non)continuous + * clock mode + */ + unsigned int csi2_flags; + /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -1604,6 +1610,12 @@ static int imx477_start_streaming(struct imx477 *imx477) __func__); return ret; } + + imx477_write_reg(imx477, IMX477_REG_FRAME_BLANKSTOP_CLK, + IMX477_REG_VALUE_08BIT, + imx477->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ? + 1 : 0); + imx477->common_regs_written = true; } @@ -2029,6 +2041,8 @@ static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477) goto error_out; } + imx477->csi2_flags = ep_cfg.bus.mipi_csi2.flags; + ret = 0; error_out: From 6491891768309e60fb56ee592eb4028097c0429a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Jun 2024 18:30:36 +0100 Subject: [PATCH 5/8] media: i2c: imx477: Add option for double link frequency (900MHz) Pi5 can support higher CSI2 link frequencies than Pi 0-4, and hence higher framerates. The simplest change is to change the DIV_IOP_PX divider from the current value of 2 to 1 to double the frequency. This is slightly outside the max rate nominally supported by RP1, but seems reliable. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx477.c | 56 ++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 3595e474d40e60..5a34025c5da609 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -86,6 +86,8 @@ MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); #define IMX477_DGTL_GAIN_DEFAULT 0x0100 #define IMX477_DGTL_GAIN_STEP 1 +#define IMX477_REG_DIV_IOP_PX 0x030b + /* Test Pattern Control */ #define IMX477_REG_TEST_PATTERN 0x0600 #define IMX477_TEST_PATTERN_DISABLE 0 @@ -151,7 +153,7 @@ struct imx477_mode { /* Frame height */ unsigned int height; - /* H-timing in pixels */ + /* H-timing in pixels when at 450MHz link freq */ unsigned int line_length_pix; /* Analog crop rectangle. */ @@ -171,12 +173,18 @@ enum { IMX477_LINK_FREQ_456MHZ, }; -static const s64 link_freqs[] = { +static const u64 imx477_link_freq_menu[] = { [IMX477_LINK_FREQ_450MHZ] = 450000000, [IMX477_LINK_FREQ_453MHZ] = 453000000, [IMX477_LINK_FREQ_456MHZ] = 456000000, }; +static const u64 imx477_double_link_freq_menu[] = { + [IMX477_LINK_FREQ_450MHZ] = 450000000 * 2, + [IMX477_LINK_FREQ_453MHZ] = 453000000 * 2, + [IMX477_LINK_FREQ_456MHZ] = 456000000 * 2, +}; + /* 450MHz is the nominal "default" link frequency */ static const struct imx477_reg link_450Mhz_regs[] = { {0x030E, 0x00}, @@ -532,7 +540,6 @@ static const struct imx477_reg mode_common_regs[] = { {0x9e9f, 0x00}, {0x0301, 0x05}, {0x0303, 0x02}, - {0x030b, 0x02}, {0x030d, 0x02}, {0x0310, 0x01}, {0x0820, 0x07}, @@ -825,7 +832,7 @@ static const struct imx477_mode supported_modes_12bit[] = { /* 12MPix 10fps mode */ .width = 4056, .height = 3040, - .line_length_pix = 0x5dc0, + .line_length_pix = 24000, .crop = { .left = IMX477_PIXEL_ARRAY_LEFT, .top = IMX477_PIXEL_ARRAY_TOP, @@ -842,7 +849,7 @@ static const struct imx477_mode supported_modes_12bit[] = { /* 2x2 binned 40fps mode */ .width = 2028, .height = 1520, - .line_length_pix = 0x31c4, + .line_length_pix = 12740, .crop = { .left = IMX477_PIXEL_ARRAY_LEFT, .top = IMX477_PIXEL_ARRAY_TOP, @@ -859,7 +866,7 @@ static const struct imx477_mode supported_modes_12bit[] = { /* 1080p 50fps cropped mode */ .width = 2028, .height = 1080, - .line_length_pix = 0x31c4, + .line_length_pix = 12740, .crop = { .left = IMX477_PIXEL_ARRAY_LEFT, .top = IMX477_PIXEL_ARRAY_TOP + 440, @@ -1010,6 +1017,12 @@ struct imx477 { */ unsigned int csi2_flags; + /* + * Flag that CSI2 link is running at twice IMX477_DEFAULT_LINK_FREQ. + * line_length_pix can be halved in that case. + */ + bool double_link_freq; + /* Rewrite common registers on stream on? */ bool common_regs_written; @@ -1453,6 +1466,7 @@ static void imx477_set_framing_limits(struct imx477 *imx477) { unsigned int frm_length_default, hblank_min; const struct imx477_mode *mode = imx477->mode; + unsigned int line_length_pix; frm_length_default = imx477_get_frame_length(mode, mode->framerate_default); @@ -1469,7 +1483,10 @@ static void imx477_set_framing_limits(struct imx477 *imx477) /* Setting this will adjust the exposure limits as well. */ __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height); - hblank_min = mode->line_length_pix - mode->width; + line_length_pix = mode->line_length_pix; + if (imx477->double_link_freq) + line_length_pix /= 2; + hblank_min = line_length_pix - mode->width; __v4l2_ctrl_modify_range(imx477->hblank, hblank_min, IMX477_LINE_LENGTH_MAX, 1, hblank_min); __v4l2_ctrl_s_ctrl(imx477->hblank, hblank_min); @@ -1616,6 +1633,10 @@ static int imx477_start_streaming(struct imx477 *imx477) imx477->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ? 1 : 0); + imx477_write_reg(imx477, IMX477_REG_DIV_IOP_PX, + IMX477_REG_VALUE_08BIT, + imx477->double_link_freq ? 1 : 2); + imx477->common_regs_written = true; } @@ -1873,6 +1894,7 @@ static int imx477_init_controls(struct imx477 *imx477) struct v4l2_ctrl_handler *ctrl_hdlr; struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); struct v4l2_fwnode_device_properties props; + const u64 *link_freq_menu; unsigned int i; int ret; @@ -1894,10 +1916,15 @@ static int imx477_init_controls(struct imx477 *imx477) imx477->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* LINK_FREQ is also read only */ + if (imx477->double_link_freq) + link_freq_menu = &imx477_double_link_freq_menu[imx477->link_freq_idx]; + else + link_freq_menu = &imx477_link_freq_menu[imx477->link_freq_idx]; + imx477->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_LINK_FREQ, 0, 0, - &link_freqs[imx477->link_freq_idx]); + link_freq_menu); if (imx477->link_freq) imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -2027,14 +2054,21 @@ static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477) goto error_out; } - for (i = 0; i < ARRAY_SIZE(link_freqs); i++) { - if (link_freqs[i] == ep_cfg.link_frequencies[0]) { + for (i = 0; i < ARRAY_SIZE(imx477_link_freq_menu); i++) { + if (imx477_link_freq_menu[i] == ep_cfg.link_frequencies[0] || + imx477_double_link_freq_menu[i] == + ep_cfg.link_frequencies[0]) { imx477->link_freq_idx = i; + + if (imx477_double_link_freq_menu[i] == + ep_cfg.link_frequencies[0]) + imx477->double_link_freq = true; + break; } } - if (i == ARRAY_SIZE(link_freqs)) { + if (i == ARRAY_SIZE(imx477_link_freq_menu)) { dev_err(dev, "Link frequency not supported: %lld\n", ep_cfg.link_frequencies[0]); ret = -EINVAL; From 40ce9abbcf129466a3bbbb72e07e5a647aed6eb3 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Jun 2024 18:32:52 +0100 Subject: [PATCH 6/8] dtoverlays: Add an override for imx477 and 378 for higher link freq The driver can now support a link freq of 900MHz (1.8Gbit/s/lane) for higher frame rates. Add an override for "pi5" that changes this link frequency. Signed-off-by: Dave Stevenson --- arch/arm/boot/dts/overlays/README | 2 ++ arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi | 1 + 2 files changed, 3 insertions(+) diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 0dbf4b5101f19e..ca1a489191e514 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -2857,6 +2857,7 @@ Params: rotation Mounting rotation of the camera sensor (0 or sync-sink Configure as vsync sink link-frequency Allowable link frequency values to use in Hz: 450000000 (default), 453000000, 456000000. + pi5 Sets the CSI2 link frequency to 900MHz. Name: imx462 @@ -2901,6 +2902,7 @@ Params: rotation Mounting rotation of the camera sensor (0 or sync-sink Configure as vsync sink link-frequency Allowable link frequency values to use in Hz: 450000000 (default), 453000000, 456000000. + pi5 Sets the CSI2 link frequency to 900MHz Name: imx500 diff --git a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi index 17d5b4aa204211..4da523b2390e05 100644 --- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi +++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi @@ -81,6 +81,7 @@ <&cam_node>, "VANA-supply:0=",<&cam0_reg>; always-on = <0>, "+99"; link-frequency = <&cam_endpoint>,"link-frequencies#0"; + pi5 = <&cam_endpoint>, "link-frequencies#0=900000000"; }; }; From d8d0b541a14a76856916803785c46771dfff1bdf Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 7 Jun 2024 15:33:35 +0100 Subject: [PATCH 7/8] media: i2c: imx477: Add full res but cropped 16:9 mode For 4k30 recording we want 16:9 output, so add a cropped mode to achieve this. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx477.c | 81 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 5a34025c5da609..c93e9458a43329 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -634,6 +634,70 @@ static const struct imx477_reg mode_4056x3040_regs[] = { {0x3f57, 0xae}, }; +/* 12 mpix cropped to 16:9 10fps */ +static const struct imx477_reg mode_4056x2160_regs[] = { + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x01}, + {0x0347, 0xb8}, + {0x0348, 0x0f}, + {0x0349, 0xd7}, + {0x034a, 0x0a}, + {0x034b, 0x27}, + {0x00e3, 0x00}, + {0x00e4, 0x00}, + {0x00fc, 0x0a}, + {0x00fd, 0x0a}, + {0x00fe, 0x0a}, + {0x00ff, 0x0a}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x3c01, 0x03}, + {0x3c02, 0xa2}, + {0x3f0d, 0x01}, + {0x5748, 0x07}, + {0x5749, 0xff}, + {0x574a, 0x00}, + {0x574b, 0x00}, + {0x7b75, 0x0a}, + {0x7b76, 0x0c}, + {0x7b77, 0x07}, + {0x7b78, 0x06}, + {0x7b79, 0x3c}, + {0x7b53, 0x01}, + {0x9369, 0x5a}, + {0x936b, 0x55}, + {0x936d, 0x28}, + {0x9304, 0x00}, + {0x9305, 0x00}, + {0xa2a9, 0x60}, + {0xa2b7, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040a, 0x00}, + {0x040b, 0x00}, + {0x040c, 0x0f}, + {0x040d, 0xd8}, + {0x040e, 0x08}, + {0x040f, 0x70}, + {0x034c, 0x0f}, + {0x034d, 0xd8}, + {0x034e, 0x08}, + {0x034f, 0x70}, + {0x0305, 0x04}, + {0x0306, 0x01}, + {0x0307, 0x5e}, + {0xe04c, 0x00}, + {0xe04d, 0x7f}, + {0xe04e, 0x00}, + {0xe04f, 0x1f}, + {0x3f56, 0x02}, + {0x3f57, 0xae}, +}; + /* 2x2 binned. 40fps */ static const struct imx477_reg mode_2028x1520_regs[] = { {0x0112, 0x0c}, @@ -845,6 +909,23 @@ static const struct imx477_mode supported_modes_12bit[] = { .regs = mode_4056x3040_regs, }, }, + { + /* 12MPix cropped 16:9 mode */ + .width = 4056, + .height = 2160, + .line_length_pix = { 24000, 20000 }, + .crop = { + .left = IMX477_PIXEL_ARRAY_LEFT, + .top = IMX477_PIXEL_ARRAY_TOP + 440, + .width = 4056, + .height = 3040, + }, + .framerate_default = 10, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_4056x2160_regs), + .regs = mode_4056x2160_regs, + }, + }, { /* 2x2 binned 40fps mode */ .width = 2028, From a69a37432c14f3ca4cc1fef3f514c861b45fba60 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Fri, 7 Jun 2024 12:48:04 +0100 Subject: [PATCH 8/8] media: i2c: imx477: Add support for 10 or 12 bit readout to all modes FIXME: Dropping the default frame rate needs to be separated out. Switching between 10 and 12 bit mode only requires a couple of registers to change, and an associated change to the minimum HBLANK that can be supported in the mode based on how long it takes the CSI2 block to send each line of the image. Add suitable switching between the 2 for all modes. --- drivers/media/i2c/imx477.c | 160 +++++++++++++++---------------------- 1 file changed, 65 insertions(+), 95 deletions(-) diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index c93e9458a43329..4ef886ace8d135 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -43,6 +43,9 @@ MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); #define IMX477_REG_ORIENTATION 0x101 +#define IMX477_REG_CSI_DT_FMT_H 0x0112 +#define IMX477_REG_CSI_DT_FMT_L 0x0113 + #define IMX477_XCLK_FREQ 24000000 #define IMX477_DEFAULT_LINK_FREQ 450000000 @@ -86,6 +89,7 @@ MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); #define IMX477_DGTL_GAIN_DEFAULT 0x0100 #define IMX477_DGTL_GAIN_STEP 1 +#define IMX477_REG_IOP_PXCK_DIV 0x0309 #define IMX477_REG_DIV_IOP_PX 0x030b /* Test Pattern Control */ @@ -153,8 +157,11 @@ struct imx477_mode { /* Frame height */ unsigned int height; - /* H-timing in pixels when at 450MHz link freq */ - unsigned int line_length_pix; + /* + * H-timing in pixels when at 450MHz link freq + * Index 0 is for 12bpp. Index 1 is for 10bpp. + */ + unsigned int line_length_pix[2]; /* Analog crop rectangle. */ struct v4l2_rect crop; @@ -569,8 +576,6 @@ static const struct imx477_reg mode_common_regs[] = { /* 12 mpix 10fps */ static const struct imx477_reg mode_4056x3040_regs[] = { - {0x0112, 0x0c}, - {0x0113, 0x0c}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x00}, @@ -625,7 +630,6 @@ static const struct imx477_reg mode_4056x3040_regs[] = { {0x0305, 0x04}, {0x0306, 0x01}, {0x0307, 0x5e}, - {0x0309, 0x0c}, {0xe04c, 0x00}, {0xe04d, 0x7f}, {0xe04e, 0x00}, @@ -700,8 +704,6 @@ static const struct imx477_reg mode_4056x2160_regs[] = { /* 2x2 binned. 40fps */ static const struct imx477_reg mode_2028x1520_regs[] = { - {0x0112, 0x0c}, - {0x0113, 0x0c}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x00}, @@ -745,7 +747,6 @@ static const struct imx477_reg mode_2028x1520_regs[] = { {0x0305, 0x04}, {0x0306, 0x01}, {0x0307, 0x5e}, - {0x0309, 0x0c}, {0xe04c, 0x00}, {0xe04d, 0x7f}, {0xe04e, 0x00}, @@ -756,8 +757,6 @@ static const struct imx477_reg mode_2028x1520_regs[] = { /* 1080p cropped mode */ static const struct imx477_reg mode_2028x1080_regs[] = { - {0x0112, 0x0c}, - {0x0113, 0x0c}, {0x0344, 0x00}, {0x0345, 0x00}, {0x0346, 0x01}, @@ -801,7 +800,6 @@ static const struct imx477_reg mode_2028x1080_regs[] = { {0x0305, 0x04}, {0x0306, 0x01}, {0x0307, 0x5e}, - {0x0309, 0x0c}, {0xe04c, 0x00}, {0xe04d, 0x7f}, {0xe04e, 0x00}, @@ -812,8 +810,6 @@ static const struct imx477_reg mode_2028x1080_regs[] = { /* 4x4 binned. 120fps */ static const struct imx477_reg mode_1332x990_regs[] = { - {0x0112, 0x0a}, - {0x0113, 0x0a}, {0x420b, 0x01}, {0x990c, 0x00}, {0x990d, 0x08}, @@ -881,7 +877,6 @@ static const struct imx477_reg mode_1332x990_regs[] = { {0x0305, 0x02}, {0x0306, 0x00}, {0x0307, 0xaf}, - {0x0309, 0x0a}, {0xe04c, 0x00}, {0xe04d, 0x5f}, {0xe04e, 0x00}, @@ -891,12 +886,12 @@ static const struct imx477_reg mode_1332x990_regs[] = { }; /* Mode configs */ -static const struct imx477_mode supported_modes_12bit[] = { +static const struct imx477_mode supported_modes[] = { { /* 12MPix 10fps mode */ .width = 4056, .height = 3040, - .line_length_pix = 24000, + .line_length_pix = { 24000, 20000 }, .crop = { .left = IMX477_PIXEL_ARRAY_LEFT, .top = IMX477_PIXEL_ARRAY_TOP, @@ -930,7 +925,7 @@ static const struct imx477_mode supported_modes_12bit[] = { /* 2x2 binned 40fps mode */ .width = 2028, .height = 1520, - .line_length_pix = 12740, + .line_length_pix = { 12740, 10616 }, .crop = { .left = IMX477_PIXEL_ARRAY_LEFT, .top = IMX477_PIXEL_ARRAY_TOP, @@ -947,7 +942,7 @@ static const struct imx477_mode supported_modes_12bit[] = { /* 1080p 50fps cropped mode */ .width = 2028, .height = 1080, - .line_length_pix = 12740, + .line_length_pix = { 12740, 10616 }, .crop = { .left = IMX477_PIXEL_ARRAY_LEFT, .top = IMX477_PIXEL_ARRAY_TOP + 440, @@ -959,15 +954,12 @@ static const struct imx477_mode supported_modes_12bit[] = { .num_of_regs = ARRAY_SIZE(mode_2028x1080_regs), .regs = mode_2028x1080_regs, }, - } -}; - -static const struct imx477_mode supported_modes_10bit[] = { + }, { /* 120fps. 2x2 binned and cropped */ .width = 1332, .height = 990, - .line_length_pix = 6664, + .line_length_pix = { 7997, 6664 }, .crop = { /* * FIXME: the analog crop rectangle is actually @@ -1119,33 +1111,6 @@ static inline struct imx477 *to_imx477(struct v4l2_subdev *_sd) return container_of(_sd, struct imx477, sd); } -static inline void get_mode_table(unsigned int code, - const struct imx477_mode **mode_list, - unsigned int *num_modes) -{ - switch (code) { - /* 12-bit */ - case MEDIA_BUS_FMT_SRGGB12_1X12: - case MEDIA_BUS_FMT_SGRBG12_1X12: - case MEDIA_BUS_FMT_SGBRG12_1X12: - case MEDIA_BUS_FMT_SBGGR12_1X12: - *mode_list = supported_modes_12bit; - *num_modes = ARRAY_SIZE(supported_modes_12bit); - break; - /* 10-bit */ - case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - *mode_list = supported_modes_10bit; - *num_modes = ARRAY_SIZE(supported_modes_10bit); - break; - default: - *mode_list = NULL; - *num_modes = 0; - } -} - /* Read registers up to 2 at a time */ static int imx477_read_reg(struct imx477 *imx477, u16 reg, u32 len, u32 *val) { @@ -1241,7 +1206,7 @@ static u32 imx477_get_format_code(struct imx477 *imx477, u32 code) static void imx477_set_default_format(struct imx477 *imx477) { /* Set default mode to max resolution */ - imx477->mode = &supported_modes_12bit[0]; + imx477->mode = &supported_modes[0]; imx477->fmt_code = MEDIA_BUS_FMT_SRGGB12_1X12; } @@ -1257,8 +1222,8 @@ static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) mutex_lock(&imx477->mutex); /* Initialize try_fmt for the image pad */ - try_fmt_img->width = supported_modes_12bit[0].width; - try_fmt_img->height = supported_modes_12bit[0].height; + try_fmt_img->width = supported_modes[0].width; + try_fmt_img->height = supported_modes[0].height; try_fmt_img->code = imx477_get_format_code(imx477, MEDIA_BUS_FMT_SRGGB12_1X12); try_fmt_img->field = V4L2_FIELD_NONE; @@ -1436,20 +1401,15 @@ static int imx477_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; if (fse->pad == IMAGE_PAD) { - const struct imx477_mode *mode_list; - unsigned int num_modes; - - get_mode_table(fse->code, &mode_list, &num_modes); - - if (fse->index >= num_modes) + if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; if (fse->code != imx477_get_format_code(imx477, fse->code)) return -EINVAL; - fse->min_width = mode_list[fse->index].width; + fse->min_width = supported_modes[fse->index].width; fse->max_width = fse->min_width; - fse->min_height = mode_list[fse->index].height; + fse->min_height = supported_modes[fse->index].height; fse->max_height = fse->min_height; } else { if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) @@ -1527,31 +1487,12 @@ static int imx477_get_pad_format(struct v4l2_subdev *sd, return 0; } -static -unsigned int imx477_get_frame_length(const struct imx477_mode *mode, - unsigned int framerate_default) -{ - u64 frame_length; - - frame_length = IMX477_PIXEL_RATE; - do_div(frame_length, - (u64)framerate_default * mode->line_length_pix); - - if (WARN_ON(frame_length > IMX477_FRAME_LENGTH_MAX)) - frame_length = IMX477_FRAME_LENGTH_MAX; - - return max_t(unsigned int, frame_length, mode->height); -} - static void imx477_set_framing_limits(struct imx477 *imx477) { - unsigned int frm_length_default, hblank_min; + unsigned int hblank_min; const struct imx477_mode *mode = imx477->mode; unsigned int line_length_pix; - frm_length_default = - imx477_get_frame_length(mode, mode->framerate_default); - /* Default to no long exposure multiplier. */ imx477->long_exp_shift = 0; @@ -1559,12 +1500,23 @@ static void imx477_set_framing_limits(struct imx477 *imx477) __v4l2_ctrl_modify_range(imx477->vblank, 1, ((1 << IMX477_LONG_EXP_SHIFT_MAX) * IMX477_FRAME_LENGTH_MAX) - mode->height, - IMX477_VBLANK_MIN, frm_length_default - mode->height); - - /* Setting this will adjust the exposure limits as well. */ - __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height); + IMX477_VBLANK_MIN, IMX477_VBLANK_MIN); - line_length_pix = mode->line_length_pix; + switch (imx477->fmt_code) { + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + line_length_pix = mode->line_length_pix[0]; + break; + /* 10-bit */ + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + line_length_pix = mode->line_length_pix[1]; + break; + } if (imx477->double_link_freq) line_length_pix /= 2; hblank_min = line_length_pix - mode->width; @@ -1587,17 +1539,12 @@ static int imx477_set_pad_format(struct v4l2_subdev *sd, mutex_lock(&imx477->mutex); if (fmt->pad == IMAGE_PAD) { - const struct imx477_mode *mode_list; - unsigned int num_modes; - /* Bayer order varies with flips */ fmt->format.code = imx477_get_format_code(imx477, fmt->format.code); - get_mode_table(fmt->format.code, &mode_list, &num_modes); - - mode = v4l2_find_nearest_size(mode_list, - num_modes, + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); @@ -1606,7 +1553,8 @@ static int imx477_set_pad_format(struct v4l2_subdev *sd, framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; - } else if (imx477->mode != mode) { + } else if (imx477->mode != mode || + fmt->format.code != imx477->fmt_code) { imx477->mode = mode; imx477->fmt_code = fmt->format.code; imx477_set_framing_limits(imx477); @@ -1685,7 +1633,7 @@ static int imx477_start_streaming(struct imx477 *imx477) struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); const struct imx477_reg_list *reg_list, *freq_regs; const struct imx477_reg_list *extra_regs; - int ret, tm; + int ret, tm, val; if (!imx477->common_regs_written) { ret = imx477_write_regs(imx477, mode_common_regs, @@ -1729,6 +1677,28 @@ static int imx477_start_streaming(struct imx477 *imx477) return ret; } + switch (imx477->fmt_code) { + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + val = 0x0c; + break; + /* 10-bit */ + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + val = 0x0a; + break; + } + imx477_write_reg(imx477, IMX477_REG_CSI_DT_FMT_H, + IMX477_REG_VALUE_08BIT, val); + imx477_write_reg(imx477, IMX477_REG_CSI_DT_FMT_L, + IMX477_REG_VALUE_08BIT, val); + imx477_write_reg(imx477, IMX477_REG_IOP_PXCK_DIV, + IMX477_REG_VALUE_08BIT, val); + /* Set on-sensor DPC. */ imx477_write_reg(imx477, 0x0b05, IMX477_REG_VALUE_08BIT, !!dpc_enable); imx477_write_reg(imx477, 0x0b06, IMX477_REG_VALUE_08BIT, !!dpc_enable);