310 lines
9.1 KiB
Diff
310 lines
9.1 KiB
Diff
From 5886d3655e1d876325ca348cf29fad3e92a45492 Mon Sep 17 00:00:00 2001
|
|
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
|
Date: Thu, 17 Jun 2021 12:05:25 +0100
|
|
Subject: [PATCH] media: i2c: imx290: Support 60fps in 2 lane operation
|
|
|
|
Commit "97589ad61c73 media: i2c: imx290: Add support for 2 data lanes"
|
|
added support for running in two lane mode (instead of 4), but
|
|
without changing the link frequency that resulted in a max of 30fps.
|
|
|
|
Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
|
|
and pixel rate" then doubled the link frequency when in 2 lane mode,
|
|
but didn't undo the correction for running at only 30fps, just extending
|
|
horizontal blanking instead.
|
|
It also didn't update the CSI timing registers in accordance with the
|
|
datasheet.
|
|
|
|
Remove the 30fps limit on 2 lane by correcting the register config
|
|
in accordance with the datasheet for 60fps operation over 2 lanes.
|
|
Frame rate control (via V4L2_CID_VBLANK or HBLANK) can still reduce
|
|
the frame rate on 2 lanes back to 30fps.
|
|
|
|
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
|
---
|
|
drivers/media/i2c/imx290.c | 163 ++++++++++++++++++++++---------------
|
|
1 file changed, 97 insertions(+), 66 deletions(-)
|
|
|
|
--- a/drivers/media/i2c/imx290.c
|
|
+++ b/drivers/media/i2c/imx290.c
|
|
@@ -46,8 +46,7 @@ enum imx290_clk_index {
|
|
#define IMX290_VMAX_MAX 0x3fff
|
|
#define IMX290_HMAX_LOW 0x301c
|
|
#define IMX290_HMAX_HIGH 0x301d
|
|
-#define IMX290_HMAX_MIN_2LANE 4400 /* Min of 4400 pixels = 30fps */
|
|
-#define IMX290_HMAX_MIN_4LANE 2200 /* Min of 2200 pixels = 60fps */
|
|
+#define IMX290_HMAX_MIN 2200 /* Min of 2200 pixels = 60fps */
|
|
#define IMX290_HMAX_MAX 0xffff
|
|
|
|
#define IMX290_EXPOSURE_MIN 1
|
|
@@ -89,8 +88,11 @@ struct imx290_mode {
|
|
u8 link_freq_index;
|
|
struct v4l2_rect crop;
|
|
|
|
- const struct imx290_regval *data;
|
|
- u32 data_size;
|
|
+ const struct imx290_regval *mode_data;
|
|
+ u32 mode_data_size;
|
|
+ const struct imx290_regval *lane_data;
|
|
+ u32 lane_data_size;
|
|
+
|
|
|
|
/* Clock setup can vary. Index as enum imx290_clk_index */
|
|
const struct imx290_regval *clk_data[2];
|
|
@@ -242,8 +244,9 @@ static const struct imx290_regval imx290
|
|
{ 0x3480, 0x92 },
|
|
};
|
|
|
|
-static const struct imx290_regval imx290_1080p_settings[] = {
|
|
+static const struct imx290_regval imx290_1080p_common_settings[] = {
|
|
/* mode settings */
|
|
+ { IMX290_FR_FDG_SEL, 0x01 },
|
|
{ 0x3007, 0x00 },
|
|
{ 0x303a, 0x0c },
|
|
{ 0x3414, 0x0a },
|
|
@@ -253,8 +256,36 @@ static const struct imx290_regval imx290
|
|
{ 0x3419, 0x04 },
|
|
{ 0x3012, 0x64 },
|
|
{ 0x3013, 0x00 },
|
|
+};
|
|
+
|
|
+static const struct imx290_regval imx290_1080p_2lane_settings[] = {
|
|
+ { 0x3405, 0x00 },
|
|
/* data rate settings */
|
|
+ { IMX290_PHY_LANE_NUM, 0x01 },
|
|
+ { IMX290_CSI_LANE_MODE, 0x01 },
|
|
+ { 0x3446, 0x77 },
|
|
+ { 0x3447, 0x00 },
|
|
+ { 0x3448, 0x67 },
|
|
+ { 0x3449, 0x00 },
|
|
+ { 0x344a, 0x47 },
|
|
+ { 0x344b, 0x00 },
|
|
+ { 0x344c, 0x37 },
|
|
+ { 0x344d, 0x00 },
|
|
+ { 0x344e, 0x3f },
|
|
+ { 0x344f, 0x00 },
|
|
+ { 0x3450, 0xff },
|
|
+ { 0x3451, 0x00 },
|
|
+ { 0x3452, 0x3f },
|
|
+ { 0x3453, 0x00 },
|
|
+ { 0x3454, 0x37 },
|
|
+ { 0x3455, 0x00 },
|
|
+};
|
|
+
|
|
+static const struct imx290_regval imx290_1080p_4lane_settings[] = {
|
|
{ 0x3405, 0x10 },
|
|
+ /* data rate settings */
|
|
+ { IMX290_PHY_LANE_NUM, 0x03 },
|
|
+ { IMX290_CSI_LANE_MODE, 0x03 },
|
|
{ 0x3446, 0x57 },
|
|
{ 0x3447, 0x00 },
|
|
{ 0x3448, 0x37 },
|
|
@@ -297,8 +328,9 @@ static const struct imx290_regval imx290
|
|
{ 0x3480, 0x92 },
|
|
};
|
|
|
|
-static const struct imx290_regval imx290_720p_settings[] = {
|
|
+static const struct imx290_regval imx290_720p_common_settings[] = {
|
|
/* mode settings */
|
|
+ { IMX290_FR_FDG_SEL, 0x01 },
|
|
{ 0x3007, 0x10 },
|
|
{ 0x303a, 0x06 },
|
|
{ 0x3414, 0x04 },
|
|
@@ -308,8 +340,36 @@ static const struct imx290_regval imx290
|
|
{ 0x3419, 0x02 },
|
|
{ 0x3012, 0x64 },
|
|
{ 0x3013, 0x00 },
|
|
+};
|
|
+
|
|
+static const struct imx290_regval imx290_720p_2lane_settings[] = {
|
|
+ { 0x3405, 0x00 },
|
|
+ { IMX290_PHY_LANE_NUM, 0x01 },
|
|
+ { IMX290_CSI_LANE_MODE, 0x01 },
|
|
/* data rate settings */
|
|
+ { 0x3446, 0x67 },
|
|
+ { 0x3447, 0x00 },
|
|
+ { 0x3448, 0x57 },
|
|
+ { 0x3449, 0x00 },
|
|
+ { 0x344a, 0x2f },
|
|
+ { 0x344b, 0x00 },
|
|
+ { 0x344c, 0x27 },
|
|
+ { 0x344d, 0x00 },
|
|
+ { 0x344e, 0x2f },
|
|
+ { 0x344f, 0x00 },
|
|
+ { 0x3450, 0xbf },
|
|
+ { 0x3451, 0x00 },
|
|
+ { 0x3452, 0x2f },
|
|
+ { 0x3453, 0x00 },
|
|
+ { 0x3454, 0x27 },
|
|
+ { 0x3455, 0x00 },
|
|
+};
|
|
+
|
|
+static const struct imx290_regval imx290_720p_4lane_settings[] = {
|
|
{ 0x3405, 0x10 },
|
|
+ { IMX290_PHY_LANE_NUM, 0x03 },
|
|
+ { IMX290_CSI_LANE_MODE, 0x03 },
|
|
+ /* data rate settings */
|
|
{ 0x3446, 0x4f },
|
|
{ 0x3447, 0x00 },
|
|
{ 0x3448, 0x2f },
|
|
@@ -389,7 +449,7 @@ static const struct imx290_mode imx290_m
|
|
{
|
|
.width = 1920,
|
|
.height = 1080,
|
|
- .hmax = 0x1130,
|
|
+ .hmax = 0x0898,
|
|
.vmax = 0x0465,
|
|
.link_freq_index = FREQ_INDEX_1080P,
|
|
.crop = {
|
|
@@ -398,8 +458,10 @@ static const struct imx290_mode imx290_m
|
|
.width = 1920,
|
|
.height = 1080,
|
|
},
|
|
- .data = imx290_1080p_settings,
|
|
- .data_size = ARRAY_SIZE(imx290_1080p_settings),
|
|
+ .mode_data = imx290_1080p_common_settings,
|
|
+ .mode_data_size = ARRAY_SIZE(imx290_1080p_common_settings),
|
|
+ .lane_data = imx290_1080p_2lane_settings,
|
|
+ .lane_data_size = ARRAY_SIZE(imx290_1080p_2lane_settings),
|
|
.clk_data = {
|
|
[CLK_37_125] = imx290_37_125mhz_clock_1080p,
|
|
[CLK_74_25] = imx290_74_250mhz_clock_1080p,
|
|
@@ -409,7 +471,7 @@ static const struct imx290_mode imx290_m
|
|
{
|
|
.width = 1280,
|
|
.height = 720,
|
|
- .hmax = 0x19c8,
|
|
+ .hmax = 0x0ce4,
|
|
.vmax = 0x02ee,
|
|
.link_freq_index = FREQ_INDEX_720P,
|
|
.crop = {
|
|
@@ -418,8 +480,10 @@ static const struct imx290_mode imx290_m
|
|
.width = 1280,
|
|
.height = 720,
|
|
},
|
|
- .data = imx290_720p_settings,
|
|
- .data_size = ARRAY_SIZE(imx290_720p_settings),
|
|
+ .mode_data = imx290_720p_common_settings,
|
|
+ .mode_data_size = ARRAY_SIZE(imx290_720p_common_settings),
|
|
+ .lane_data = imx290_720p_2lane_settings,
|
|
+ .lane_data_size = ARRAY_SIZE(imx290_720p_2lane_settings),
|
|
.clk_data = {
|
|
[CLK_37_125] = imx290_37_125mhz_clock_1080p,
|
|
[CLK_74_25] = imx290_74_250mhz_clock_1080p,
|
|
@@ -441,8 +505,10 @@ static const struct imx290_mode imx290_m
|
|
.width = 1920,
|
|
.height = 1080,
|
|
},
|
|
- .data = imx290_1080p_settings,
|
|
- .data_size = ARRAY_SIZE(imx290_1080p_settings),
|
|
+ .mode_data = imx290_1080p_common_settings,
|
|
+ .mode_data_size = ARRAY_SIZE(imx290_1080p_common_settings),
|
|
+ .lane_data = imx290_1080p_4lane_settings,
|
|
+ .lane_data_size = ARRAY_SIZE(imx290_1080p_4lane_settings),
|
|
.clk_data = {
|
|
[CLK_37_125] = imx290_37_125mhz_clock_720p,
|
|
[CLK_74_25] = imx290_74_250mhz_clock_720p,
|
|
@@ -461,8 +527,10 @@ static const struct imx290_mode imx290_m
|
|
.width = 1280,
|
|
.height = 720,
|
|
},
|
|
- .data = imx290_720p_settings,
|
|
- .data_size = ARRAY_SIZE(imx290_720p_settings),
|
|
+ .mode_data = imx290_720p_common_settings,
|
|
+ .mode_data_size = ARRAY_SIZE(imx290_720p_common_settings),
|
|
+ .lane_data = imx290_720p_4lane_settings,
|
|
+ .lane_data_size = ARRAY_SIZE(imx290_720p_4lane_settings),
|
|
.clk_data = {
|
|
[CLK_37_125] = imx290_37_125mhz_clock_720p,
|
|
[CLK_74_25] = imx290_74_250mhz_clock_720p,
|
|
@@ -1016,8 +1084,18 @@ static int imx290_start_streaming(struct
|
|
}
|
|
|
|
/* Apply default values of current mode */
|
|
- ret = imx290_set_register_array(imx290, imx290->current_mode->data,
|
|
- imx290->current_mode->data_size);
|
|
+ ret = imx290_set_register_array(imx290,
|
|
+ imx290->current_mode->mode_data,
|
|
+ imx290->current_mode->mode_data_size);
|
|
+ if (ret < 0) {
|
|
+ dev_err(imx290->dev, "Could not set current mode\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Apply lane config registers of current mode */
|
|
+ ret = imx290_set_register_array(imx290,
|
|
+ imx290->current_mode->lane_data,
|
|
+ imx290->current_mode->lane_data_size);
|
|
if (ret < 0) {
|
|
dev_err(imx290->dev, "Could not set current mode\n");
|
|
return ret;
|
|
@@ -1082,49 +1160,6 @@ static int imx290_get_regulators(struct
|
|
imx290->supplies);
|
|
}
|
|
|
|
-static int imx290_set_data_lanes(struct imx290 *imx290)
|
|
-{
|
|
- int ret = 0, laneval, frsel;
|
|
-
|
|
- switch (imx290->nlanes) {
|
|
- case 2:
|
|
- laneval = 0x01;
|
|
- frsel = 0x02;
|
|
- break;
|
|
- case 4:
|
|
- laneval = 0x03;
|
|
- frsel = 0x01;
|
|
- break;
|
|
- default:
|
|
- /*
|
|
- * We should never hit this since the data lane count is
|
|
- * validated in probe itself
|
|
- */
|
|
- dev_err(imx290->dev, "Lane configuration not supported\n");
|
|
- ret = -EINVAL;
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- ret = imx290_write_reg(imx290, IMX290_PHY_LANE_NUM, laneval);
|
|
- if (ret) {
|
|
- dev_err(imx290->dev, "Error setting Physical Lane number register\n");
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- ret = imx290_write_reg(imx290, IMX290_CSI_LANE_MODE, laneval);
|
|
- if (ret) {
|
|
- dev_err(imx290->dev, "Error setting CSI Lane mode register\n");
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- ret = imx290_write_reg(imx290, IMX290_FR_FDG_SEL, frsel);
|
|
- if (ret)
|
|
- dev_err(imx290->dev, "Error setting FR/FDG SEL register\n");
|
|
-
|
|
-exit:
|
|
- return ret;
|
|
-}
|
|
-
|
|
static int imx290_power_on(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
@@ -1149,9 +1184,6 @@ static int imx290_power_on(struct device
|
|
gpiod_set_value_cansleep(imx290->rst_gpio, 0);
|
|
usleep_range(30000, 31000);
|
|
|
|
- /* Set data lane count */
|
|
- imx290_set_data_lanes(imx290);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -1275,8 +1307,7 @@ static int imx290_probe(struct i2c_clien
|
|
ret = -EINVAL;
|
|
goto free_err;
|
|
}
|
|
- imx290->hmax_min = (imx290->nlanes == 2) ? IMX290_HMAX_MIN_2LANE :
|
|
- IMX290_HMAX_MIN_4LANE;
|
|
+ imx290->hmax_min = IMX290_HMAX_MIN;
|
|
|
|
dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
|
|
|