From c9f918319593861c4975b229579def5fbd637ad9 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Thu, 25 Jun 2020 17:03:11 +0100
Subject: [PATCH] media : i2c: imx290: Add support for the mono
 sensor variant.

The IMX290 module is available as either mono or colour (Bayer).

Update the driver so that it can advertise the correct mono
formats instead of the colour ones.

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

--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1,10 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Sony IMX290/327 CMOS Image Sensor Driver
+ * Sony IMX290 & IMX327 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).
+ * IMX327 can support up to 60fps, whilst IMX290 can support up to 120fps, but
+ * only 10bit and when connected over 4 CSI-2 lanes.
+ * The modules don't appear to have a mechanism to identify whether the mono or
+ * colour variant is connected, therefore it is done via compatible string.
  *
  * Copyright (C) 2019 FRAMOS GmbH.
  *
@@ -17,6 +19,7 @@
 #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>
@@ -95,6 +98,8 @@ struct imx290 {
 	u8 bpp;
 	u16 hmax_min;
 
+	const struct imx290_pixfmt *formats;
+
 	struct v4l2_subdev sd;
 	struct media_pad pad;
 	struct v4l2_mbus_framefmt current_format;
@@ -120,11 +125,18 @@ struct imx290_pixfmt {
 	u8 bpp;
 };
 
-static const struct imx290_pixfmt imx290_formats[] = {
+#define IMX290_NUM_FORMATS 2
+
+static const struct imx290_pixfmt imx290_colour_formats[IMX290_NUM_FORMATS] = {
 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
 };
 
+static const struct imx290_pixfmt imx290_mono_formats[IMX290_NUM_FORMATS] = {
+	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
+	{ MEDIA_BUS_FMT_Y12_1X12, 12 },
+};
+
 static const struct regmap_config imx290_regmap_config = {
 	.reg_bits = 16,
 	.val_bits = 8,
@@ -671,10 +683,12 @@ static int imx290_enum_mbus_code(struct
 				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
-	if (code->index >= ARRAY_SIZE(imx290_formats))
+	const struct imx290 *imx290 = to_imx290(sd);
+
+	if (code->index >= IMX290_NUM_FORMATS)
 		return -EINVAL;
 
-	code->code = imx290_formats[code->index].code;
+	code->code = imx290->formats[code->index].code;
 
 	return 0;
 }
@@ -686,8 +700,8 @@ static int imx290_enum_frame_size(struct
 	const struct imx290 *imx290 = to_imx290(sd);
 	const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
 
-	if ((fse->code != imx290_formats[0].code) &&
-	    (fse->code != imx290_formats[1].code))
+	if (fse->code != imx290->formats[0].code &&
+	    fse->code != imx290->formats[1].code)
 		return -EINVAL;
 
 	if (fse->index >= imx290_modes_num(imx290))
@@ -765,14 +779,14 @@ static int imx290_set_fmt(struct v4l2_su
 	fmt->format.width = mode->width;
 	fmt->format.height = mode->height;
 
-	for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
-		if (imx290_formats[i].code == fmt->format.code)
+	for (i = 0; i < IMX290_NUM_FORMATS; i++)
+		if (imx290->formats[i].code == fmt->format.code)
 			break;
 
-	if (i >= ARRAY_SIZE(imx290_formats))
+	if (i >= IMX290_NUM_FORMATS)
 		i = 0;
 
-	fmt->format.code = imx290_formats[i].code;
+	fmt->format.code = imx290->formats[i].code;
 	fmt->format.field = V4L2_FIELD_NONE;
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
@@ -780,7 +794,7 @@ static int imx290_set_fmt(struct v4l2_su
 	} else {
 		format = &imx290->current_format;
 		imx290->current_mode = mode;
-		imx290->bpp = imx290_formats[i].bpp;
+		imx290->bpp = imx290->formats[i].bpp;
 
 		if (imx290->link_freq)
 			__v4l2_ctrl_s_ctrl(imx290->link_freq,
@@ -835,6 +849,7 @@ static int imx290_write_current_format(s
 
 	switch (imx290->current_format.code) {
 	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_Y10_1X10:
 		ret = imx290_set_register_array(imx290, imx290_10bit_settings,
 						ARRAY_SIZE(
 							imx290_10bit_settings));
@@ -844,6 +859,7 @@ static int imx290_write_current_format(s
 		}
 		break;
 	case MEDIA_BUS_FMT_SRGGB12_1X12:
+	case MEDIA_BUS_FMT_Y12_1X12:
 		ret = imx290_set_register_array(imx290, imx290_12bit_settings,
 						ARRAY_SIZE(
 							imx290_12bit_settings));
@@ -1091,6 +1107,12 @@ static s64 imx290_check_link_freqs(const
 	return 0;
 }
 
+static const struct of_device_id imx290_of_match[] = {
+	{ .compatible = "sony,imx290", .data = imx290_colour_formats },
+	{ .compatible = "sony,imx290-mono", .data = imx290_mono_formats },
+	{ /* sentinel */ }
+};
+
 static int imx290_probe(struct i2c_client *client)
 {
 	struct device *dev = &client->dev;
@@ -1099,6 +1121,7 @@ static int imx290_probe(struct i2c_clien
 	struct v4l2_fwnode_endpoint ep = {
 		.bus_type = V4L2_MBUS_CSI2_DPHY
 	};
+	const struct of_device_id *match;
 	const struct imx290_mode *mode;
 	struct imx290 *imx290;
 	s64 fq;
@@ -1115,6 +1138,11 @@ static int imx290_probe(struct i2c_clien
 		return -ENODEV;
 	}
 
+	match = of_match_device(imx290_of_match, dev);
+	if (!match)
+		return -ENODEV;
+	imx290->formats = (const struct imx290_pixfmt *)match->data;
+
 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
 	if (!endpoint) {
 		dev_err(dev, "Endpoint node not found\n");
@@ -1330,10 +1358,6 @@ static int imx290_remove(struct i2c_clie
 	return 0;
 }
 
-static const struct of_device_id imx290_of_match[] = {
-	{ .compatible = "sony,imx290" },
-	{ /* sentinel */ }
-};
 MODULE_DEVICE_TABLE(of, imx290_of_match);
 
 static struct i2c_driver imx290_i2c_driver = {
