From 9fe4d33587bd7931e2a0decc7c4881945a1c0ab3 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Thu, 25 Jun 2020 08:28:51 +0100
Subject: [PATCH] media: i2c: imx290: Add support for 74.25MHz clock

The existing driver only supported a clock of 37.125MHz, but the
sensor also supports 74.25MHz.

Add the relevant register modifications to support this alternate
clock frequency.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 119 ++++++++++++++++++++++++++++++-------
 1 file changed, 97 insertions(+), 22 deletions(-)

--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1,6 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Sony IMX290 CMOS Image Sensor Driver
+ * Sony IMX290/327 CMOS Image Sensor Driver
+ *
+ * The IMX290 and IMX327 are very similar 1920x1080 1/2.8 CMOS image sensors.
+ * IMX327 can support up to 60fps, whilst IMX290 support up to 120fps (only
+ * 10bit and when connected over 4 CSI-2 lanes).
  *
  * Copyright (C) 2019 FRAMOS GmbH.
  *
@@ -22,6 +26,11 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
+enum imx290_clk_index {
+	CLK_37_125,
+	CLK_74_25,
+};
+
 #define IMX290_STANDBY 0x3000
 #define IMX290_REGHOLD 0x3001
 #define IMX290_XMSTA 0x3002
@@ -60,11 +69,16 @@ struct imx290_mode {
 
 	const struct imx290_regval *data;
 	u32 data_size;
+
+	/* Clock setup can vary. Index as enum imx290_clk_index */
+	const struct imx290_regval *clk_data[2];
+	u32 clk_size;
 };
 
 struct imx290 {
 	struct device *dev;
 	struct clk *xclk;
+	u32 xclk_freq;
 	struct regmap *regmap;
 	u8 nlanes;
 	u8 bpp;
@@ -116,8 +130,6 @@ static const struct imx290_regval imx290
 	{ 0x3018, 0x65 },
 	{ 0x3019, 0x04 },
 	{ 0x301a, 0x00 },
-	{ 0x3444, 0x20 },
-	{ 0x3445, 0x25 },
 	{ 0x303a, 0x0c },
 	{ 0x3040, 0x00 },
 	{ 0x3041, 0x00 },
@@ -171,6 +183,30 @@ static const struct imx290_regval imx290
 	{ 0x33b3, 0x04 },
 };
 
+static const struct imx290_regval imx290_37_125mhz_clock_1080p[] = {
+	{ 0x305c, 0x18 },
+	{ 0x305d, 0x03 },
+	{ 0x305e, 0x20 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1a },
+	{ 0x3164, 0x1a },
+	{ 0x3444, 0x20 },
+	{ 0x3445, 0x25 },
+	{ 0x3480, 0x49 },
+};
+
+static const struct imx290_regval imx290_74_250mhz_clock_1080p[] = {
+	{ 0x305c, 0x0c },
+	{ 0x305d, 0x03 },
+	{ 0x305e, 0x10 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1b },
+	{ 0x3164, 0x1b },
+	{ 0x3444, 0x40 },
+	{ 0x3445, 0x4a },
+	{ 0x3480, 0x92 },
+};
+
 static const struct imx290_regval imx290_1080p_settings[] = {
 	/* mode settings */
 	{ 0x3007, 0x00 },
@@ -182,13 +218,6 @@ static const struct imx290_regval imx290
 	{ 0x3419, 0x04 },
 	{ 0x3012, 0x64 },
 	{ 0x3013, 0x00 },
-	{ 0x305c, 0x18 },
-	{ 0x305d, 0x03 },
-	{ 0x305e, 0x20 },
-	{ 0x305f, 0x01 },
-	{ 0x315e, 0x1a },
-	{ 0x3164, 0x1a },
-	{ 0x3480, 0x49 },
 	/* data rate settings */
 	{ 0x3405, 0x10 },
 	{ 0x3446, 0x57 },
@@ -209,6 +238,30 @@ static const struct imx290_regval imx290
 	{ 0x3455, 0x00 },
 };
 
+static const struct imx290_regval imx290_37_125mhz_clock_720p[] = {
+	{ 0x305c, 0x20 },
+	{ 0x305d, 0x00 },
+	{ 0x305e, 0x20 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1a },
+	{ 0x3164, 0x1a },
+	{ 0x3444, 0x20 },
+	{ 0x3445, 0x25 },
+	{ 0x3480, 0x49 },
+};
+
+static const struct imx290_regval imx290_74_250mhz_clock_720p[] = {
+	{ 0x305c, 0x10 },
+	{ 0x305d, 0x00 },
+	{ 0x305e, 0x10 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1b },
+	{ 0x3164, 0x1b },
+	{ 0x3444, 0x40 },
+	{ 0x3445, 0x4a },
+	{ 0x3480, 0x92 },
+};
+
 static const struct imx290_regval imx290_720p_settings[] = {
 	/* mode settings */
 	{ 0x3007, 0x10 },
@@ -220,13 +273,6 @@ static const struct imx290_regval imx290
 	{ 0x3419, 0x02 },
 	{ 0x3012, 0x64 },
 	{ 0x3013, 0x00 },
-	{ 0x305c, 0x20 },
-	{ 0x305d, 0x00 },
-	{ 0x305e, 0x20 },
-	{ 0x305f, 0x01 },
-	{ 0x315e, 0x1a },
-	{ 0x3164, 0x1a },
-	{ 0x3480, 0x49 },
 	/* data rate settings */
 	{ 0x3405, 0x10 },
 	{ 0x3446, 0x4f },
@@ -312,6 +358,11 @@ static const struct imx290_mode imx290_m
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_1080p,
+			[CLK_74_25] = imx290_74_250mhz_clock_1080p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_1080p),
 	},
 	{
 		.width = 1280,
@@ -320,6 +371,11 @@ static const struct imx290_mode imx290_m
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_1080p,
+			[CLK_74_25] = imx290_74_250mhz_clock_1080p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_1080p),
 	},
 };
 
@@ -331,6 +387,11 @@ static const struct imx290_mode imx290_m
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_720p,
+			[CLK_74_25] = imx290_74_250mhz_clock_720p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_720p),
 	},
 	{
 		.width = 1280,
@@ -339,6 +400,11 @@ static const struct imx290_mode imx290_m
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_720p,
+			[CLK_74_25] = imx290_74_250mhz_clock_720p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_720p),
 	},
 };
 
@@ -712,6 +778,8 @@ static int imx290_set_hmax(struct imx290
 /* Start streaming */
 static int imx290_start_streaming(struct imx290 *imx290)
 {
+	enum imx290_clk_index clk_idx = imx290->xclk_freq == 37125000 ?
+					CLK_37_125 : CLK_74_25;
 	int ret;
 
 	/* Set init register settings */
@@ -723,6 +791,14 @@ static int imx290_start_streaming(struct
 		return ret;
 	}
 
+	ret = imx290_set_register_array(imx290,
+					imx290->current_mode->clk_data[clk_idx],
+					imx290->current_mode->clk_size);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set clock registers\n");
+		return ret;
+	}
+
 	/* Apply the register values related to current frame format */
 	ret = imx290_write_current_format(imx290);
 	if (ret < 0) {
@@ -935,7 +1011,6 @@ static int imx290_probe(struct i2c_clien
 		.bus_type = V4L2_MBUS_CSI2_DPHY
 	};
 	struct imx290 *imx290;
-	u32 xclk_freq;
 	s64 fq;
 	int ret;
 
@@ -999,21 +1074,21 @@ static int imx290_probe(struct i2c_clien
 	}
 
 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
-				       &xclk_freq);
+				       &imx290->xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not get xclk frequency\n");
 		goto free_err;
 	}
 
 	/* external clock must be 37.125 MHz */
-	if (xclk_freq != 37125000) {
+	if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) {
 		dev_err(dev, "External clock frequency %u is not supported\n",
-			xclk_freq);
+			imx290->xclk_freq);
 		ret = -EINVAL;
 		goto free_err;
 	}
 
-	ret = clk_set_rate(imx290->xclk, xclk_freq);
+	ret = clk_set_rate(imx290->xclk, imx290->xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not set xclk frequency\n");
 		goto free_err;
