mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones <mj8263788@gmail.com> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			270 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			270 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From c9d976e455b911c977ffeb000b4342cd65c79dbc Mon Sep 17 00:00:00 2001
 | 
						|
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
 | 
						|
Date: Tue, 15 Jun 2021 18:29:52 +0100
 | 
						|
Subject: [PATCH] media: i2c: imx258: Implement HFLIP and VFLIP
 | 
						|
 controls.
 | 
						|
 | 
						|
The sensor supports H & V flips, so implement the relevant controls.
 | 
						|
Note that the Bayer order changes with these flips, therefore
 | 
						|
they set the V4L2_CTRL_FLAG_MODIFY_LAYOUT property.
 | 
						|
 | 
						|
As we now support flips, remove the restriction of the sensor only
 | 
						|
probing if rotated 180 degrees, but do take that value and initialise
 | 
						|
VFLIP and HFLIP based on it.
 | 
						|
 | 
						|
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
 | 
						|
---
 | 
						|
 drivers/media/i2c/imx258.c | 106 +++++++++++++++++++++++++------------
 | 
						|
 1 file changed, 73 insertions(+), 33 deletions(-)
 | 
						|
 | 
						|
--- a/drivers/media/i2c/imx258.c
 | 
						|
+++ b/drivers/media/i2c/imx258.c
 | 
						|
@@ -9,6 +9,7 @@
 | 
						|
 #include <linux/pm_runtime.h>
 | 
						|
 #include <media/v4l2-ctrls.h>
 | 
						|
 #include <media/v4l2-device.h>
 | 
						|
+#include <media/v4l2-fwnode.h>
 | 
						|
 #include <asm/unaligned.h>
 | 
						|
 
 | 
						|
 #define IMX258_REG_VALUE_08BIT		1
 | 
						|
@@ -69,7 +70,8 @@
 | 
						|
 
 | 
						|
 /* Orientation */
 | 
						|
 #define REG_MIRROR_FLIP_CONTROL		0x0101
 | 
						|
-#define REG_CONFIG_MIRROR_FLIP		0x03
 | 
						|
+#define REG_CONFIG_MIRROR_HFLIP		0x01
 | 
						|
+#define REG_CONFIG_MIRROR_VFLIP		0x02
 | 
						|
 #define REG_CONFIG_FLIP_TEST_PATTERN	0x02
 | 
						|
 
 | 
						|
 /* Input clock frequency in Hz */
 | 
						|
@@ -504,6 +506,22 @@ static const struct imx258_reg mode_1048
 | 
						|
 	{ 0x0220, 0x00 },
 | 
						|
 };
 | 
						|
 
 | 
						|
+/*
 | 
						|
+ * The supported formats.
 | 
						|
+ * This table MUST contain 4 entries per format, to cover the various flip
 | 
						|
+ * combinations in the order
 | 
						|
+ * - no flip
 | 
						|
+ * - h flip
 | 
						|
+ * - v flip
 | 
						|
+ * - h&v flips
 | 
						|
+ */
 | 
						|
+static const u32 codes[] = {
 | 
						|
+	/* 10-bit modes. */
 | 
						|
+	MEDIA_BUS_FMT_SRGGB10_1X10,
 | 
						|
+	MEDIA_BUS_FMT_SGRBG10_1X10,
 | 
						|
+	MEDIA_BUS_FMT_SGBRG10_1X10,
 | 
						|
+	MEDIA_BUS_FMT_SBGGR10_1X10
 | 
						|
+};
 | 
						|
 static const char * const imx258_test_pattern_menu[] = {
 | 
						|
 	"Disabled",
 | 
						|
 	"Solid Colour",
 | 
						|
@@ -605,6 +623,8 @@ struct imx258 {
 | 
						|
 	struct v4l2_ctrl *vblank;
 | 
						|
 	struct v4l2_ctrl *hblank;
 | 
						|
 	struct v4l2_ctrl *exposure;
 | 
						|
+	struct v4l2_ctrl *hflip;
 | 
						|
+	struct v4l2_ctrl *vflip;
 | 
						|
 
 | 
						|
 	/* Current mode */
 | 
						|
 	const struct imx258_mode *cur_mode;
 | 
						|
@@ -700,16 +720,29 @@ static int imx258_write_regs(struct imx2
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
+/* Get bayer order based on flip setting. */
 | 
						|
+static u32 imx258_get_format_code(struct imx258 *imx258)
 | 
						|
+{
 | 
						|
+	unsigned int i;
 | 
						|
+
 | 
						|
+	lockdep_assert_held(&imx258->mutex);
 | 
						|
+
 | 
						|
+	i = (imx258->vflip->val ? 2 : 0) |
 | 
						|
+	    (imx258->hflip->val ? 1 : 0);
 | 
						|
+
 | 
						|
+	return codes[i];
 | 
						|
+}
 | 
						|
 /* Open sub-device */
 | 
						|
 static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 | 
						|
 {
 | 
						|
+	struct imx258 *imx258 = to_imx258(sd);
 | 
						|
 	struct v4l2_mbus_framefmt *try_fmt =
 | 
						|
 		v4l2_subdev_get_try_format(sd, fh->state, 0);
 | 
						|
 
 | 
						|
 	/* Initialize try_fmt */
 | 
						|
 	try_fmt->width = supported_modes[0].width;
 | 
						|
 	try_fmt->height = supported_modes[0].height;
 | 
						|
-	try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
 | 
						|
+	try_fmt->code = imx258_get_format_code(imx258);
 | 
						|
 	try_fmt->field = V4L2_FIELD_NONE;
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
@@ -775,10 +808,6 @@ static int imx258_set_ctrl(struct v4l2_c
 | 
						|
 		ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN,
 | 
						|
 				IMX258_REG_VALUE_16BIT,
 | 
						|
 				ctrl->val);
 | 
						|
-		ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
 | 
						|
-				IMX258_REG_VALUE_08BIT,
 | 
						|
-				!ctrl->val ? REG_CONFIG_MIRROR_FLIP :
 | 
						|
-				REG_CONFIG_FLIP_TEST_PATTERN);
 | 
						|
 		break;
 | 
						|
 	case V4L2_CID_WIDE_DYNAMIC_RANGE:
 | 
						|
 		if (!ctrl->val) {
 | 
						|
@@ -796,6 +825,15 @@ static int imx258_set_ctrl(struct v4l2_c
 | 
						|
 					       BIT(IMX258_HDR_RATIO_MAX));
 | 
						|
 		}
 | 
						|
 		break;
 | 
						|
+	case V4L2_CID_VFLIP:
 | 
						|
+	case V4L2_CID_HFLIP:
 | 
						|
+		ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
 | 
						|
+				       IMX258_REG_VALUE_08BIT,
 | 
						|
+				       (imx258->hflip->val ?
 | 
						|
+					REG_CONFIG_MIRROR_HFLIP : 0) |
 | 
						|
+				       (imx258->vflip->val ?
 | 
						|
+					REG_CONFIG_MIRROR_VFLIP : 0));
 | 
						|
+		break;
 | 
						|
 	default:
 | 
						|
 		dev_info(&client->dev,
 | 
						|
 			 "ctrl(id:0x%x,val:0x%x) is not handled\n",
 | 
						|
@@ -817,11 +855,13 @@ static int imx258_enum_mbus_code(struct
 | 
						|
 				  struct v4l2_subdev_state *sd_state,
 | 
						|
 				  struct v4l2_subdev_mbus_code_enum *code)
 | 
						|
 {
 | 
						|
-	/* Only one bayer order(GRBG) is supported */
 | 
						|
+	struct imx258 *imx258 = to_imx258(sd);
 | 
						|
+
 | 
						|
+	/* Only one bayer format (10 bit) is supported */
 | 
						|
 	if (code->index > 0)
 | 
						|
 		return -EINVAL;
 | 
						|
 
 | 
						|
-	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
 | 
						|
+	code->code = imx258_get_format_code(imx258);
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
@@ -830,10 +870,11 @@ static int imx258_enum_frame_size(struct
 | 
						|
 				  struct v4l2_subdev_state *sd_state,
 | 
						|
 				  struct v4l2_subdev_frame_size_enum *fse)
 | 
						|
 {
 | 
						|
+	struct imx258 *imx258 = to_imx258(sd);
 | 
						|
 	if (fse->index >= ARRAY_SIZE(supported_modes))
 | 
						|
 		return -EINVAL;
 | 
						|
 
 | 
						|
-	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
 | 
						|
+	if (fse->code != imx258_get_format_code(imx258))
 | 
						|
 		return -EINVAL;
 | 
						|
 
 | 
						|
 	fse->min_width = supported_modes[fse->index].width;
 | 
						|
@@ -844,12 +885,13 @@ static int imx258_enum_frame_size(struct
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
-static void imx258_update_pad_format(const struct imx258_mode *mode,
 | 
						|
+static void imx258_update_pad_format(struct imx258 *imx258,
 | 
						|
+				     const struct imx258_mode *mode,
 | 
						|
 				     struct v4l2_subdev_format *fmt)
 | 
						|
 {
 | 
						|
 	fmt->format.width = mode->width;
 | 
						|
 	fmt->format.height = mode->height;
 | 
						|
-	fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
 | 
						|
+	fmt->format.code = imx258_get_format_code(imx258);
 | 
						|
 	fmt->format.field = V4L2_FIELD_NONE;
 | 
						|
 }
 | 
						|
 
 | 
						|
@@ -862,7 +904,7 @@ static int __imx258_get_pad_format(struc
 | 
						|
 							  sd_state,
 | 
						|
 							  fmt->pad);
 | 
						|
 	else
 | 
						|
-		imx258_update_pad_format(imx258->cur_mode, fmt);
 | 
						|
+		imx258_update_pad_format(imx258, imx258->cur_mode, fmt);
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
@@ -896,13 +938,12 @@ static int imx258_set_pad_format(struct
 | 
						|
 
 | 
						|
 	mutex_lock(&imx258->mutex);
 | 
						|
 
 | 
						|
-	/* Only one raw bayer(GBRG) order is supported */
 | 
						|
-	fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
 | 
						|
+	fmt->format.code = imx258_get_format_code(imx258);
 | 
						|
 
 | 
						|
 	mode = v4l2_find_nearest_size(supported_modes,
 | 
						|
 		ARRAY_SIZE(supported_modes), width, height,
 | 
						|
 		fmt->format.width, fmt->format.height);
 | 
						|
-	imx258_update_pad_format(mode, fmt);
 | 
						|
+	imx258_update_pad_format(imx258, mode, fmt);
 | 
						|
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
 | 
						|
 		framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
 | 
						|
 		*framefmt = fmt->format;
 | 
						|
@@ -959,15 +1000,6 @@ static int imx258_start_streaming(struct
 | 
						|
 		return ret;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	/* Set Orientation be 180 degree */
 | 
						|
-	ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
 | 
						|
-			       IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP);
 | 
						|
-	if (ret) {
 | 
						|
-		dev_err(&client->dev, "%s failed to set orientation\n",
 | 
						|
-			__func__);
 | 
						|
-		return ret;
 | 
						|
-	}
 | 
						|
-
 | 
						|
 	/* Apply customized values from user */
 | 
						|
 	ret =  __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler);
 | 
						|
 	if (ret)
 | 
						|
@@ -1142,6 +1174,7 @@ static const struct v4l2_subdev_internal
 | 
						|
 static int imx258_init_controls(struct imx258 *imx258)
 | 
						|
 {
 | 
						|
 	struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
 | 
						|
+	struct v4l2_fwnode_device_properties props;
 | 
						|
 	struct v4l2_ctrl_handler *ctrl_hdlr;
 | 
						|
 	s64 vblank_def;
 | 
						|
 	s64 vblank_min;
 | 
						|
@@ -1150,7 +1183,7 @@ static int imx258_init_controls(struct i
 | 
						|
 	int ret;
 | 
						|
 
 | 
						|
 	ctrl_hdlr = &imx258->ctrl_handler;
 | 
						|
-	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
 | 
						|
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
 | 
						|
 	if (ret)
 | 
						|
 		return ret;
 | 
						|
 
 | 
						|
@@ -1219,6 +1252,21 @@ static int imx258_init_controls(struct i
 | 
						|
 				ARRAY_SIZE(imx258_test_pattern_menu) - 1,
 | 
						|
 				0, 0, imx258_test_pattern_menu);
 | 
						|
 
 | 
						|
+	ret = v4l2_fwnode_device_parse(&client->dev, &props);
 | 
						|
+	if (ret)
 | 
						|
+		goto error;
 | 
						|
+
 | 
						|
+	imx258->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
 | 
						|
+					  V4L2_CID_HFLIP, 0, 1, 1,
 | 
						|
+					  props.rotation == 180 ? 1 : 0);
 | 
						|
+	if (imx258->hflip)
 | 
						|
+		imx258->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
 | 
						|
+
 | 
						|
+	imx258->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
 | 
						|
+					  V4L2_CID_VFLIP, 0, 1, 1,
 | 
						|
+					  props.rotation == 180 ? 1 : 0);
 | 
						|
+	if (imx258->vflip)
 | 
						|
+		imx258->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
 | 
						|
 	if (ctrl_hdlr->error) {
 | 
						|
 		ret = ctrl_hdlr->error;
 | 
						|
 		dev_err(&client->dev, "%s control init failed (%d)\n",
 | 
						|
@@ -1270,14 +1318,6 @@ static int imx258_probe(struct i2c_clien
 | 
						|
 		return -EINVAL;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	/*
 | 
						|
-	 * Check that the device is mounted upside down. The driver only
 | 
						|
-	 * supports a single pixel order right now.
 | 
						|
-	 */
 | 
						|
-	ret = device_property_read_u32(&client->dev, "rotation", &val);
 | 
						|
-	if (ret || val != 180)
 | 
						|
-		return -EINVAL;
 | 
						|
-
 | 
						|
 	/* Initialize subdev */
 | 
						|
 	v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
 | 
						|
 
 |