All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC 0/2] media: TPG with internal pads
@ 2025-01-09 10:15 Tomi Valkeinen
  2025-01-09 10:15 ` [PATCH RFC 1/2] media: mc: Add INTERNAL pad flag Tomi Valkeinen
  2025-01-09 10:15 ` [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support Tomi Valkeinen
  0 siblings, 2 replies; 5+ messages in thread
From: Tomi Valkeinen @ 2025-01-09 10:15 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Laurent Pinchart,
	Hans Verkuil
  Cc: linux-media, linux-kernel, Jai Luthra, Tomi Valkeinen

Hi,

This is an RFC that shows how to add TPG support using the
(non-upstream) internal pads. I'm posting this to get some more material
around the internal pads to help the discussions.

 Tomi

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
Sakari Ailus (1):
      media: mc: Add INTERNAL pad flag

Tomi Valkeinen (1):
      media: i2c: ds90ub953: Add TPG support

 .../userspace-api/media/mediactl/media-types.rst   |   8 +
 drivers/media/i2c/ds90ub953.c                      | 498 +++++++++++++++++++--
 drivers/media/mc/mc-entity.c                       |  10 +-
 include/uapi/linux/media.h                         |   1 +
 4 files changed, 466 insertions(+), 51 deletions(-)
---
base-commit: 40ed9e9b2808beeb835bd0ed971fb364c285d39c
change-id: 20250109-ub953-tpg-6472fcc3ff5b

Best regards,
-- 
Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH RFC 1/2] media: mc: Add INTERNAL pad flag
  2025-01-09 10:15 [PATCH RFC 0/2] media: TPG with internal pads Tomi Valkeinen
@ 2025-01-09 10:15 ` Tomi Valkeinen
  2025-01-09 10:15 ` [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support Tomi Valkeinen
  1 sibling, 0 replies; 5+ messages in thread
From: Tomi Valkeinen @ 2025-01-09 10:15 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Laurent Pinchart,
	Hans Verkuil
  Cc: linux-media, linux-kernel, Jai Luthra, Tomi Valkeinen

From: Sakari Ailus <sakari.ailus@linux.intel.com>

Internal source pads will be used as routing endpoints in V4L2
[GS]_ROUTING IOCTLs, to indicate that the stream begins in the entity.
Internal source pads are pads that have both SINK and INTERNAL flags set.

Also prevent creating links to pads that have been flagged as internal and
initialising SOURCE pads with INTERNAL flag set.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 Documentation/userspace-api/media/mediactl/media-types.rst |  8 ++++++++
 drivers/media/mc/mc-entity.c                               | 10 ++++++++--
 include/uapi/linux/media.h                                 |  1 +
 3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst
index 6332e8395263..f55ef055bcf8 100644
--- a/Documentation/userspace-api/media/mediactl/media-types.rst
+++ b/Documentation/userspace-api/media/mediactl/media-types.rst
@@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements
 .. _MEDIA-PAD-FL-SINK:
 .. _MEDIA-PAD-FL-SOURCE:
 .. _MEDIA-PAD-FL-MUST-CONNECT:
+.. _MEDIA-PAD-FL-INTERNAL:
 
 .. flat-table:: Media pad flags
     :header-rows:  0
@@ -381,6 +382,13 @@ Types and flags used to represent the media graph elements
 	  enabled links even when this flag isn't set; the absence of the flag
 	  doesn't imply there is none.
 
+    *  -  ``MEDIA_PAD_FL_INTERNAL``
+       -  The internal flag indicates an internal pad that has no external
+	  connections. Such a pad shall not be connected with a link.
+
+	  The internal flag may currently be present only in a source pad where
+	  it indicates that the :ref:``stream <media-glossary-stream>``
+	  originates from within the entity.
 
 One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE``
 must be set for every pad.
diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
index 045590905582..d1feacc60807 100644
--- a/drivers/media/mc/mc-entity.c
+++ b/drivers/media/mc/mc-entity.c
@@ -213,7 +213,9 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
 		iter->index = i++;
 
 		if (hweight32(iter->flags & (MEDIA_PAD_FL_SINK |
-					     MEDIA_PAD_FL_SOURCE)) != 1) {
+					     MEDIA_PAD_FL_SOURCE)) != 1 ||
+		    (iter->flags & MEDIA_PAD_FL_INTERNAL &&
+		     !(iter->flags & MEDIA_PAD_FL_SINK))) {
 			ret = -EINVAL;
 			break;
 		}
@@ -1118,7 +1120,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type,
 
 	for (i = 0; i < entity->num_pads; i++) {
 		if ((entity->pads[i].flags &
-		     (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type)
+		     (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE |
+		      MEDIA_PAD_FL_INTERNAL)) != pad_type)
 			continue;
 
 		if (entity->pads[i].sig_type == sig_type)
@@ -1148,6 +1151,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad,
 		return -EINVAL;
 	if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK)))
 		return -EINVAL;
+	if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL) ||
+	    WARN_ON(sink->pads[sink_pad].flags & MEDIA_PAD_FL_INTERNAL))
+		return -EINVAL;
 
 	link = media_add_link(&source->links);
 	if (link == NULL)
diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index 1c80b1d6bbaf..80cfd12a43fc 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -208,6 +208,7 @@ struct media_entity_desc {
 #define MEDIA_PAD_FL_SINK			(1U << 0)
 #define MEDIA_PAD_FL_SOURCE			(1U << 1)
 #define MEDIA_PAD_FL_MUST_CONNECT		(1U << 2)
+#define MEDIA_PAD_FL_INTERNAL			(1U << 3)
 
 struct media_pad_desc {
 	__u32 entity;		/* entity ID */

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support
  2025-01-09 10:15 [PATCH RFC 0/2] media: TPG with internal pads Tomi Valkeinen
  2025-01-09 10:15 ` [PATCH RFC 1/2] media: mc: Add INTERNAL pad flag Tomi Valkeinen
@ 2025-01-09 10:15 ` Tomi Valkeinen
  2025-01-10 10:37   ` kernel test robot
  2025-01-10 12:39   ` kernel test robot
  1 sibling, 2 replies; 5+ messages in thread
From: Tomi Valkeinen @ 2025-01-09 10:15 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Laurent Pinchart,
	Hans Verkuil
  Cc: linux-media, linux-kernel, Jai Luthra, Tomi Valkeinen

Add TPG support using an internal pad.

The TPG can be enabled, when streaming is off, by setting a routing
containing a single stream from the internal TPG pad to the source pad.

The frame size, format (only RGB888 for now) and frame interval can be
configured for the stream in the internal pad. The TPG pattern can be
changed via v4l2 control even when the streaming is enabled.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
 drivers/media/i2c/ds90ub953.c | 498 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 449 insertions(+), 49 deletions(-)

diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index 028892cfa361..adaa8bd92920 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -21,8 +21,10 @@
 #include <linux/property.h>
 #include <linux/rational.h>
 #include <linux/regmap.h>
+#include <linux/units.h>
 
 #include <media/i2c/ds90ub9xx.h>
+#include <media/mipi-csi2.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-mediabus.h>
@@ -30,6 +32,8 @@
 
 #define UB953_PAD_SINK			0
 #define UB953_PAD_SOURCE		1
+#define UB953_PAD_TPG			2
+#define UB953_NUM_PADS			3
 
 #define UB953_NUM_GPIOS			4
 
@@ -102,7 +106,12 @@
 
 #define UB953_IND_PGEN_CTL			0x01
 #define UB953_IND_PGEN_CTL_PGEN_ENABLE		BIT(0)
+
 #define UB953_IND_PGEN_CFG			0x02
+#define UB953_IND_PGEN_CFG_FIXED_COLOR_MODE	BIT(7)
+#define UB953_IND_PGEN_CFG_NUM_CBARS_MASK	GENMASK(5, 4)
+#define UB953_IND_PGEN_CFG_BLOCK_SIZE_MASK	GENMASK(3, 0)
+
 #define UB953_IND_PGEN_CSI_DI			0x03
 #define UB953_IND_PGEN_LINE_SIZE1		0x04
 #define UB953_IND_PGEN_LINE_SIZE0		0x05
@@ -130,6 +139,12 @@ enum ub953_mode {
 	UB953_MODE_DVP,
 };
 
+enum ub953_tpg_pattern {
+	UB953_TPG_RED,
+	UB953_TPG_GREEN,
+	UB953_TPG_BLUE,
+};
+
 struct ub953_hw_data {
 	const char *model;
 	bool is_ub971;
@@ -155,7 +170,7 @@ struct ub953_data {
 	struct gpio_chip	gpio_chip;
 
 	struct v4l2_subdev	sd;
-	struct media_pad	pads[2];
+	struct media_pad	pads[UB953_NUM_PADS];
 
 	struct v4l2_async_notifier	notifier;
 
@@ -174,8 +189,38 @@ struct ub953_data {
 	enum ub953_mode		mode;
 
 	const struct ds90ub9xx_platform_data	*plat_data;
+
+	struct v4l2_ctrl_handler	hdl;
+	struct v4l2_ctrl	*tpg_ctrl;
+};
+
+struct ub953_format_info {
+	u32 code;
+	u8 dt;
+	u8 bitspp;
+	u8 block_size;
 };
 
+static const struct ub953_format_info ub953_tpg_formats[] = {
+	/* Only RGB888 supported for now */
+	{
+		.code = MEDIA_BUS_FMT_RGB888_1X24,
+		.dt = MIPI_CSI2_DT_RGB888,
+		.bitspp = 24,
+		.block_size = 3,
+	},
+};
+
+static const struct ub953_format_info *ub953_find_format(u32 code)
+{
+	for (unsigned int i = 0; i < ARRAY_SIZE(ub953_tpg_formats); i++) {
+		if (ub953_tpg_formats[i].code == code)
+			return &ub953_tpg_formats[i];
+	}
+
+	return NULL;
+}
+
 static inline struct ub953_data *sd_to_ub953(struct v4l2_subdev *sd)
 {
 	return container_of(sd, struct ub953_data, sd);
@@ -311,6 +356,38 @@ static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val)
 	return ret;
 }
 
+static int ub953_write_ind16(struct ub953_data *priv, u8 block, u8 reg, u16 val)
+{
+	int ret;
+
+	mutex_lock(&priv->reg_lock);
+
+	ret = ub953_select_ind_reg_block(priv, block);
+	if (ret)
+		goto out;
+
+	ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg);
+	if (ret)
+		goto out;
+
+	ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_DATA, val >> 8);
+	if (ret)
+		goto out;
+
+	ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg + 1);
+	if (ret)
+		goto out;
+
+	ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_DATA, val & 0xff);
+	if (ret)
+		goto out;
+
+out:
+	mutex_unlock(&priv->reg_lock);
+
+	return ret;
+}
+
 /*
  * GPIO chip
  */
@@ -440,67 +517,151 @@ static void ub953_gpiochip_remove(struct ub953_data *priv)
  * V4L2
  */
 
-static int _ub953_set_routing(struct v4l2_subdev *sd,
-			      struct v4l2_subdev_state *state,
-			      struct v4l2_subdev_krouting *routing)
+static bool ub953_is_tpg_selected(struct v4l2_subdev_state *state)
+{
+	return state->routing.num_routes == 1 &&
+	       state->routing.routes[0].sink_pad == UB953_PAD_TPG;
+}
+
+static int ub953_set_routing_tpg(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *state,
+				 const struct v4l2_subdev_krouting *routing)
 {
 	static const struct v4l2_mbus_framefmt format = {
 		.width = 640,
 		.height = 480,
-		.code = MEDIA_BUS_FMT_UYVY8_1X16,
+		.code = MEDIA_BUS_FMT_RGB888_1X24,
 		.field = V4L2_FIELD_NONE,
 		.colorspace = V4L2_COLORSPACE_SRGB,
-		.ycbcr_enc = V4L2_YCBCR_ENC_601,
-		.quantization = V4L2_QUANTIZATION_LIM_RANGE,
+		.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+		.quantization = V4L2_QUANTIZATION_FULL_RANGE,
 		.xfer_func = V4L2_XFER_FUNC_SRGB,
 	};
+
+	const struct v4l2_subdev_route *route;
+	struct v4l2_fract *ival;
 	int ret;
 
-	/*
-	 * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
-	 * frame desc is made dynamically allocated.
-	 */
+	/* Only a single stream allowed for TPG */
+	if (routing->num_routes != 1)
+		return -EINVAL;
 
-	if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
+	route = &routing->routes[0];
+
+	/* The route must be active */
+	if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
 		return -EINVAL;
 
-	ret = v4l2_subdev_routing_validate(sd, routing,
-					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
-	if (ret)
-		return ret;
+	/* Stream ID must be 0 */
+	if (route->sink_stream != 0)
+		return -EINVAL;
 
 	ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
 	if (ret)
 		return ret;
 
+	ival = v4l2_subdev_state_get_interval(state, UB953_PAD_TPG, 0);
+	if (!ival)
+		return -EINVAL;
+
+	ival->numerator = 1;
+	ival->denominator = 30;
+
 	return 0;
 }
 
+static int ub953_set_routing_normal(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_state *state,
+				    const struct v4l2_subdev_krouting *routing)
+{
+	static const struct v4l2_mbus_framefmt format = {
+		.width = 640,
+		.height = 480,
+		.code = MEDIA_BUS_FMT_UYVY8_1X16,
+		.field = V4L2_FIELD_NONE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.ycbcr_enc = V4L2_YCBCR_ENC_601,
+		.quantization = V4L2_QUANTIZATION_LIM_RANGE,
+		.xfer_func = V4L2_XFER_FUNC_SRGB,
+	};
+
+	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+}
+
 static int ub953_set_routing(struct v4l2_subdev *sd,
 			     struct v4l2_subdev_state *state,
 			     enum v4l2_subdev_format_whence which,
 			     struct v4l2_subdev_krouting *routing)
 {
 	struct ub953_data *priv = sd_to_ub953(sd);
+	int ret;
 
 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams)
 		return -EBUSY;
 
-	return _ub953_set_routing(sd, state, routing);
+	/*
+	 * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
+	 * frame desc is made dynamically allocated.
+	 */
+
+	if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
+		return -EINVAL;
+
+	ret = v4l2_subdev_routing_validate(sd, routing,
+					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+					   V4L2_SUBDEV_ROUTING_NO_STREAM_MIX);
+	if (ret)
+		return ret;
+
+	if (routing->num_routes > 0 &&
+	    routing->routes[0].sink_pad == UB953_PAD_TPG)
+		return ub953_set_routing_tpg(sd, state, routing);
+	else
+		return ub953_set_routing_normal(sd, state, routing);
 }
 
-static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
-				struct v4l2_mbus_frame_desc *fd)
+static int ub953_get_frame_desc_tpg(struct v4l2_subdev *sd, unsigned int pad,
+				    struct v4l2_mbus_frame_desc *fd,
+				    struct v4l2_subdev_state *state)
+{
+	const struct ub953_format_info *fmt_info;
+	struct v4l2_mbus_framefmt *fmt;
+	struct v4l2_subdev_route *route;
+
+	fmt = v4l2_subdev_state_get_format(state, UB953_PAD_TPG, 0);
+	if (!fmt)
+		return -EINVAL;
+
+	fmt_info = ub953_find_format(fmt->code);
+	if (!fmt_info)
+		return -EINVAL;
+
+	/* There is exactly one route for TPG */
+	route = &state->routing.routes[0];
+
+	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+	fd->entry[fd->num_entries].stream = route->source_stream;
+	fd->entry[fd->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
+	fd->entry[fd->num_entries].length =
+		fmt->width * fmt->height * fmt_info->bitspp / 8;
+	fd->entry[fd->num_entries].pixelcode = fmt->code;
+	fd->entry[fd->num_entries].bus.csi2.vc = 0;
+	fd->entry[fd->num_entries].bus.csi2.dt = fmt_info->dt;
+
+	fd->num_entries++;
+
+	return 0;
+}
+
+static int ub953_get_frame_desc_normal(struct v4l2_subdev *sd, unsigned int pad,
+				       struct v4l2_mbus_frame_desc *fd,
+				       struct v4l2_subdev_state *state)
 {
 	struct ub953_data *priv = sd_to_ub953(sd);
 	struct v4l2_mbus_frame_desc source_fd;
 	struct v4l2_subdev_route *route;
-	struct v4l2_subdev_state *state;
 	int ret;
 
-	if (pad != UB953_PAD_SOURCE)
-		return -EINVAL;
-
 	ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc,
 			       priv->source_sd_pad, &source_fd);
 	if (ret)
@@ -508,8 +669,6 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
 
 	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
 
-	state = v4l2_subdev_lock_and_get_active_state(sd);
-
 	for_each_active_route(&state->routing, route) {
 		struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
 		unsigned int i;
@@ -527,8 +686,7 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
 		if (!source_entry) {
 			dev_err(&priv->client->dev,
 				"Failed to find stream from source frame desc\n");
-			ret = -EPIPE;
-			goto out_unlock;
+			return -EPIPE;
 		}
 
 		fd->entry[fd->num_entries].stream = route->source_stream;
@@ -543,7 +701,27 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
 		fd->num_entries++;
 	}
 
-out_unlock:
+	return 0;
+}
+
+static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+				struct v4l2_mbus_frame_desc *fd)
+{
+	struct v4l2_subdev_state *state;
+	int ret;
+
+	if (pad != UB953_PAD_SOURCE)
+		return -EINVAL;
+
+	state = v4l2_subdev_lock_and_get_active_state(sd);
+	if (!state)
+		return -EINVAL;
+
+	if (ub953_is_tpg_selected(state))
+		ret = ub953_get_frame_desc_tpg(sd, pad, fd, state);
+	else
+		ret = ub953_get_frame_desc_normal(sd, pad, fd, state);
+
 	v4l2_subdev_unlock_state(state);
 
 	return ret;
@@ -564,6 +742,11 @@ static int ub953_set_fmt(struct v4l2_subdev *sd,
 	if (format->pad == UB953_PAD_SOURCE)
 		return v4l2_subdev_get_fmt(sd, state, format);
 
+	if (ub953_is_tpg_selected(state)) {
+		if (!ub953_find_format(format->format.code))
+			format->format.code = ub953_tpg_formats[0].code;
+	}
+
 	/* Set sink format */
 	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
 	if (!fmt)
@@ -582,6 +765,42 @@ static int ub953_set_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int ub953_get_frame_interval(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_state *state,
+				    struct v4l2_subdev_frame_interval *fi)
+{
+	struct v4l2_fract *ival;
+
+	if (fi->pad != UB953_PAD_TPG)
+		return -ENOTTY;
+
+	ival = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream);
+	if (!ival)
+		return -EINVAL;
+
+	fi->interval = *ival;
+
+	return 0;
+}
+
+static int ub953_set_frame_interval(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_state *state,
+				    struct v4l2_subdev_frame_interval *fi)
+{
+	struct v4l2_fract *ival;
+
+	if (fi->pad != UB953_PAD_TPG)
+		return -ENOTTY;
+
+	ival = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream);
+	if (!ival)
+		return -EINVAL;
+
+	*ival = fi->interval;
+
+	return 0;
+}
+
 static int ub953_init_state(struct v4l2_subdev *sd,
 			    struct v4l2_subdev_state *state)
 {
@@ -595,12 +814,12 @@ static int ub953_init_state(struct v4l2_subdev *sd,
 		},
 	};
 
-	struct v4l2_subdev_krouting routing = {
+	const struct v4l2_subdev_krouting routing = {
 		.num_routes = ARRAY_SIZE(routes),
 		.routes = routes,
 	};
 
-	return _ub953_set_routing(sd, state, &routing);
+	return ub953_set_routing_normal(sd, state, &routing);
 }
 
 static int ub953_log_status(struct v4l2_subdev *sd)
@@ -675,6 +894,120 @@ static int ub953_log_status(struct v4l2_subdev *sd)
 	return 0;
 }
 
+static int ub953_enable_tpg(struct ub953_data *priv,
+			    enum ub953_tpg_pattern pattern)
+{
+	const struct ub953_format_info *fmt_info;
+	struct device *dev = &priv->client->dev;
+	struct v4l2_subdev *sd = &priv->sd;
+	struct v4l2_subdev_state *state;
+	struct v4l2_mbus_framefmt *fmt;
+	struct v4l2_fract *ival;
+	const u8 num_cbars = 8;
+	const u8 vc = 0; /* Always VC 0 for now */
+	const u8 vbp = 33;
+	const u8 vfp = 10;
+	const u16 tot_blanking = vbp + vfp + 2;
+	u16 line_size; /* in bytes */
+	u16 bar_size; /* in bytes */
+	u16 act_lpf; /* active lines/frame */
+	u16 tot_lpf; /* tot lines/frame */
+	u64 line_pd; /* Line period in 10-ns units */
+
+	state = v4l2_subdev_get_locked_active_state(sd);
+
+	fmt = v4l2_subdev_state_get_format(state, UB953_PAD_TPG, 0);
+	if (!fmt)
+		return -EINVAL;
+
+	ival = v4l2_subdev_state_get_interval(state, UB953_PAD_TPG, 0);
+	if (!ival)
+		return -EINVAL;
+
+	fmt_info = ub953_find_format(fmt->code);
+	if (!fmt_info) {
+		dev_err(dev, "unsupported TPG format %#x\n", fmt->code);
+		return -EINVAL;
+	}
+
+	line_size = fmt->width * fmt_info->bitspp / 8;
+	bar_size = rounddown(line_size / num_cbars, fmt_info->block_size);
+	act_lpf = fmt->height;
+	tot_lpf = act_lpf + tot_blanking;
+	line_pd = div_u64((u64)NANO / 10 * ival->numerator,
+			  ival->denominator * tot_lpf);
+
+	if (line_pd > 0xffff) {
+		dev_err(dev, "Line period over the limit: %llu\n", line_pd);
+		return -EINVAL;
+	}
+
+	if (fmt->width * fmt_info->bitspp % 8 != 0) {
+		dev_err(dev, "Invalid TPG width\n");
+		return -EINVAL;
+	}
+
+	if (line_size % fmt_info->block_size != 0) {
+		dev_err(dev, "Invalid TPG line size\n");
+		return -EINVAL;
+	}
+
+	ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CSI_DI,
+			(vc << 6) | (fmt_info->dt << 0));
+	ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
+			  UB953_IND_PGEN_LINE_SIZE1, line_size);
+	ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
+			  UB953_IND_PGEN_BAR_SIZE1, bar_size);
+	ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
+			  UB953_IND_PGEN_ACT_LPF1, act_lpf);
+	ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
+			  UB953_IND_PGEN_TOT_LPF1, tot_lpf);
+	ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
+			  UB953_IND_PGEN_LINE_PD1, line_pd);
+	ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_VBP,
+			vbp);
+	ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_VFP,
+			vfp);
+
+	for (unsigned int i = 0; i < 3; ++i)
+		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
+				UB953_IND_PGEN_COLOR(i), 0x0);
+
+	ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CFG,
+			UB953_IND_PGEN_CFG_FIXED_COLOR_MODE |
+			FIELD_PREP(UB953_IND_PGEN_CFG_NUM_CBARS_MASK, 0) |
+			FIELD_PREP(UB953_IND_PGEN_CFG_BLOCK_SIZE_MASK,
+				   fmt_info->block_size));
+
+	switch (pattern) {
+	case UB953_TPG_RED:
+		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
+				UB953_IND_PGEN_COLOR(2), 0xff);
+		break;
+	case UB953_TPG_GREEN:
+		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
+				UB953_IND_PGEN_COLOR(1), 0xff);
+		break;
+	case UB953_TPG_BLUE:
+		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
+				UB953_IND_PGEN_COLOR(0), 0xff);
+		break;
+	default:
+		break;
+	}
+
+	ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CTL,
+			UB953_IND_PGEN_CTL_PGEN_ENABLE);
+
+	return 0;
+}
+
+static void ub953_disable_tpg(struct ub953_data *priv)
+{
+	ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CTL,
+			0x0);
+}
+
 static int ub953_enable_streams(struct v4l2_subdev *sd,
 				struct v4l2_subdev_state *state, u32 pad,
 				u64 streams_mask)
@@ -683,14 +1016,24 @@ static int ub953_enable_streams(struct v4l2_subdev *sd,
 	u64 sink_streams;
 	int ret;
 
-	sink_streams = v4l2_subdev_state_xlate_streams(state, UB953_PAD_SOURCE,
-						       UB953_PAD_SINK,
-						       &streams_mask);
+	if (ub953_is_tpg_selected(state)) {
+		ret = ub953_enable_tpg(priv, priv->tpg_ctrl->val);
+		if (ret)
+			return ret;
 
-	ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad,
-					 sink_streams);
-	if (ret)
-		return ret;
+		sink_streams = BIT_ULL(0);
+	} else {
+		sink_streams = v4l2_subdev_state_xlate_streams(state,
+							       UB953_PAD_SOURCE,
+							       UB953_PAD_SINK,
+							       &streams_mask);
+
+		ret = v4l2_subdev_enable_streams(priv->source_sd,
+						 priv->source_sd_pad,
+						 sink_streams);
+		if (ret)
+			return ret;
+	}
 
 	priv->enabled_source_streams |= streams_mask;
 
@@ -705,14 +1048,22 @@ static int ub953_disable_streams(struct v4l2_subdev *sd,
 	u64 sink_streams;
 	int ret;
 
-	sink_streams = v4l2_subdev_state_xlate_streams(state, UB953_PAD_SOURCE,
-						       UB953_PAD_SINK,
-						       &streams_mask);
+	if (ub953_is_tpg_selected(state)) {
+		ub953_disable_tpg(priv);
 
-	ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad,
-					  sink_streams);
-	if (ret)
-		return ret;
+		sink_streams = BIT_ULL(0);
+	} else {
+		sink_streams = v4l2_subdev_state_xlate_streams(state,
+							       UB953_PAD_SOURCE,
+							       UB953_PAD_SINK,
+							       &streams_mask);
+
+		ret = v4l2_subdev_disable_streams(priv->source_sd,
+						  priv->source_sd_pad,
+						  sink_streams);
+		if (ret)
+			return ret;
+	}
 
 	priv->enabled_source_streams &= ~streams_mask;
 
@@ -726,6 +1077,8 @@ static const struct v4l2_subdev_pad_ops ub953_pad_ops = {
 	.get_frame_desc = ub953_get_frame_desc,
 	.get_fmt = v4l2_subdev_get_fmt,
 	.set_fmt = ub953_set_fmt,
+	.get_frame_interval = ub953_get_frame_interval,
+	.set_frame_interval = ub953_set_frame_interval,
 };
 
 static const struct v4l2_subdev_core_ops ub953_subdev_core_ops = {
@@ -1261,6 +1614,29 @@ static int ub953_hw_init(struct ub953_data *priv)
 	return 0;
 }
 
+static const char * const ub953_test_pattern_menu[] = {
+	[UB953_TPG_RED] = "Red",
+	[UB953_TPG_GREEN] = "Green",
+	[UB953_TPG_BLUE] = "Blue",
+};
+
+static int ub953_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ub953_data *priv = container_of(ctrl->handler,
+					struct ub953_data, hdl);
+
+	switch (ctrl->id) {
+	case V4L2_CID_TEST_PATTERN:
+		return ub953_enable_tpg(priv, ctrl->val);
+	}
+
+	return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops ub953_ctrl_ops = {
+	.s_ctrl = ub953_s_ctrl,
+};
+
 static int ub953_subdev_init(struct ub953_data *priv)
 {
 	struct device *dev = &priv->client->dev;
@@ -1269,17 +1645,38 @@ static int ub953_subdev_init(struct ub953_data *priv)
 	v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub953_subdev_ops);
 	priv->sd.internal_ops = &ub953_internal_ops;
 
+	v4l2_ctrl_handler_init(&priv->hdl, 1);
+	v4l2_ctrl_new_std_menu_items(&priv->hdl, &ub953_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(ub953_test_pattern_menu) - 1,
+				     0, 0, ub953_test_pattern_menu);
+
+	if (priv->hdl.error) {
+		ret = priv->hdl.error;
+		dev_err_probe(dev, ret, "Failed to init controls\n");
+		goto err_ctrl_handler_free;
+	}
+
+	priv->sd.ctrl_handler = &priv->hdl;
+	priv->tpg_ctrl = v4l2_ctrl_find(&priv->hdl, V4L2_CID_TEST_PATTERN);
+
 	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
 			  V4L2_SUBDEV_FL_STREAMS;
 	priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
 	priv->sd.entity.ops = &ub953_entity_ops;
 
-	priv->pads[0].flags = MEDIA_PAD_FL_SINK;
-	priv->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+	priv->pads[UB953_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+	priv->pads[UB953_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+	priv->pads[UB953_PAD_TPG].flags = MEDIA_PAD_FL_INTERNAL |
+					  MEDIA_PAD_FL_SINK;
 
-	ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads);
-	if (ret)
-		return dev_err_probe(dev, ret, "Failed to init pads\n");
+	ret = media_entity_pads_init(&priv->sd.entity, UB953_NUM_PADS, priv->pads);
+	if (ret) {
+		dev_err_probe(dev, ret, "Failed to init pads\n");
+		goto err_ctrl_handler_free;
+	}
+
+	priv->sd.state_lock = priv->sd.ctrl_handler->lock;
 
 	ret = v4l2_subdev_init_finalize(&priv->sd);
 	if (ret)
@@ -1306,12 +1703,15 @@ static int ub953_subdev_init(struct ub953_data *priv)
 	v4l2_subdev_cleanup(&priv->sd);
 err_entity_cleanup:
 	media_entity_cleanup(&priv->sd.entity);
+err_ctrl_handler_free:
+	v4l2_ctrl_handler_free(&priv->hdl);
 
 	return ret;
 }
 
 static void ub953_subdev_uninit(struct ub953_data *priv)
 {
+	v4l2_ctrl_handler_free(&priv->hdl);
 	v4l2_async_unregister_subdev(&priv->sd);
 	ub953_v4l2_notifier_unregister(priv);
 	v4l2_subdev_cleanup(&priv->sd);

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support
  2025-01-09 10:15 ` [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support Tomi Valkeinen
@ 2025-01-10 10:37   ` kernel test robot
  2025-01-10 12:39   ` kernel test robot
  1 sibling, 0 replies; 5+ messages in thread
From: kernel test robot @ 2025-01-10 10:37 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: oe-kbuild-all

Hi Tomi,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on 40ed9e9b2808beeb835bd0ed971fb364c285d39c]

url:    https://github.com/intel-lab-lkp/linux/commits/Tomi-Valkeinen/media-mc-Add-INTERNAL-pad-flag/20250109-181850
base:   40ed9e9b2808beeb835bd0ed971fb364c285d39c
patch link:    https://lore.kernel.org/r/20250109-ub953-tpg-v1-2-d7392375c243%40ideasonboard.com
patch subject: [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support
config: csky-randconfig-001-20250110 (https://download.01.org/0day-ci/archive/20250110/202501101823.2pdM9LEe-lkp@intel.com/config)
compiler: csky-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250110/202501101823.2pdM9LEe-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501101823.2pdM9LEe-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/media/i2c/ds90ub953.c: In function 'ub953_enable_tpg':
>> drivers/media/i2c/ds90ub953.c:978:25: error: implicit declaration of function 'FIELD_PREP' [-Wimplicit-function-declaration]
     978 |                         FIELD_PREP(UB953_IND_PGEN_CFG_NUM_CBARS_MASK, 0) |
         |                         ^~~~~~~~~~


vim +/FIELD_PREP +978 drivers/media/i2c/ds90ub953.c

   896	
   897	static int ub953_enable_tpg(struct ub953_data *priv,
   898				    enum ub953_tpg_pattern pattern)
   899	{
   900		const struct ub953_format_info *fmt_info;
   901		struct device *dev = &priv->client->dev;
   902		struct v4l2_subdev *sd = &priv->sd;
   903		struct v4l2_subdev_state *state;
   904		struct v4l2_mbus_framefmt *fmt;
   905		struct v4l2_fract *ival;
   906		const u8 num_cbars = 8;
   907		const u8 vc = 0; /* Always VC 0 for now */
   908		const u8 vbp = 33;
   909		const u8 vfp = 10;
   910		const u16 tot_blanking = vbp + vfp + 2;
   911		u16 line_size; /* in bytes */
   912		u16 bar_size; /* in bytes */
   913		u16 act_lpf; /* active lines/frame */
   914		u16 tot_lpf; /* tot lines/frame */
   915		u64 line_pd; /* Line period in 10-ns units */
   916	
   917		state = v4l2_subdev_get_locked_active_state(sd);
   918	
   919		fmt = v4l2_subdev_state_get_format(state, UB953_PAD_TPG, 0);
   920		if (!fmt)
   921			return -EINVAL;
   922	
   923		ival = v4l2_subdev_state_get_interval(state, UB953_PAD_TPG, 0);
   924		if (!ival)
   925			return -EINVAL;
   926	
   927		fmt_info = ub953_find_format(fmt->code);
   928		if (!fmt_info) {
   929			dev_err(dev, "unsupported TPG format %#x\n", fmt->code);
   930			return -EINVAL;
   931		}
   932	
   933		line_size = fmt->width * fmt_info->bitspp / 8;
   934		bar_size = rounddown(line_size / num_cbars, fmt_info->block_size);
   935		act_lpf = fmt->height;
   936		tot_lpf = act_lpf + tot_blanking;
   937		line_pd = div_u64((u64)NANO / 10 * ival->numerator,
   938				  ival->denominator * tot_lpf);
   939	
   940		if (line_pd > 0xffff) {
   941			dev_err(dev, "Line period over the limit: %llu\n", line_pd);
   942			return -EINVAL;
   943		}
   944	
   945		if (fmt->width * fmt_info->bitspp % 8 != 0) {
   946			dev_err(dev, "Invalid TPG width\n");
   947			return -EINVAL;
   948		}
   949	
   950		if (line_size % fmt_info->block_size != 0) {
   951			dev_err(dev, "Invalid TPG line size\n");
   952			return -EINVAL;
   953		}
   954	
   955		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CSI_DI,
   956				(vc << 6) | (fmt_info->dt << 0));
   957		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   958				  UB953_IND_PGEN_LINE_SIZE1, line_size);
   959		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   960				  UB953_IND_PGEN_BAR_SIZE1, bar_size);
   961		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   962				  UB953_IND_PGEN_ACT_LPF1, act_lpf);
   963		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   964				  UB953_IND_PGEN_TOT_LPF1, tot_lpf);
   965		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   966				  UB953_IND_PGEN_LINE_PD1, line_pd);
   967		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_VBP,
   968				vbp);
   969		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_VFP,
   970				vfp);
   971	
   972		for (unsigned int i = 0; i < 3; ++i)
   973			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   974					UB953_IND_PGEN_COLOR(i), 0x0);
   975	
   976		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CFG,
   977				UB953_IND_PGEN_CFG_FIXED_COLOR_MODE |
 > 978				FIELD_PREP(UB953_IND_PGEN_CFG_NUM_CBARS_MASK, 0) |
   979				FIELD_PREP(UB953_IND_PGEN_CFG_BLOCK_SIZE_MASK,
   980					   fmt_info->block_size));
   981	
   982		switch (pattern) {
   983		case UB953_TPG_RED:
   984			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   985					UB953_IND_PGEN_COLOR(2), 0xff);
   986			break;
   987		case UB953_TPG_GREEN:
   988			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   989					UB953_IND_PGEN_COLOR(1), 0xff);
   990			break;
   991		case UB953_TPG_BLUE:
   992			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   993					UB953_IND_PGEN_COLOR(0), 0xff);
   994			break;
   995		default:
   996			break;
   997		}
   998	
   999		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CTL,
  1000				UB953_IND_PGEN_CTL_PGEN_ENABLE);
  1001	
  1002		return 0;
  1003	}
  1004	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support
  2025-01-09 10:15 ` [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support Tomi Valkeinen
  2025-01-10 10:37   ` kernel test robot
@ 2025-01-10 12:39   ` kernel test robot
  1 sibling, 0 replies; 5+ messages in thread
From: kernel test robot @ 2025-01-10 12:39 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: llvm, oe-kbuild-all

Hi Tomi,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on 40ed9e9b2808beeb835bd0ed971fb364c285d39c]

url:    https://github.com/intel-lab-lkp/linux/commits/Tomi-Valkeinen/media-mc-Add-INTERNAL-pad-flag/20250109-181850
base:   40ed9e9b2808beeb835bd0ed971fb364c285d39c
patch link:    https://lore.kernel.org/r/20250109-ub953-tpg-v1-2-d7392375c243%40ideasonboard.com
patch subject: [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support
config: i386-buildonly-randconfig-001-20250110 (https://download.01.org/0day-ci/archive/20250110/202501102016.Hyworcdy-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250110/202501102016.Hyworcdy-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501102016.Hyworcdy-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/media/i2c/ds90ub953.c:16:
   In file included from include/linux/i2c-atr.h:14:
   In file included from include/linux/i2c.h:19:
   In file included from include/linux/regulator/consumer.h:35:
   In file included from include/linux/suspend.h:5:
   In file included from include/linux/swap.h:9:
   In file included from include/linux/memcontrol.h:21:
   In file included from include/linux/mm.h:2223:
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
>> drivers/media/i2c/ds90ub953.c:978:4: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     978 |                         FIELD_PREP(UB953_IND_PGEN_CFG_NUM_CBARS_MASK, 0) |
         |                         ^
   1 warning and 1 error generated.


vim +/FIELD_PREP +978 drivers/media/i2c/ds90ub953.c

   896	
   897	static int ub953_enable_tpg(struct ub953_data *priv,
   898				    enum ub953_tpg_pattern pattern)
   899	{
   900		const struct ub953_format_info *fmt_info;
   901		struct device *dev = &priv->client->dev;
   902		struct v4l2_subdev *sd = &priv->sd;
   903		struct v4l2_subdev_state *state;
   904		struct v4l2_mbus_framefmt *fmt;
   905		struct v4l2_fract *ival;
   906		const u8 num_cbars = 8;
   907		const u8 vc = 0; /* Always VC 0 for now */
   908		const u8 vbp = 33;
   909		const u8 vfp = 10;
   910		const u16 tot_blanking = vbp + vfp + 2;
   911		u16 line_size; /* in bytes */
   912		u16 bar_size; /* in bytes */
   913		u16 act_lpf; /* active lines/frame */
   914		u16 tot_lpf; /* tot lines/frame */
   915		u64 line_pd; /* Line period in 10-ns units */
   916	
   917		state = v4l2_subdev_get_locked_active_state(sd);
   918	
   919		fmt = v4l2_subdev_state_get_format(state, UB953_PAD_TPG, 0);
   920		if (!fmt)
   921			return -EINVAL;
   922	
   923		ival = v4l2_subdev_state_get_interval(state, UB953_PAD_TPG, 0);
   924		if (!ival)
   925			return -EINVAL;
   926	
   927		fmt_info = ub953_find_format(fmt->code);
   928		if (!fmt_info) {
   929			dev_err(dev, "unsupported TPG format %#x\n", fmt->code);
   930			return -EINVAL;
   931		}
   932	
   933		line_size = fmt->width * fmt_info->bitspp / 8;
   934		bar_size = rounddown(line_size / num_cbars, fmt_info->block_size);
   935		act_lpf = fmt->height;
   936		tot_lpf = act_lpf + tot_blanking;
   937		line_pd = div_u64((u64)NANO / 10 * ival->numerator,
   938				  ival->denominator * tot_lpf);
   939	
   940		if (line_pd > 0xffff) {
   941			dev_err(dev, "Line period over the limit: %llu\n", line_pd);
   942			return -EINVAL;
   943		}
   944	
   945		if (fmt->width * fmt_info->bitspp % 8 != 0) {
   946			dev_err(dev, "Invalid TPG width\n");
   947			return -EINVAL;
   948		}
   949	
   950		if (line_size % fmt_info->block_size != 0) {
   951			dev_err(dev, "Invalid TPG line size\n");
   952			return -EINVAL;
   953		}
   954	
   955		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CSI_DI,
   956				(vc << 6) | (fmt_info->dt << 0));
   957		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   958				  UB953_IND_PGEN_LINE_SIZE1, line_size);
   959		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   960				  UB953_IND_PGEN_BAR_SIZE1, bar_size);
   961		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   962				  UB953_IND_PGEN_ACT_LPF1, act_lpf);
   963		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   964				  UB953_IND_PGEN_TOT_LPF1, tot_lpf);
   965		ub953_write_ind16(priv, UB953_IND_TARGET_PAT_GEN,
   966				  UB953_IND_PGEN_LINE_PD1, line_pd);
   967		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_VBP,
   968				vbp);
   969		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_VFP,
   970				vfp);
   971	
   972		for (unsigned int i = 0; i < 3; ++i)
   973			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   974					UB953_IND_PGEN_COLOR(i), 0x0);
   975	
   976		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CFG,
   977				UB953_IND_PGEN_CFG_FIXED_COLOR_MODE |
 > 978				FIELD_PREP(UB953_IND_PGEN_CFG_NUM_CBARS_MASK, 0) |
   979				FIELD_PREP(UB953_IND_PGEN_CFG_BLOCK_SIZE_MASK,
   980					   fmt_info->block_size));
   981	
   982		switch (pattern) {
   983		case UB953_TPG_RED:
   984			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   985					UB953_IND_PGEN_COLOR(2), 0xff);
   986			break;
   987		case UB953_TPG_GREEN:
   988			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   989					UB953_IND_PGEN_COLOR(1), 0xff);
   990			break;
   991		case UB953_TPG_BLUE:
   992			ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN,
   993					UB953_IND_PGEN_COLOR(0), 0xff);
   994			break;
   995		default:
   996			break;
   997		}
   998	
   999		ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_CTL,
  1000				UB953_IND_PGEN_CTL_PGEN_ENABLE);
  1001	
  1002		return 0;
  1003	}
  1004	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-01-10 12:40 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-09 10:15 [PATCH RFC 0/2] media: TPG with internal pads Tomi Valkeinen
2025-01-09 10:15 ` [PATCH RFC 1/2] media: mc: Add INTERNAL pad flag Tomi Valkeinen
2025-01-09 10:15 ` [PATCH RFC 2/2] media: i2c: ds90ub953: Add TPG support Tomi Valkeinen
2025-01-10 10:37   ` kernel test robot
2025-01-10 12:39   ` kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.