From nobody Thu Dec 18 11:44:48 2025 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 75A6432C95C; Mon, 15 Dec 2025 07:32:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.167.242.64 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765783936; cv=none; b=MOsiqYIhemScNGXdR6WdIbCpjsAQZO7hblYAkibWls5alSxAsXXmZ20ezBfItlYfwr+JjeqjG4sn414cSQRab2HB+QzDnFgLcqBYhEZ4YW2k95ly3tSxOJNsrz7ONBipgp0mfEfI/mv0Zna/cm1mtDbSJHUwZid6FHFLWD41GTw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765783936; c=relaxed/simple; bh=klMUtdyTu+St9Mah4o1XXKQLWF3jzg4MYFUbu5CB4fY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=g35xdj5PAOtjoEejPqEdYnQDaf5TvrYZCD2bIz45Uli32ivbGr3w3nFlWpxfq5N+oadOkoozfysi9KDHfCwADTdoFXj4XqwZDn5RNjXeqqFsYYafwj/AVAtR5sWSEosuzpR4R1qTKw5KjtSk9erVGn/z+JHFlNgAVIRHTSDUI6A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ideasonboard.com; spf=pass smtp.mailfrom=ideasonboard.com; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b=cCU5JEZF; arc=none smtp.client-ip=213.167.242.64 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ideasonboard.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ideasonboard.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="cCU5JEZF" Received: from mail.ideasonboard.com (unknown [223.190.82.135]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D4702EB4; Mon, 15 Dec 2025 08:32:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765783928; bh=klMUtdyTu+St9Mah4o1XXKQLWF3jzg4MYFUbu5CB4fY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=cCU5JEZFrqrIiQdlzwx1u2FXuv1UeQN21SVA2eIYlPI5Q/u5IGnmqm17tmCLFeXxS zhh4KW6cvfhqyeu2a38lzFr4Fj4Sv7IJX7MPlC8GpqmT4arZCW5x/mZMOtXCT7cg1k ojppuuQUuvEjHuuN9k7jzC15mz3I2uYRA+pDLgt8= From: Jai Luthra Date: Mon, 15 Dec 2025 12:58:05 +0530 Subject: [PATCH v3 09/16] media: i2c: ov5647: Support HFLIP and VFLIP Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251215-b4-rpi-ov5647-v3-9-4c25e3c883b2@ideasonboard.com> References: <20251215-b4-rpi-ov5647-v3-0-4c25e3c883b2@ideasonboard.com> In-Reply-To: <20251215-b4-rpi-ov5647-v3-0-4c25e3c883b2@ideasonboard.com> To: Sakari Ailus , Dave Stevenson , Jacopo Mondi , Mauro Carvalho Chehab , Naushir Patuck , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, Mauro Carvalho Chehab , Kieran Bingham , David Plowman , Laurent Pinchart , Peter Robinson , Stefan Wahren , "Ivan T. Ivanov" , Jacopo Mondi , Jai Luthra X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=6914; i=jai.luthra@ideasonboard.com; h=from:subject:message-id; bh=lR4Al1EI2luJCAQV8ZaeuTd88Dge1UeeByvXETR961M=; b=owEBbQKS/ZANAwAKAUPekfkkmnFFAcsmYgBpP7jfCTvrOlNteiMibtxAW1KfOLCi5x6fiUycG m1NV6c+L0yJAjMEAAEKAB0WIQRN4NgY5dV16NRar8VD3pH5JJpxRQUCaT+43wAKCRBD3pH5JJpx RbR+D/wNFC2cAUVcwBxtJk5ax43oi3ItNwh527qmg1J5yihGB6XQ8y07WJxpWeIMtEGWncobZQG ZkCP3mDBJzV61q9iXpWOXPf9oF4j9ptBOpu3K3YF0UA3YcD2c9bsa7/biL/+l8mSjQA2PsmDhri amTEoXe9l0SADTW7kM0jlOleoB6yMDzLo4++SHSL1gWKTgLSBaoNt/L8ZJv9r1TK35dLfuu39Gz 8Ilxx+sd9WEw8ZQ/i7aU1hADottYW1MhSofn7QJH1fsJ0PUvXoHyVrfBJp7EIJuHGRTnyF7yWx8 kjj2+1LXsbumnK7hWymwc4FjmT5oLq7BeS6ONibNxS1584DvwX4jrXKSRzE/5RsC86svn903vBH yJ0V/dk3r8/BGLCSt8OBoojLWbiA+Wum5T64X+4FYzELe848/I9hJRbAFVPhq38RBOm85AnFJtO /qFqXlji2Fay0HW8i0tHCCmooji7QX0PqvoPX95oILqhCriOZcp8MTkjPYGtqbolr2trz2W2EWE n8KRdT/2t7e2nqNAFo4R8lO1MLtG9tRRkFsBPnQHfuIrOtb5y0DL9wzMSLbR4ruG9ravp6pgxdw hYORA2Td7mmdJTDXwtAYyCv2SjuQjrvI9BaWRTwpvf5RC7uec7bg1DZiw73hDLHLXsuZ4YIl1QC N8Jn0kOeHXLmSIg== X-Developer-Key: i=jai.luthra@ideasonboard.com; a=openpgp; fpr=4DE0D818E5D575E8D45AAFC543DE91F9249A7145 From: David Plowman Add missing controls for horizontal and vertical flipping. The sensor readout mirrors in the horizontal direction by default (if 0x3821[1] =3D 0) which can make things unnecessarily difficult for applications. The register table prior to this commit was setting that bit explicitly, to achieve a normally oriented image. Now that we have userspace controls for HFLIP, we keep the convention and report the non-mirrored image (with 0x3821[1] =3D 1) as horizontal_flip=3D0, and vice versa. Signed-off-by: David Plowman Reviewed-by: Jacopo Mondi Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra --- drivers/media/i2c/ov5647.c | 81 ++++++++++++++++++++++++++++++++++++++++++= ---- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 5009fd8c05a64d7e06f66f8f75f0a881cd0b95c1..07411494e32446d921b80adda52= d66926fc07645 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -55,6 +55,8 @@ #define OV5647_REG_GAIN_LO 0x350b #define OV5647_REG_VTS_HI 0x380e #define OV5647_REG_VTS_LO 0x380f +#define OV5647_REG_TIMING_TC_V 0x3820 +#define OV5647_REG_TIMING_TC_H 0x3821 #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 @@ -120,6 +122,8 @@ struct ov5647 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; }; =20 static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -161,7 +165,7 @@ static struct regval_list ov5647_2592x1944_10bpp[] =3D { {0x3036, 0x69}, {0x303c, 0x11}, {0x3106, 0xf5}, - {0x3821, 0x06}, + {0x3821, 0x02}, {0x3820, 0x00}, {0x3827, 0xec}, {0x370c, 0x03}, @@ -250,7 +254,7 @@ static struct regval_list ov5647_1080p30_10bpp[] =3D { {0x3036, 0x62}, {0x303c, 0x11}, {0x3106, 0xf5}, - {0x3821, 0x06}, + {0x3821, 0x02}, {0x3820, 0x00}, {0x3827, 0xec}, {0x370c, 0x03}, @@ -414,7 +418,7 @@ static struct regval_list ov5647_2x2binned_10bpp[] =3D { {0x4800, 0x24}, {0x3503, 0x03}, {0x3820, 0x41}, - {0x3821, 0x07}, + {0x3821, 0x03}, {0x350a, 0x00}, {0x350b, 0x10}, {0x3500, 0x00}, @@ -430,7 +434,7 @@ static struct regval_list ov5647_640x480_10bpp[] =3D { {0x3035, 0x11}, {0x3036, 0x46}, {0x303c, 0x11}, - {0x3821, 0x07}, + {0x3821, 0x03}, {0x3820, 0x41}, {0x370c, 0x03}, {0x3612, 0x59}, @@ -956,6 +960,26 @@ static const struct v4l2_subdev_video_ops ov5647_subde= v_video_ops =3D { .s_stream =3D ov5647_s_stream, }; =20 +/* + * This function returns the mbus code for the current settings of the HFL= IP + * and VFLIP controls. + */ +static u32 ov5647_get_mbus_code(struct v4l2_subdev *sd) +{ + struct ov5647 *sensor =3D to_sensor(sd); + /* The control values are only 0 or 1. */ + int index =3D sensor->hflip->val | (sensor->vflip->val << 1); + + static const u32 codes[4] =3D { + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10 + }; + + return codes[index]; +} + static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -963,7 +987,7 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; =20 - code->code =3D MEDIA_BUS_FMT_SBGGR10_1X10; + code->code =3D ov5647_get_mbus_code(sd); =20 return 0; } @@ -974,7 +998,7 @@ static int ov5647_enum_frame_size(struct v4l2_subdev *s= d, { const struct v4l2_mbus_framefmt *fmt; =20 - if (fse->code !=3D MEDIA_BUS_FMT_SBGGR10_1X10 || + if (fse->code !=3D ov5647_get_mbus_code(sd) || fse->index >=3D ARRAY_SIZE(ov5647_modes)) return -EINVAL; =20 @@ -1007,6 +1031,8 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, } =20 *fmt =3D *sensor_format; + /* The code we pass back must reflect the current h/vflips. */ + fmt->code =3D ov5647_get_mbus_code(sd); mutex_unlock(&sensor->lock); =20 return 0; @@ -1054,6 +1080,8 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, exposure_def); } *fmt =3D mode->format; + /* The code we pass back must reflect the current h/vflips. */ + fmt->code =3D ov5647_get_mbus_code(sd); mutex_unlock(&sensor->lock); =20 return 0; @@ -1229,6 +1257,31 @@ static int ov5647_s_exposure(struct v4l2_subdev *sd,= u32 val) return ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4); } =20 +static int ov5647_s_flip(struct v4l2_subdev *sd, u16 reg, u32 ctrl_val) +{ + u8 reg_val; + int ret; + + /* + * TIMING TC REG20 (Vertical) and REG21 (Horizontal): + * - [2]: ISP mirror/flip + * - [1]: Sensor mirror/flip + * + * We only use sensor flip. + * + * Using ISP flip retains the BGGR pattern at the cost of changing the + * pixel array readout. This affects the selection rectangles in ways + * that are not very well documented, and would be tougher to deal with + * for applications compared to reading a different bayer pattern. + */ + ret =3D ov5647_read(sd, reg, ®_val); + if (ret) + return ret; + + return ov5647_write(sd, reg, ctrl_val ? reg_val | BIT(1) + : reg_val & ~BIT(1)); +} + static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov5647 *sensor =3D container_of(ctrl->handler, @@ -1291,6 +1344,14 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) /* Read-only, but we adjust it based on mode. */ break; =20 + case V4L2_CID_HFLIP: + /* There's an in-built hflip in the sensor, so account for that here. */ + ret =3D ov5647_s_flip(sd, OV5647_REG_TIMING_TC_H, !ctrl->val); + break; + case V4L2_CID_VFLIP: + ret =3D ov5647_s_flip(sd, OV5647_REG_TIMING_TC_V, ctrl->val); + break; + default: dev_info(&client->dev, "Control (id:0x%x, val:0x%x) not supported\n", @@ -1324,7 +1385,7 @@ static int ov5647_init_controls(struct ov5647 *sensor) int hblank, exposure_max, exposure_def; struct device *dev =3D &client->dev; =20 - v4l2_ctrl_handler_init(&sensor->ctrls, 11); + v4l2_ctrl_handler_init(&sensor->ctrls, 13); =20 v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 0); @@ -1373,6 +1434,12 @@ static int ov5647_init_controls(struct ov5647 *senso= r) ARRAY_SIZE(ov5647_test_pattern_menu) - 1, 0, 0, ov5647_test_pattern_menu); =20 + sensor->hflip =3D v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + sensor->vflip =3D v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_fwnode_device_parse(dev, &props); =20 v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &ov5647_ctrl_ops, --=20 2.52.0