[v10,00/14] TVP5150 features and fixes
mbox series

Message ID 20190830101646.6530-1-m.felsch@pengutronix.de
Headers show
Series
  • TVP5150 features and fixes
Related show

Message

Marco Felsch Aug. 30, 2019, 10:16 a.m. UTC
Hi,

this v10 adresses Hans comments made on my v9 [1]. This series fixes
minor typos and drops the initial hdmi-connector parsing support. It
also address the set_selection handling if the format was FMT_TRY and
the subdev-api isn't available. During work on this series I noticed a
bug within the driver. This gets addressed by Patch ("media: tvp5150:
fix set_selection rectangle handling").

Regards,
  Marco

[1] https://www.spinics.net/lists/linux-media/msg156237.html

Javier Martinez Canillas (1):
  partial revert of "[media] tvp5150: add HW input connectors support"

Marco Felsch (12):
  dt-bindings: connector: analog: add sdtv standards property
  media: v4l2-fwnode: add v4l2_fwnode_connector
  media: v4l2-fwnode: add initial connector parsing support
  media: tvp5150: add input source selection of_graph support
  media: dt-bindings: tvp5150: Add input port connectors DT bindings
  media: tvp5150: fix set_selection rectangle handling
  media: tvp5150: add FORMAT_TRY support for get/set selection handlers
  media: tvp5150: add s_power callback
  media: dt-bindings: tvp5150: cleanup bindings stlye
  media: dt-bindings: tvp5150: add optional sdtv standards documentation
  media: tvp5150: add support to limit sdtv standards
  media: tvp5150: make debug output more readable

Michael Tretter (1):
  media: tvp5150: initialize subdev before parsing device tree

 .../display/connector/analog-tv-connector.txt |   6 +
 .../devicetree/bindings/media/i2c/tvp5150.txt | 146 +++-
 drivers/media/i2c/tvp5150.c                   | 669 +++++++++++++-----
 drivers/media/v4l2-core/v4l2-fwnode.c         | 129 ++++
 include/dt-bindings/display/sdtv-standards.h  |  76 ++
 include/dt-bindings/media/tvp5150.h           |   2 -
 include/media/v4l2-fwnode.h                   |  81 +++
 include/uapi/linux/videodev2.h                |   4 +
 8 files changed, 914 insertions(+), 199 deletions(-)
 create mode 100644 include/dt-bindings/display/sdtv-standards.h

Comments

Hans Verkuil Sept. 27, 2019, 8:52 a.m. UTC | #1
Hi Marco,

On 8/30/19 12:16 PM, Marco Felsch wrote:
> The tvp5150 accepts NTSC(M,J,4.43), PAL (B,D,G,H,I,M,N) and SECAM video
> data and is able to auto-detect the input signal. The auto-detection
> does not work if the connector does not receive an input signal and the
> tvp5150 might not be configured correctly. This misconfiguration leads
> into wrong decoded video streams if the tvp5150 gets powered on before
> the video signal is present.
> 
> Limit the supported sdtv standards according to the actual selected
> connector to avoid a misconfiguration.
> 
> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>

checkpatch gave a bunch of warnings:

WARNING: line over 80 characters
#97: FILE: drivers/media/i2c/tvp5150.c:1393:
+                       decoder->cur_connector->base.connector.analog.sdtv_stds);

WARNING: line over 80 characters
#107: FILE: drivers/media/i2c/tvp5150.c:1613:
+                               decoder->connectors[i].base.connector.analog.sdtv_stds);

CHECK: Alignment should match open parenthesis
#107: FILE: drivers/media/i2c/tvp5150.c:1613:
+                       tvp5150_s_std(sd,
+                               decoder->connectors[i].base.connector.analog.sdtv_stds);

I think it would be best to introduce a local pointer to
decoder->connectors[i].base.connector.analog.

Regards,

	Hans

> ---
> [1] https://patchwork.kernel.org/cover/10794703/
> 
> v8:
> - adapt commit message
> - fix rebasing issue
> - apdapt to new v4l2_fwnode_connector_analog naming
> - fix cur_connector update during tvp5150_link_setup()
>   -> Only update if we have of-connectors.
> - fix supported_stds detection during tvp5150_s_std()
>   -> use connectors_num to detect of-connectors presence
> 
> v5:
> - probe() initialize supported tv-norms according the given connectors
>   if they are available.
> - check if media-controller is used. Don't limit the norm if it isn't
>   used.
> - add more logic to be smarter during connector changing so it is
>   intuitiver for the user space.
> 
> v2-v4:
> - nothing since the patch was squashed from series [1] into this
>   series.
> 
>  drivers/media/i2c/tvp5150.c | 78 ++++++++++++++++++++++++++++++++++++-
>  1 file changed, 76 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> index d7079809f33f..da7e7fe16f89 100644
> --- a/drivers/media/i2c/tvp5150.c
> +++ b/drivers/media/i2c/tvp5150.c
> @@ -32,6 +32,13 @@
>  #define TVP5150_MBUS_FMT	MEDIA_BUS_FMT_UYVY8_2X8
>  #define TVP5150_FIELD		V4L2_FIELD_ALTERNATE
>  #define TVP5150_COLORSPACE	V4L2_COLORSPACE_SMPTE170M
> +#define TVP5150_STD_MASK	(V4L2_STD_NTSC     | \
> +				 V4L2_STD_NTSC_443 | \
> +				 V4L2_STD_PAL      | \
> +				 V4L2_STD_PAL_M    | \
> +				 V4L2_STD_PAL_N    | \
> +				 V4L2_STD_PAL_Nc   | \
> +				 V4L2_STD_SECAM)
>  
>  #define TVP5150_MAX_CONNECTORS	3 /* Check dt-bindings for more information */
>  
> @@ -64,6 +71,7 @@ struct tvp5150 {
>  
>  	struct media_pad pads[TVP5150_NUM_PADS];
>  	struct tvp5150_connector connectors[TVP5150_MAX_CONNECTORS];
> +	struct tvp5150_connector *cur_connector;
>  	unsigned int connectors_num;
>  
>  	struct v4l2_ctrl_handler hdl;
> @@ -783,17 +791,33 @@ static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
>  static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
>  {
>  	struct tvp5150 *decoder = to_tvp5150(sd);
> +	struct tvp5150_connector *cur_con = decoder->cur_connector;
> +	v4l2_std_id supported_stds;
>  
>  	if (decoder->norm == std)
>  		return 0;
>  
> +	/* In case of no of-connectors are available no limitations are made */
> +	if (!decoder->connectors_num)
> +		supported_stds = V4L2_STD_ALL;
> +	else
> +		supported_stds = cur_con->base.connector.analog.sdtv_stds;
> +
> +	/*
> +	 * Check if requested std or group of std's is/are supported by the
> +	 * connector.
> +	 */
> +	if ((supported_stds & std) == 0)
> +		return -EINVAL;
> +
>  	/* Change cropping height limits */
>  	if (std & V4L2_STD_525_60)
>  		decoder->rect.height = TVP5150_V_MAX_525_60;
>  	else
>  		decoder->rect.height = TVP5150_V_MAX_OTHERS;
>  
> -	decoder->norm = std;
> +	/* Set only the specific supported std in case of group of std's. */
> +	decoder->norm = supported_stds & std;
>  
>  	return tvp5150_set_std(sd, std);
>  }
> @@ -1331,6 +1355,8 @@ static int tvp5150_link_setup(struct media_entity *entity,
>  			  TVP5150_BLACK_SCREEN, 0);
>  
>  	if (flags & MEDIA_LNK_FL_ENABLED) {
> +		u32 new_norm;
> +
>  		/*
>  		 * S-Video connector is conneted to both ports AIP1A and AIP1B.
>  		 * Both links must be enabled in one-shot regardless which link
> @@ -1342,6 +1368,29 @@ static int tvp5150_link_setup(struct media_entity *entity,
>  			if (err)
>  				return err;
>  		}
> +
> +		if (!decoder->connectors_num)
> +			return 0;
> +
> +		/* Update the current connector */
> +		decoder->cur_connector =
> +			container_of(remote, struct tvp5150_connector, pad);
> +
> +		/*
> +		 * Do nothing if the new connector supports the same tv-norms as
> +		 * the old one.
> +		 */
> +		new_norm = decoder->norm &
> +			decoder->cur_connector->base.connector.analog.sdtv_stds;
> +		if (decoder->norm == new_norm)
> +			return 0;
> +
> +		/*
> +		 * Fallback to the new connector tv-norms if we can't find any
> +		 * common between the current tv-norm and the new one.
> +		 */
> +		tvp5150_s_std(sd, new_norm ? new_norm :
> +			decoder->cur_connector->base.connector.analog.sdtv_stds);
>  	}
>  
>  	return 0;
> @@ -1559,6 +1608,9 @@ static int tvp5150_registered(struct v4l2_subdev *sd)
>  				TVP5150_COMPOSITE1;
>  
>  			tvp5150_selmux(sd);
> +			decoder->cur_connector = &decoder->connectors[i];
> +			tvp5150_s_std(sd,
> +				decoder->connectors[i].base.connector.analog.sdtv_stds);
>  		}
>  	}
>  
> @@ -1848,6 +1900,13 @@ tvp5150_add_connector(struct tvp5150 *decoder, struct device_node *np,
>  		goto err;
>  	}
>  
> +	if (!(vc->connector.analog.sdtv_stds & TVP5150_STD_MASK)) {
> +		dev_err(dev, "Unsupported tv-norm on connector %s\n",
> +			remote_np->name);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
>  	c->ent.flags = MEDIA_ENT_FL_CONNECTOR;
>  	c->ent.name = vc->label;
>  	c->ent.name = kasprintf(GFP_KERNEL, "%s %s", remote_np->name,
> @@ -1945,6 +2004,7 @@ static int tvp5150_probe(struct i2c_client *c)
>  	struct v4l2_subdev *sd;
>  	struct device_node *np = c->dev.of_node;
>  	struct regmap *map;
> +	unsigned int i;
>  	int res;
>  
>  	/* Check if the adapter supports the needed features */
> @@ -1989,7 +2049,21 @@ static int tvp5150_probe(struct i2c_client *c)
>  	if (res < 0)
>  		return res;
>  
> -	core->norm = V4L2_STD_ALL;	/* Default is autodetect */
> +	/*
> +	 * Iterate over all available connectors in case they are supported and
> +	 * successfully parsed. Fallback to default autodetect in case they
> +	 * aren't supported.
> +	 */
> +	for (i = 0; i < core->connectors_num; i++) {
> +		struct v4l2_fwnode_connector *vc;
> +
> +		vc = &core->connectors[i].base;
> +		core->norm |= vc->connector.analog.sdtv_stds;
> +	}
> +
> +	if (!core->connectors_num)
> +		core->norm = V4L2_STD_ALL;
> +
>  	core->detected_norm = V4L2_STD_UNKNOWN;
>  	core->input = TVP5150_COMPOSITE1;
>  	core->enable = true;
>
Hans Verkuil Sept. 27, 2019, 8:54 a.m. UTC | #2
On 8/30/19 12:16 PM, Marco Felsch wrote:
> This patch adds the of_graph support to describe the tvp input connections.
> Physical the TVP5150 has three ports: AIP1A, AIP1B and YOUT. As result
> of discussion [1],[2] the device-tree maps these ports 1:1. Look at the
> Documentation for more information. Since the TVP5150 is a converter/bridge
> the device-tree must contain at least 1-input and 1-output port. The
> mc-connectors and mc-links are only created if the device-tree contains the
> corresponding connector nodes. If more than one connector is available the
> media_entity_operations.link_setup() callback ensures that only one
> connector is active.
> 
> [1] https://www.spinics.net/lists/linux-media/msg138545.html
> [2] https://www.spinics.net/lists/linux-media/msg138546.html
> 
> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>

More checkpatch warnings:

WARNING: line over 80 characters
#110: FILE: drivers/media/i2c/tvp5150.c:1244:
+                       is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;

CHECK: No space is necessary after a cast
#157: FILE: drivers/media/i2c/tvp5150.c:1291:
+                       err = tvp5150_set_link((struct media_pad *) remote,

WARNING: line over 80 characters
#192: FILE: drivers/media/i2c/tvp5150.c:1466:
+               unsigned int port = decoder->connectors[i].base.links[0].remote_port;

WARNING: line over 80 characters
#194: FILE: drivers/media/i2c/tvp5150.c:1468:
+               bool is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;

Here too adding a local pointer to decoder->connectors[i].base would
prevent these warnings, and make it more readable.

Regards,

	Hans

> ---
> Changelog:
> 
> [1] https://patchwork.kernel.org/cover/10794703/
> [2] https://patchwork.kernel.org/cover/10786553/
> 
> v8:
> - fix rebasing issue
> - fix error handling during tvp5150_registered()
> - adapt to new v4l2_connector_type enum
> 
> v7:
> - don't init enum tvp5150_pads with TVP5150_COMPOSITE0 functionality
> - break some 80 character limitation to improve readability
> - fix comment style -> always start with capital letters and end with dot
> - fix some minor style issues
> - fix tvp5150_registered error handling
> - simplify tvp5150_mc_init
> - make connectors static
>   -> since now only three connectors are possible (as described in DT)
>   -> drop tvp5150.endpoints storage
>   -> squash tvp5150_parse_dt and tvp5150_add_of_connectors
> - improve tvp5150_parse_dt:
>   -> make parsing stricter and fix not detected missconfigured dt-data
>   -> svideo must be connected now to port@[0,1]/endpoint@1
> 
> v6:
> - fix misspelled comments
> - use 'unsigned int' where it's possible
> - cleanup ifdef part-2:
>   * tvp5150_mc_init, tvp5150_add_of_connectors: add surrounding
>     CONFIG_MEDIA_CONTROLLER if def and stubs to improve quality
> - tvp5150_mc_init: uniform interface, use 'struct tvp5150' since all
>   internal function do this.
> - tvp5150_add_of_connectors: call within probe() to make it cleaner
> - tvp5150_parse_dt: move local loop vars within the loop.
> 
> v5:
> - Fixing build deps:
>   - tvp5150_mc_init: fix CONFIG_MEDIA_CONTROLLER deps
>   - struct tvp5150: drop CONFIG_MEDIA_CONTROLLER conditional property
>     includes. This leads into to complex deps for futher development.
>   - tvp5150_dt_cleanup: enable function only if CONFIG_OF is enabled
>   - tvp5150_parse_dt: enable function only if CONFIG_OF is enabled
>   - tvp5150_probe: call tvp5150_dt_cleanup only if CONFIG_OF is enabled
> 
> - Simplify link_setup routine:
>   - use generic connector parsing since both series [1,2] are squashed into
>     one
>   - struct tvp5150: drop pads_state and modify_second_link property
>     due to link_setup() rework.
>   - tvp5150_link_setup: add more comments
>   - tvp5150_link_setup: simply the link setup routine a lot. Edit the 2nd
>     link directly within the driver instead of a recursive media-framework
>     call (__media_entity_setup_link). This improves the readability and
>     shrinks the driver code.
>   - tvp5150_link_setup: disable all active links in case user switches
>     connectors without disable it first.
>   - tvp5150_registered: simplify default link enable path due to link_setup()
>     rework.
> 
> - General cleanups
>   - tvp5150_parse_dt: drop unecessary test
>   - tvp5150_parse_dt: add err message due to misconfiguration
>   - tvp5150_parse_dt: make use of V4L2_MBUS_UNKNOWN definition
>   - s/dev_dbg/dev_dbg_lvl
> 
> v4:
>  - rebase on top of media_tree/master, fix merge conflict due to commit
>    60359a28d592 ("media: v4l: fwnode: Initialise the V4L2 fwnode endpoints
>    to zero")
> 
> v3:
> - probe(): s/err/err_free_v4l2_ctrls
> - drop MC dependency for tvp5150_pads
> 
> v2:
> - adapt commit message
> - unify ifdef switches
> - rename tvp5150_valid_input -> tvp5150_of_valid_input, to be more precise
> - mc: use 2-input and 1-output pad
> - mc: link svideo connector to both input pads
> - mc: enable/disable svideo links in one go
> - mc: change link_setup() behaviour, switch the input src don't require a
>       explicite disable before.
> - mc: rename 'local' media_pad param to tvp5150_pad to avoid confusion
> - mc: enable link to the first available connector and set the
>       corresponding tvp5150 input src per default during registered()
> - mc/of: factor out oftree connector allocation
> - of: drop svideo dt port
> - of: move svideo connector to port@0/endpoint@1
> - of: require at least 1-in and 1-out endpoint
> 
>  drivers/media/i2c/tvp5150.c | 378 +++++++++++++++++++++++++++++++++---
>  1 file changed, 348 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> index 11a5fd7e2f58..477a929d4f89 100644
> --- a/drivers/media/i2c/tvp5150.c
> +++ b/drivers/media/i2c/tvp5150.c
> @@ -32,6 +32,8 @@
>  #define TVP5150_FIELD		V4L2_FIELD_ALTERNATE
>  #define TVP5150_COLORSPACE	V4L2_COLORSPACE_SMPTE170M
>  
> +#define TVP5150_MAX_CONNECTORS	3 /* Check dt-bindings for more information */
> +
>  MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver");
>  MODULE_AUTHOR("Mauro Carvalho Chehab");
>  MODULE_LICENSE("GPL v2");
> @@ -44,16 +46,25 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");
>  #define dprintk0(__dev, __arg...) dev_dbg_lvl(__dev, 0, 0, __arg)
>  
>  enum tvp5150_pads {
> -	TVP5150_PAD_IF_INPUT,
> +	TVP5150_PAD_AIP1A,
> +	TVP5150_PAD_AIP1B,
>  	TVP5150_PAD_VID_OUT,
>  	TVP5150_NUM_PADS
>  };
>  
> +struct tvp5150_connector {
> +	struct v4l2_fwnode_connector base;
> +	struct media_entity ent;
> +	struct media_pad pad;
> +};
> +
>  struct tvp5150 {
>  	struct v4l2_subdev sd;
> -#ifdef CONFIG_MEDIA_CONTROLLER
> +
>  	struct media_pad pads[TVP5150_NUM_PADS];
> -#endif
> +	struct tvp5150_connector connectors[TVP5150_MAX_CONNECTORS];
> +	unsigned int connectors_num;
> +
>  	struct v4l2_ctrl_handler hdl;
>  	struct v4l2_rect rect;
>  	struct regmap *regmap;
> @@ -1167,6 +1178,130 @@ static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
>  	return 0;
>  }
>  
> +/****************************************************************************
> + *			Media entity ops
> + ****************************************************************************/
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +static int tvp5150_set_link(struct media_pad *connector_pad,
> +			    struct media_pad *tvp5150_pad, u32 flags)
> +{
> +	struct media_link *link;
> +
> +	link = media_entity_find_link(connector_pad, tvp5150_pad);
> +	if (!link)
> +		return -EINVAL;
> +
> +	link->flags = flags;
> +	link->reverse->flags = link->flags;
> +
> +	return 0;
> +}
> +
> +static int tvp5150_disable_all_input_links(struct tvp5150 *decoder)
> +{
> +	struct media_pad *connector_pad;
> +	unsigned int i;
> +	int err;
> +
> +	for (i = 0; i < TVP5150_NUM_PADS - 1; i++) {
> +		connector_pad = media_entity_remote_pad(&decoder->pads[i]);
> +		if (!connector_pad)
> +			continue;
> +
> +		err = tvp5150_set_link(connector_pad, &decoder->pads[i], 0);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tvp5150_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
> +			     u32 config);
> +
> +static int tvp5150_link_setup(struct media_entity *entity,
> +			      const struct media_pad *tvp5150_pad,
> +			      const struct media_pad *remote, u32 flags)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> +	struct tvp5150 *decoder = to_tvp5150(sd);
> +	struct media_pad *other_tvp5150_pad =
> +		&decoder->pads[tvp5150_pad->index ^ 1];
> +	bool is_svideo = false;
> +	unsigned int i;
> +	int err;
> +
> +	/*
> +	 * The TVP5150 state is determined by the enabled sink pad link(s).
> +	 * Enabling or disabling the source pad link has no effect.
> +	 */
> +	if (tvp5150_pad->flags & MEDIA_PAD_FL_SOURCE)
> +		return 0;
> +
> +	/* Check if the svideo connector should be enabled */
> +	for (i = 0; i < decoder->connectors_num; i++) {
> +		if (remote->entity == &decoder->connectors[i].ent) {
> +			is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;
> +			break;
> +		}
> +	}
> +
> +	dev_dbg_lvl(sd->dev, 1, debug, "link setup '%s':%d->'%s':%d[%d]",
> +		    remote->entity->name, remote->index,
> +		    tvp5150_pad->entity->name, tvp5150_pad->index,
> +		    flags & MEDIA_LNK_FL_ENABLED);
> +	if (is_svideo)
> +		dev_dbg_lvl(sd->dev, 1, debug,
> +			    "link setup '%s':%d->'%s':%d[%d]",
> +			    remote->entity->name, remote->index,
> +			    other_tvp5150_pad->entity->name,
> +			    other_tvp5150_pad->index,
> +			    flags & MEDIA_LNK_FL_ENABLED);
> +
> +	/*
> +	 * The TVP5150 has an internal mux which allows the following setup:
> +	 *
> +	 * comp-connector1  --\
> +	 *		       |---> AIP1A
> +	 *		      /
> +	 * svideo-connector -|
> +	 *		      \
> +	 *		       |---> AIP1B
> +	 * comp-connector2  --/
> +	 *
> +	 * We can't rely on user space that the current connector gets disabled
> +	 * first before enabling the new connector. Disable all active
> +	 * connector links to be on the safe side.
> +	 */
> +	err = tvp5150_disable_all_input_links(decoder);
> +	if (err)
> +		return err;
> +
> +	tvp5150_s_routing(sd, is_svideo ? TVP5150_SVIDEO : tvp5150_pad->index,
> +			  flags & MEDIA_LNK_FL_ENABLED ? TVP5150_NORMAL :
> +			  TVP5150_BLACK_SCREEN, 0);
> +
> +	if (flags & MEDIA_LNK_FL_ENABLED) {
> +		/*
> +		 * S-Video connector is conneted to both ports AIP1A and AIP1B.
> +		 * Both links must be enabled in one-shot regardless which link
> +		 * the user requests.
> +		 */
> +		if (is_svideo) {
> +			err = tvp5150_set_link((struct media_pad *) remote,
> +					       other_tvp5150_pad, flags);
> +			if (err)
> +				return err;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct media_entity_operations tvp5150_sd_media_ops = {
> +	.link_setup = tvp5150_link_setup,
> +};
> +#endif
>  /****************************************************************************
>  			I2C Command
>   ****************************************************************************/
> @@ -1314,6 +1449,71 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
>  	return 0;
>  }
>  
> +static int tvp5150_registered(struct v4l2_subdev *sd)
> +{
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	struct tvp5150 *decoder = to_tvp5150(sd);
> +	unsigned int i;
> +	int ret;
> +
> +	/*
> +	 * Setup connector pads and links. Enable the link to the first
> +	 * available connector per default.
> +	 */
> +	for (i = 0; i < decoder->connectors_num; i++) {
> +		struct media_entity *con = &decoder->connectors[i].ent;
> +		struct media_pad *pad = &decoder->connectors[i].pad;
> +		unsigned int port = decoder->connectors[i].base.links[0].remote_port;
> +		unsigned int flags = i ? 0 : MEDIA_LNK_FL_ENABLED;
> +		bool is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;
> +
> +		pad->flags = MEDIA_PAD_FL_SOURCE;
> +		ret = media_entity_pads_init(con, 1, pad);
> +		if (ret < 0)
> +			goto err;
> +
> +		ret = media_device_register_entity(sd->v4l2_dev->mdev, con);
> +		if (ret < 0)
> +			goto err;
> +
> +		ret = media_create_pad_link(con, 0, &sd->entity, port, flags);
> +		if (ret < 0)
> +			goto err;
> +
> +		if (is_svideo) {
> +			/*
> +			 * Check tvp5150_link_setup() comments for more
> +			 * information.
> +			 */
> +			port = decoder->connectors[i].base.links[1].remote_port;
> +			ret = media_create_pad_link(con, 0, &sd->entity, port,
> +						    flags);
> +			if (ret < 0)
> +				goto err;
> +		}
> +
> +		/* Enable default input. */
> +		if (flags == MEDIA_LNK_FL_ENABLED) {
> +			decoder->input =
> +				is_svideo ? TVP5150_SVIDEO :
> +				port == 0 ? TVP5150_COMPOSITE0 :
> +				TVP5150_COMPOSITE1;
> +
> +			tvp5150_selmux(sd);
> +		}
> +	}
> +
> +	return 0;
> +
> +err:
> +	for (i = 0; i < decoder->connectors_num; i++)
> +		media_device_unregister_entity(&decoder->connectors[i].ent);
> +	return ret;
> +#endif
> +
> +	return 0;
> +}
> +
>  /* ----------------------------------------------------------------------- */
>  
>  static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
> @@ -1367,6 +1567,10 @@ static const struct v4l2_subdev_ops tvp5150_ops = {
>  	.pad = &tvp5150_pad_ops,
>  };
>  
> +static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
> +	.registered = tvp5150_registered,
> +};
> +
>  /****************************************************************************
>  			I2C Client & Driver
>   ****************************************************************************/
> @@ -1515,36 +1719,159 @@ static int tvp5150_init(struct i2c_client *c)
>  	return 0;
>  }
>  
> -static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +static int tvp5150_mc_init(struct tvp5150 *decoder)
>  {
> -	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
> -	struct device_node *ep;
> -	unsigned int flags;
> -	int ret = 0;
> +	struct v4l2_subdev *sd = &decoder->sd;
> +	unsigned int i;
>  
> -	ep = of_graph_get_next_endpoint(np, NULL);
> -	if (!ep)
> -		return -EINVAL;
> +	sd->entity.ops = &tvp5150_sd_media_ops;
> +	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
> +
> +	for (i = 0; i < TVP5150_NUM_PADS - 1; i++) {
> +		decoder->pads[i].flags = MEDIA_PAD_FL_SINK;
> +		decoder->pads[i].sig_type = PAD_SIGNAL_ANALOG;
> +	}
> +
> +	decoder->pads[i].flags = MEDIA_PAD_FL_SOURCE;
> +	decoder->pads[i].sig_type = PAD_SIGNAL_DV;
>  
> -	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
> +	return media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS,
> +				      decoder->pads);
> +}
> +
> +#else /* !defined(CONFIG_MEDIA_CONTROLLER) */
> +
> +static inline int tvp5150_mc_init(struct tvp5150 *decoder)
> +{
> +	return 0;
> +}
> +#endif /* defined(CONFIG_MEDIA_CONTROLLER) */
> +
> +static int
> +tvp5150_add_connector(struct tvp5150 *decoder, struct device_node *np,
> +		      unsigned int portn, unsigned int epn, unsigned int svideo)
> +{
> +	struct device *dev = decoder->sd.dev;
> +	struct device_node *ep_np, *remote_np;
> +	struct v4l2_fwnode_connector *vc;
> +	struct tvp5150_connector *c;
> +	unsigned int next_connector = decoder->connectors_num;
> +	int ret;
> +
> +	/* Get free connector entry. */
> +	if (next_connector >= TVP5150_MAX_CONNECTORS)
> +		return 0;
> +
> +	c = &decoder->connectors[next_connector];
> +	vc = &c->base;
> +	ep_np = of_graph_get_endpoint_by_regs(np, portn, epn);
> +	remote_np = of_graph_get_remote_port_parent(ep_np);
> +
> +	ret = v4l2_fwnode_connector_alloc_parse(of_fwnode_handle(ep_np), vc);
>  	if (ret)
>  		goto err;
>  
> -	flags = bus_cfg.bus.parallel.flags;
> +	if ((!svideo && vc->type != V4L2_CONN_COMPOSITE) ||
> +	    (svideo && vc->type != V4L2_CONN_SVIDEO)) {
> +		dev_err(dev, "Invalid connector type for port@%u/endpoint@%u\n",
> +			portn, epn);
> +		ret = -EINVAL;
> +		goto err;
> +	}
>  
> +	if (svideo &&
> +	    (vc->nr_of_links != 2 ||
> +	     vc->links[0].remote_port == vc->links[1].remote_port)) {
> +		dev_err(dev, "Invalid Svideo connector configuration\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	c->ent.flags = MEDIA_ENT_FL_CONNECTOR;
> +	c->ent.name = vc->label;
> +	c->ent.name = kasprintf(GFP_KERNEL, "%s %s", remote_np->name,
> +				vc->label ? vc->label : "");
> +	c->ent.function = vc->type == V4L2_CONN_SVIDEO ?
> +		MEDIA_ENT_F_CONN_SVIDEO : MEDIA_ENT_F_CONN_COMPOSITE;
> +
> +	decoder->connectors_num++;
> +
> +	of_node_put(remote_np);
> +	of_node_put(ep_np);
> +	return 0;
> +
> +err:
> +	of_node_put(remote_np);
> +	of_node_put(ep_np);
> +	v4l2_fwnode_connector_free(vc);
> +	return ret;
> +}
> +
> +static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
> +{
> +	struct device *dev = decoder->sd.dev;
> +	struct device_node *ep_np;
> +	struct v4l2_fwnode_endpoint bus_cfg = {
> +		.bus_type = V4L2_MBUS_UNKNOWN
> +	};
> +	unsigned int flags, ep_num;
> +	int ret;
> +
> +	/* At least 1 output and 1 input. */
> +	ep_num = of_graph_get_endpoint_count(np);
> +	if (ep_num < 2 || ep_num > 5) {
> +		dev_err(dev, "At least 1 input and 1 output must be connected to the device.\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Layout if all connectors are used
> +	 *
> +	 * tvp-5150 port@0 (AIP1A)
> +	 *	endpoint@0 -----------> Comp0-Con  port
> +	 *	endpoint@1 --------+--> Svideo-Con port
> +	 * tvp-5150 port@1 (AIP1B) |
> +	 *	endpoint@1 --------+
> +	 *	endpoint@0 -----------> Comp1-Con  port
> +	 * tvp-5150 port@2
> +	 *	endpoint (video bitstream output at YOUT[0-7] parallel bus)
> +	 */
> +	for_each_endpoint_of_node(np, ep_np) {
> +		struct of_endpoint ep;
> +
> +		of_graph_parse_endpoint(ep_np, &ep);
> +		if (ep.port > 1)
> +			continue;
> +
> +		ret = tvp5150_add_connector(decoder, np, ep.port, ep.id,
> +					    ep.id ? 1 : 0);
> +		if (ret) {
> +			of_node_put(ep_np);
> +			return ret;
> +		}
> +	}
> +
> +	ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0);
> +	if (!ep_np) {
> +		dev_err(dev, "Error no output endpoint available\n");
> +		return -EINVAL;
> +	}
> +	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_np), &bus_cfg);
> +	of_node_put(ep_np);
> +	if (ret)
> +		return ret;
> +
> +	flags = bus_cfg.bus.parallel.flags;
>  	if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL &&
>  	    !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH &&
>  	      flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH &&
>  	      flags & V4L2_MBUS_FIELD_EVEN_LOW)) {
> -		ret = -EINVAL;
> -		goto err;
> +		return -EINVAL;
>  	}
>  
>  	decoder->mbus_type = bus_cfg.bus_type;
>  
> -err:
> -	of_node_put(ep);
> -	return ret;
> +	return decoder->connectors_num ? 0 : -ENODEV;
>  }
>  
>  static const char * const tvp5150_test_patterns[2] = {
> @@ -1592,22 +1919,13 @@ static int tvp5150_probe(struct i2c_client *c)
>  	}
>  
>  	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
> +	sd->internal_ops = &tvp5150_internal_ops;
>  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>  
> -#if defined(CONFIG_MEDIA_CONTROLLER)
> -	core->pads[TVP5150_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
> -	core->pads[TVP5150_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG;
> -	core->pads[TVP5150_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
> -	core->pads[TVP5150_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV;
> -
> -	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
> -
> -	res = media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS, core->pads);
> -	if (res < 0)
> +	res = tvp5150_mc_init(core);
> +	if (res)
>  		return res;
>  
> -#endif
> -
>  	res = tvp5150_detect_version(core);
>  	if (res < 0)
>  		return res;
>
Hans Verkuil Sept. 27, 2019, 9:01 a.m. UTC | #3
On 8/30/19 12:16 PM, Marco Felsch wrote:
> Since commit 10d5509c8d50 ("[media] v4l2: remove g/s_crop from video ops")
> the 'which' field for set/get_selection must be FORMAT_ACTIVE. There is
> no way to try different selections. The patch adds a helper function to
> select the correct selection memory space (sub-device file handle or
> driver state) which will be set/returned.
> 
> The selection rectangle is updated if the format is FORMAT_ACTIVE and
> the rectangle position and/or size differs from the current set
> rectangle.
> 
> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>

One checkpatch warning:

CHECK: Alignment should match open parenthesis
#170: FILE: drivers/media/i2c/tvp5150.c:1155:
+               crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad,
+                                               sel->which);

I'd have fixed this myself, but since there are some changes needed
elsewhere, you can pick this up as well for a v11.

And some more comments below:

> ---
> Changelog:
> 
> v10:
> - __tvp5150_get_pad_crop: drop confusing fall-through
> - set_selection: fix FORMAT_TRY handling if CONFIG_VIDEO_V4L2_SUBDEV_API
>                  is disabled. Adapt sel->r and return 0.
> v8:
> - adapt commit message
> - remove wrong FORMAT_TRY handling for tvp5150_fill_fmt() handling
> - return 0 during set_selection if FORMAT_TRY was requested and
>   CONFIG_VIDEO_V4L2_SUBDEV_API is disabled
> - return -EINVAL during get_selection if FORMAT_TRY was requested and
>   CONFIG_VIDEO_V4L2_SUBDEV_API is disabled
> v7:
> - __tvp5150_get_pad_crop(): return error on default case
> - simplify __tvp5150_get_pad_crop() error handling
> - tvp5150_set_selection() squash __tvp5150_set_selection() execution
>   conditions
> v6:
> nothing
> v5:
>  - handle stub for v4l2_subdev_get_try_crop() internal since commit
>    ("media: v4l2-subdev: add stubs for v4l2_subdev_get_try_*")
>    isn't anymore part of this series.
>  - add error handling of __tvp5150_get_pad_crop()
> v4:
>  - fix merge conflict due to rebase on top of media-tree/master
>  - __tvp5150_get_pad_crop(): cosmetic alignment fixes
> 
>  drivers/media/i2c/tvp5150.c | 113 ++++++++++++++++++++++++++----------
>  1 file changed, 81 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> index c1542a89e8c8..636e8737ac44 100644
> --- a/drivers/media/i2c/tvp5150.c
> +++ b/drivers/media/i2c/tvp5150.c
> @@ -19,6 +19,7 @@
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-fwnode.h>
>  #include <media/v4l2-mc.h>
> +#include <media/v4l2-rect.h>
>  
>  #include "tvp5150_reg.h"
>  
> @@ -995,6 +996,25 @@ static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop)
>  		crop->height = TVP5150_V_MAX_OTHERS;
>  }
>  
> +static struct v4l2_rect *
> +__tvp5150_get_pad_crop(struct tvp5150 *decoder,
> +		       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
> +		       enum v4l2_subdev_format_whence which)

Please don't use __ in the function name. Double underscores should never be
used in C code since that's reserved for use by the compiler. What's wrong
with just plain tvp5150_get_pad_crop()?

> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &decoder->rect;
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
> +		return v4l2_subdev_get_try_crop(&decoder->sd, cfg, pad);
> +#else
> +		return ERR_PTR(-EINVAL);
> +#endif
> +	default:
> +		return ERR_PTR(-EINVAL);
> +	}
> +}
> +
>  static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
>  			    struct v4l2_subdev_pad_config *cfg,
>  			    struct v4l2_subdev_format *format)
> @@ -1019,17 +1039,51 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
>  	return 0;
>  }
>  
> +unsigned int tvp5150_get_hmax(struct v4l2_subdev *sd)
> +{
> +	struct tvp5150 *decoder = to_tvp5150(sd);
> +	v4l2_std_id std;
> +
> +	/* Calculate height based on current standard */
> +	if (decoder->norm == V4L2_STD_ALL)
> +		std = tvp5150_read_std(sd);
> +	else
> +		std = decoder->norm;
> +
> +	return (std & V4L2_STD_525_60) ?
> +		TVP5150_V_MAX_525_60 : TVP5150_V_MAX_OTHERS;
> +}
> +
> +static inline void
> +__tvp5150_set_selection(struct v4l2_subdev *sd, struct v4l2_rect *rect)

You can drop 'inline', there is no need for that.

I'd also rename the function to something more understandable. E.g.
tvp5150_set_hw_selection().

> +{
> +	struct tvp5150 *decoder = to_tvp5150(sd);
> +	unsigned int hmax = tvp5150_get_hmax(sd);
> +
> +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
> +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
> +		     rect->top + rect->height - hmax);
> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
> +		     rect->left >> TVP5150_CROP_SHIFT);
> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
> +		     rect->left | (1 << TVP5150_CROP_SHIFT));
> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
> +		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
> +		     TVP5150_CROP_SHIFT);
> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
> +		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
> +}
> +
>  static int tvp5150_set_selection(struct v4l2_subdev *sd,
>  				 struct v4l2_subdev_pad_config *cfg,
>  				 struct v4l2_subdev_selection *sel)
>  {
>  	struct tvp5150 *decoder = to_tvp5150(sd);
>  	struct v4l2_rect *rect = &sel->r;
> -	v4l2_std_id std;
> -	int hmax;
> +	struct v4l2_rect *crop;
> +	unsigned int hmax;
>  
> -	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
> -	    sel->target != V4L2_SEL_TGT_CROP)
> +	if (sel->target != V4L2_SEL_TGT_CROP)
>  		return -EINVAL;
>  
>  	dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
> @@ -1038,17 +1092,7 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>  	/* tvp5150 has some special limits */
>  	rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
>  	rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
> -
> -	/* Calculate height based on current standard */
> -	if (decoder->norm == V4L2_STD_ALL)
> -		std = tvp5150_read_std(sd);
> -	else
> -		std = decoder->norm;
> -
> -	if (std & V4L2_STD_525_60)
> -		hmax = TVP5150_V_MAX_525_60;
> -	else
> -		hmax = TVP5150_V_MAX_OTHERS;
> +	hmax = tvp5150_get_hmax(sd);
>  
>  	/*
>  	 * alignments:
> @@ -1061,20 +1105,23 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>  			      hmax - TVP5150_MAX_CROP_TOP - rect->top,
>  			      hmax - rect->top, 0, 0);
>  
> -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
> -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
> -		     rect->top + rect->height - hmax);
> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
> -		     rect->left >> TVP5150_CROP_SHIFT);
> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
> -		     rect->left | (1 << TVP5150_CROP_SHIFT));
> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
> -		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
> -		     TVP5150_CROP_SHIFT);
> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
> -		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
> +	if (!IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) &&
> +	    sel->which == V4L2_SUBDEV_FORMAT_TRY)
> +		return 0;
> +
> +	crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad, sel->which);
> +	if (IS_ERR(crop))
> +		return PTR_ERR(crop);
> +
> +	/*
> +	 * Update output image size if the selection (crop) rectangle size or
> +	 * position has been modified.
> +	 */
> +	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
> +	    !v4l2_rect_equal(rect, crop))
> +		__tvp5150_set_selection(sd, rect);
>  
> -	decoder->rect = *rect;
> +	*crop = *rect;
>  
>  	return 0;
>  }
> @@ -1084,11 +1131,9 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd,
>  				 struct v4l2_subdev_selection *sel)
>  {
>  	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
> +	struct v4l2_rect *crop;
>  	v4l2_std_id std;
>  
> -	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> -		return -EINVAL;
> -
>  	switch (sel->target) {
>  	case V4L2_SEL_TGT_CROP_BOUNDS:
>  		sel->r.left = 0;
> @@ -1106,7 +1151,11 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd,
>  			sel->r.height = TVP5150_V_MAX_OTHERS;
>  		return 0;
>  	case V4L2_SEL_TGT_CROP:
> -		sel->r = decoder->rect;
> +		crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad,
> +						sel->which);
> +		if (IS_ERR(crop))
> +			return PTR_ERR(crop);
> +		sel->r = *crop;
>  		return 0;
>  	default:
>  		return -EINVAL;
> 

Regards,

	Hans
Marco Felsch Sept. 27, 2019, 10:57 a.m. UTC | #4
Hi Hans,

On 19-09-27 11:01, Hans Verkuil wrote:
> On 8/30/19 12:16 PM, Marco Felsch wrote:
> > Since commit 10d5509c8d50 ("[media] v4l2: remove g/s_crop from video ops")
> > the 'which' field for set/get_selection must be FORMAT_ACTIVE. There is
> > no way to try different selections. The patch adds a helper function to
> > select the correct selection memory space (sub-device file handle or
> > driver state) which will be set/returned.
> > 
> > The selection rectangle is updated if the format is FORMAT_ACTIVE and
> > the rectangle position and/or size differs from the current set
> > rectangle.
> > 
> > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> 
> One checkpatch warning:
> 
> CHECK: Alignment should match open parenthesis
> #170: FILE: drivers/media/i2c/tvp5150.c:1155:
> +               crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad,
> +                                               sel->which);
> 
> I'd have fixed this myself, but since there are some changes needed
> elsewhere, you can pick this up as well for a v11.
> 
> And some more comments below:
> 
> > ---
> > Changelog:
> > 
> > v10:
> > - __tvp5150_get_pad_crop: drop confusing fall-through
> > - set_selection: fix FORMAT_TRY handling if CONFIG_VIDEO_V4L2_SUBDEV_API
> >                  is disabled. Adapt sel->r and return 0.
> > v8:
> > - adapt commit message
> > - remove wrong FORMAT_TRY handling for tvp5150_fill_fmt() handling
> > - return 0 during set_selection if FORMAT_TRY was requested and
> >   CONFIG_VIDEO_V4L2_SUBDEV_API is disabled
> > - return -EINVAL during get_selection if FORMAT_TRY was requested and
> >   CONFIG_VIDEO_V4L2_SUBDEV_API is disabled
> > v7:
> > - __tvp5150_get_pad_crop(): return error on default case
> > - simplify __tvp5150_get_pad_crop() error handling
> > - tvp5150_set_selection() squash __tvp5150_set_selection() execution
> >   conditions
> > v6:
> > nothing
> > v5:
> >  - handle stub for v4l2_subdev_get_try_crop() internal since commit
> >    ("media: v4l2-subdev: add stubs for v4l2_subdev_get_try_*")
> >    isn't anymore part of this series.
> >  - add error handling of __tvp5150_get_pad_crop()
> > v4:
> >  - fix merge conflict due to rebase on top of media-tree/master
> >  - __tvp5150_get_pad_crop(): cosmetic alignment fixes
> > 
> >  drivers/media/i2c/tvp5150.c | 113 ++++++++++++++++++++++++++----------
> >  1 file changed, 81 insertions(+), 32 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> > index c1542a89e8c8..636e8737ac44 100644
> > --- a/drivers/media/i2c/tvp5150.c
> > +++ b/drivers/media/i2c/tvp5150.c
> > @@ -19,6 +19,7 @@
> >  #include <media/v4l2-ctrls.h>
> >  #include <media/v4l2-fwnode.h>
> >  #include <media/v4l2-mc.h>
> > +#include <media/v4l2-rect.h>
> >  
> >  #include "tvp5150_reg.h"
> >  
> > @@ -995,6 +996,25 @@ static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop)
> >  		crop->height = TVP5150_V_MAX_OTHERS;
> >  }
> >  
> > +static struct v4l2_rect *
> > +__tvp5150_get_pad_crop(struct tvp5150 *decoder,
> > +		       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
> > +		       enum v4l2_subdev_format_whence which)
> 
> Please don't use __ in the function name. Double underscores should never be
> used in C code since that's reserved for use by the compiler. What's wrong
> with just plain tvp5150_get_pad_crop()?

I tought it is common at least within the v4l subsystem. There are a
few other drivers following that __ pattern. Anyway I can rename it ;)

> > +{
> > +	switch (which) {
> > +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> > +		return &decoder->rect;
> > +	case V4L2_SUBDEV_FORMAT_TRY:
> > +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
> > +		return v4l2_subdev_get_try_crop(&decoder->sd, cfg, pad);
> > +#else
> > +		return ERR_PTR(-EINVAL);
> > +#endif
> > +	default:
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +}
> > +
> >  static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
> >  			    struct v4l2_subdev_pad_config *cfg,
> >  			    struct v4l2_subdev_format *format)
> > @@ -1019,17 +1039,51 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
> >  	return 0;
> >  }
> >  
> > +unsigned int tvp5150_get_hmax(struct v4l2_subdev *sd)
> > +{
> > +	struct tvp5150 *decoder = to_tvp5150(sd);
> > +	v4l2_std_id std;
> > +
> > +	/* Calculate height based on current standard */
> > +	if (decoder->norm == V4L2_STD_ALL)
> > +		std = tvp5150_read_std(sd);
> > +	else
> > +		std = decoder->norm;
> > +
> > +	return (std & V4L2_STD_525_60) ?
> > +		TVP5150_V_MAX_525_60 : TVP5150_V_MAX_OTHERS;
> > +}
> > +
> > +static inline void
> > +__tvp5150_set_selection(struct v4l2_subdev *sd, struct v4l2_rect *rect)
> 
> You can drop 'inline', there is no need for that.
> 
> I'd also rename the function to something more understandable. E.g.
> tvp5150_set_hw_selection().

Okay I will change that.

Regards,
  Marco 

> 
> > +{
> > +	struct tvp5150 *decoder = to_tvp5150(sd);
> > +	unsigned int hmax = tvp5150_get_hmax(sd);
> > +
> > +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
> > +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
> > +		     rect->top + rect->height - hmax);
> > +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
> > +		     rect->left >> TVP5150_CROP_SHIFT);
> > +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
> > +		     rect->left | (1 << TVP5150_CROP_SHIFT));
> > +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
> > +		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
> > +		     TVP5150_CROP_SHIFT);
> > +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
> > +		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
> > +}
> > +
> >  static int tvp5150_set_selection(struct v4l2_subdev *sd,
> >  				 struct v4l2_subdev_pad_config *cfg,
> >  				 struct v4l2_subdev_selection *sel)
> >  {
> >  	struct tvp5150 *decoder = to_tvp5150(sd);
> >  	struct v4l2_rect *rect = &sel->r;
> > -	v4l2_std_id std;
> > -	int hmax;
> > +	struct v4l2_rect *crop;
> > +	unsigned int hmax;
> >  
> > -	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
> > -	    sel->target != V4L2_SEL_TGT_CROP)
> > +	if (sel->target != V4L2_SEL_TGT_CROP)
> >  		return -EINVAL;
> >  
> >  	dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
> > @@ -1038,17 +1092,7 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
> >  	/* tvp5150 has some special limits */
> >  	rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
> >  	rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
> > -
> > -	/* Calculate height based on current standard */
> > -	if (decoder->norm == V4L2_STD_ALL)
> > -		std = tvp5150_read_std(sd);
> > -	else
> > -		std = decoder->norm;
> > -
> > -	if (std & V4L2_STD_525_60)
> > -		hmax = TVP5150_V_MAX_525_60;
> > -	else
> > -		hmax = TVP5150_V_MAX_OTHERS;
> > +	hmax = tvp5150_get_hmax(sd);
> >  
> >  	/*
> >  	 * alignments:
> > @@ -1061,20 +1105,23 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
> >  			      hmax - TVP5150_MAX_CROP_TOP - rect->top,
> >  			      hmax - rect->top, 0, 0);
> >  
> > -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
> > -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
> > -		     rect->top + rect->height - hmax);
> > -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
> > -		     rect->left >> TVP5150_CROP_SHIFT);
> > -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
> > -		     rect->left | (1 << TVP5150_CROP_SHIFT));
> > -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
> > -		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
> > -		     TVP5150_CROP_SHIFT);
> > -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
> > -		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
> > +	if (!IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) &&
> > +	    sel->which == V4L2_SUBDEV_FORMAT_TRY)
> > +		return 0;
> > +
> > +	crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad, sel->which);
> > +	if (IS_ERR(crop))
> > +		return PTR_ERR(crop);
> > +
> > +	/*
> > +	 * Update output image size if the selection (crop) rectangle size or
> > +	 * position has been modified.
> > +	 */
> > +	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
> > +	    !v4l2_rect_equal(rect, crop))
> > +		__tvp5150_set_selection(sd, rect);
> >  
> > -	decoder->rect = *rect;
> > +	*crop = *rect;
> >  
> >  	return 0;
> >  }
> > @@ -1084,11 +1131,9 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd,
> >  				 struct v4l2_subdev_selection *sel)
> >  {
> >  	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
> > +	struct v4l2_rect *crop;
> >  	v4l2_std_id std;
> >  
> > -	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> > -		return -EINVAL;
> > -
> >  	switch (sel->target) {
> >  	case V4L2_SEL_TGT_CROP_BOUNDS:
> >  		sel->r.left = 0;
> > @@ -1106,7 +1151,11 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd,
> >  			sel->r.height = TVP5150_V_MAX_OTHERS;
> >  		return 0;
> >  	case V4L2_SEL_TGT_CROP:
> > -		sel->r = decoder->rect;
> > +		crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad,
> > +						sel->which);
> > +		if (IS_ERR(crop))
> > +			return PTR_ERR(crop);
> > +		sel->r = *crop;
> >  		return 0;
> >  	default:
> >  		return -EINVAL;
> > 
> 
> Regards,
> 
> 	Hans
>
Hans Verkuil Sept. 27, 2019, 11:05 a.m. UTC | #5
On 9/27/19 12:57 PM, Marco Felsch wrote:
> Hi Hans,
> 
> On 19-09-27 11:01, Hans Verkuil wrote:
>> On 8/30/19 12:16 PM, Marco Felsch wrote:
>>> Since commit 10d5509c8d50 ("[media] v4l2: remove g/s_crop from video ops")
>>> the 'which' field for set/get_selection must be FORMAT_ACTIVE. There is
>>> no way to try different selections. The patch adds a helper function to
>>> select the correct selection memory space (sub-device file handle or
>>> driver state) which will be set/returned.
>>>
>>> The selection rectangle is updated if the format is FORMAT_ACTIVE and
>>> the rectangle position and/or size differs from the current set
>>> rectangle.
>>>
>>> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
>>
>> One checkpatch warning:
>>
>> CHECK: Alignment should match open parenthesis
>> #170: FILE: drivers/media/i2c/tvp5150.c:1155:
>> +               crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad,
>> +                                               sel->which);
>>
>> I'd have fixed this myself, but since there are some changes needed
>> elsewhere, you can pick this up as well for a v11.
>>
>> And some more comments below:
>>
>>> ---
>>> Changelog:
>>>
>>> v10:
>>> - __tvp5150_get_pad_crop: drop confusing fall-through
>>> - set_selection: fix FORMAT_TRY handling if CONFIG_VIDEO_V4L2_SUBDEV_API
>>>                  is disabled. Adapt sel->r and return 0.
>>> v8:
>>> - adapt commit message
>>> - remove wrong FORMAT_TRY handling for tvp5150_fill_fmt() handling
>>> - return 0 during set_selection if FORMAT_TRY was requested and
>>>   CONFIG_VIDEO_V4L2_SUBDEV_API is disabled
>>> - return -EINVAL during get_selection if FORMAT_TRY was requested and
>>>   CONFIG_VIDEO_V4L2_SUBDEV_API is disabled
>>> v7:
>>> - __tvp5150_get_pad_crop(): return error on default case
>>> - simplify __tvp5150_get_pad_crop() error handling
>>> - tvp5150_set_selection() squash __tvp5150_set_selection() execution
>>>   conditions
>>> v6:
>>> nothing
>>> v5:
>>>  - handle stub for v4l2_subdev_get_try_crop() internal since commit
>>>    ("media: v4l2-subdev: add stubs for v4l2_subdev_get_try_*")
>>>    isn't anymore part of this series.
>>>  - add error handling of __tvp5150_get_pad_crop()
>>> v4:
>>>  - fix merge conflict due to rebase on top of media-tree/master
>>>  - __tvp5150_get_pad_crop(): cosmetic alignment fixes
>>>
>>>  drivers/media/i2c/tvp5150.c | 113 ++++++++++++++++++++++++++----------
>>>  1 file changed, 81 insertions(+), 32 deletions(-)
>>>
>>> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
>>> index c1542a89e8c8..636e8737ac44 100644
>>> --- a/drivers/media/i2c/tvp5150.c
>>> +++ b/drivers/media/i2c/tvp5150.c
>>> @@ -19,6 +19,7 @@
>>>  #include <media/v4l2-ctrls.h>
>>>  #include <media/v4l2-fwnode.h>
>>>  #include <media/v4l2-mc.h>
>>> +#include <media/v4l2-rect.h>
>>>  
>>>  #include "tvp5150_reg.h"
>>>  
>>> @@ -995,6 +996,25 @@ static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop)
>>>  		crop->height = TVP5150_V_MAX_OTHERS;
>>>  }
>>>  
>>> +static struct v4l2_rect *
>>> +__tvp5150_get_pad_crop(struct tvp5150 *decoder,
>>> +		       struct v4l2_subdev_pad_config *cfg, unsigned int pad,
>>> +		       enum v4l2_subdev_format_whence which)
>>
>> Please don't use __ in the function name. Double underscores should never be
>> used in C code since that's reserved for use by the compiler. What's wrong
>> with just plain tvp5150_get_pad_crop()?
> 
> I tought it is common at least within the v4l subsystem. There are a
> few other drivers following that __ pattern. Anyway I can rename it ;)

I think it is used too much in our code, generally for no good reason.

In this case there is no reason whatsoever that would warrant adding a __ prefix.

Regards,

	Hans

> 
>>> +{
>>> +	switch (which) {
>>> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
>>> +		return &decoder->rect;
>>> +	case V4L2_SUBDEV_FORMAT_TRY:
>>> +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
>>> +		return v4l2_subdev_get_try_crop(&decoder->sd, cfg, pad);
>>> +#else
>>> +		return ERR_PTR(-EINVAL);
>>> +#endif
>>> +	default:
>>> +		return ERR_PTR(-EINVAL);
>>> +	}
>>> +}
>>> +
>>>  static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
>>>  			    struct v4l2_subdev_pad_config *cfg,
>>>  			    struct v4l2_subdev_format *format)
>>> @@ -1019,17 +1039,51 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd,
>>>  	return 0;
>>>  }
>>>  
>>> +unsigned int tvp5150_get_hmax(struct v4l2_subdev *sd)
>>> +{
>>> +	struct tvp5150 *decoder = to_tvp5150(sd);
>>> +	v4l2_std_id std;
>>> +
>>> +	/* Calculate height based on current standard */
>>> +	if (decoder->norm == V4L2_STD_ALL)
>>> +		std = tvp5150_read_std(sd);
>>> +	else
>>> +		std = decoder->norm;
>>> +
>>> +	return (std & V4L2_STD_525_60) ?
>>> +		TVP5150_V_MAX_525_60 : TVP5150_V_MAX_OTHERS;
>>> +}
>>> +
>>> +static inline void
>>> +__tvp5150_set_selection(struct v4l2_subdev *sd, struct v4l2_rect *rect)
>>
>> You can drop 'inline', there is no need for that.
>>
>> I'd also rename the function to something more understandable. E.g.
>> tvp5150_set_hw_selection().
> 
> Okay I will change that.
> 
> Regards,
>   Marco 
> 
>>
>>> +{
>>> +	struct tvp5150 *decoder = to_tvp5150(sd);
>>> +	unsigned int hmax = tvp5150_get_hmax(sd);
>>> +
>>> +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
>>> +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
>>> +		     rect->top + rect->height - hmax);
>>> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
>>> +		     rect->left >> TVP5150_CROP_SHIFT);
>>> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
>>> +		     rect->left | (1 << TVP5150_CROP_SHIFT));
>>> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
>>> +		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
>>> +		     TVP5150_CROP_SHIFT);
>>> +	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
>>> +		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
>>> +}
>>> +
>>>  static int tvp5150_set_selection(struct v4l2_subdev *sd,
>>>  				 struct v4l2_subdev_pad_config *cfg,
>>>  				 struct v4l2_subdev_selection *sel)
>>>  {
>>>  	struct tvp5150 *decoder = to_tvp5150(sd);
>>>  	struct v4l2_rect *rect = &sel->r;
>>> -	v4l2_std_id std;
>>> -	int hmax;
>>> +	struct v4l2_rect *crop;
>>> +	unsigned int hmax;
>>>  
>>> -	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
>>> -	    sel->target != V4L2_SEL_TGT_CROP)
>>> +	if (sel->target != V4L2_SEL_TGT_CROP)
>>>  		return -EINVAL;
>>>  
>>>  	dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
>>> @@ -1038,17 +1092,7 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>>>  	/* tvp5150 has some special limits */
>>>  	rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
>>>  	rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
>>> -
>>> -	/* Calculate height based on current standard */
>>> -	if (decoder->norm == V4L2_STD_ALL)
>>> -		std = tvp5150_read_std(sd);
>>> -	else
>>> -		std = decoder->norm;
>>> -
>>> -	if (std & V4L2_STD_525_60)
>>> -		hmax = TVP5150_V_MAX_525_60;
>>> -	else
>>> -		hmax = TVP5150_V_MAX_OTHERS;
>>> +	hmax = tvp5150_get_hmax(sd);
>>>  
>>>  	/*
>>>  	 * alignments:
>>> @@ -1061,20 +1105,23 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>>>  			      hmax - TVP5150_MAX_CROP_TOP - rect->top,
>>>  			      hmax - rect->top, 0, 0);
>>>  
>>> -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
>>> -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
>>> -		     rect->top + rect->height - hmax);
>>> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
>>> -		     rect->left >> TVP5150_CROP_SHIFT);
>>> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
>>> -		     rect->left | (1 << TVP5150_CROP_SHIFT));
>>> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
>>> -		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
>>> -		     TVP5150_CROP_SHIFT);
>>> -	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
>>> -		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
>>> +	if (!IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) &&
>>> +	    sel->which == V4L2_SUBDEV_FORMAT_TRY)
>>> +		return 0;
>>> +
>>> +	crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad, sel->which);
>>> +	if (IS_ERR(crop))
>>> +		return PTR_ERR(crop);
>>> +
>>> +	/*
>>> +	 * Update output image size if the selection (crop) rectangle size or
>>> +	 * position has been modified.
>>> +	 */
>>> +	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
>>> +	    !v4l2_rect_equal(rect, crop))
>>> +		__tvp5150_set_selection(sd, rect);
>>>  
>>> -	decoder->rect = *rect;
>>> +	*crop = *rect;
>>>  
>>>  	return 0;
>>>  }
>>> @@ -1084,11 +1131,9 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd,
>>>  				 struct v4l2_subdev_selection *sel)
>>>  {
>>>  	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
>>> +	struct v4l2_rect *crop;
>>>  	v4l2_std_id std;
>>>  
>>> -	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
>>> -		return -EINVAL;
>>> -
>>>  	switch (sel->target) {
>>>  	case V4L2_SEL_TGT_CROP_BOUNDS:
>>>  		sel->r.left = 0;
>>> @@ -1106,7 +1151,11 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd,
>>>  			sel->r.height = TVP5150_V_MAX_OTHERS;
>>>  		return 0;
>>>  	case V4L2_SEL_TGT_CROP:
>>> -		sel->r = decoder->rect;
>>> +		crop = __tvp5150_get_pad_crop(decoder, cfg, sel->pad,
>>> +						sel->which);
>>> +		if (IS_ERR(crop))
>>> +			return PTR_ERR(crop);
>>> +		sel->r = *crop;
>>>  		return 0;
>>>  	default:
>>>  		return -EINVAL;
>>>
>>
>> Regards,
>>
>> 	Hans
>>
>
Marco Felsch Sept. 27, 2019, 12:09 p.m. UTC | #6
Hi Hans,

On 19-09-27 10:52, Hans Verkuil wrote:
> Hi Marco,
> 
> On 8/30/19 12:16 PM, Marco Felsch wrote:
> > The tvp5150 accepts NTSC(M,J,4.43), PAL (B,D,G,H,I,M,N) and SECAM video
> > data and is able to auto-detect the input signal. The auto-detection
> > does not work if the connector does not receive an input signal and the
> > tvp5150 might not be configured correctly. This misconfiguration leads
> > into wrong decoded video streams if the tvp5150 gets powered on before
> > the video signal is present.
> > 
> > Limit the supported sdtv standards according to the actual selected
> > connector to avoid a misconfiguration.
> > 
> > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> 
> checkpatch gave a bunch of warnings:
> 
> WARNING: line over 80 characters
> #97: FILE: drivers/media/i2c/tvp5150.c:1393:
> +                       decoder->cur_connector->base.connector.analog.sdtv_stds);
> 
> WARNING: line over 80 characters
> #107: FILE: drivers/media/i2c/tvp5150.c:1613:
> +                               decoder->connectors[i].base.connector.analog.sdtv_stds);
> 
> CHECK: Alignment should match open parenthesis
> #107: FILE: drivers/media/i2c/tvp5150.c:1613:
> +                       tvp5150_s_std(sd,
> +                               decoder->connectors[i].base.connector.analog.sdtv_stds);
> 
> I think it would be best to introduce a local pointer to
> decoder->connectors[i].base.connector.analog.

I fixed it, thanks.

Regards,
  Marco 

> 
> Regards,
> 
> 	Hans
> 
> > ---
> > [1] https://patchwork.kernel.org/cover/10794703/
> > 
> > v8:
> > - adapt commit message
> > - fix rebasing issue
> > - apdapt to new v4l2_fwnode_connector_analog naming
> > - fix cur_connector update during tvp5150_link_setup()
> >   -> Only update if we have of-connectors.
> > - fix supported_stds detection during tvp5150_s_std()
> >   -> use connectors_num to detect of-connectors presence
> > 
> > v5:
> > - probe() initialize supported tv-norms according the given connectors
> >   if they are available.
> > - check if media-controller is used. Don't limit the norm if it isn't
> >   used.
> > - add more logic to be smarter during connector changing so it is
> >   intuitiver for the user space.
> > 
> > v2-v4:
> > - nothing since the patch was squashed from series [1] into this
> >   series.
> > 
> >  drivers/media/i2c/tvp5150.c | 78 ++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 76 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> > index d7079809f33f..da7e7fe16f89 100644
> > --- a/drivers/media/i2c/tvp5150.c
> > +++ b/drivers/media/i2c/tvp5150.c
> > @@ -32,6 +32,13 @@
> >  #define TVP5150_MBUS_FMT	MEDIA_BUS_FMT_UYVY8_2X8
> >  #define TVP5150_FIELD		V4L2_FIELD_ALTERNATE
> >  #define TVP5150_COLORSPACE	V4L2_COLORSPACE_SMPTE170M
> > +#define TVP5150_STD_MASK	(V4L2_STD_NTSC     | \
> > +				 V4L2_STD_NTSC_443 | \
> > +				 V4L2_STD_PAL      | \
> > +				 V4L2_STD_PAL_M    | \
> > +				 V4L2_STD_PAL_N    | \
> > +				 V4L2_STD_PAL_Nc   | \
> > +				 V4L2_STD_SECAM)
> >  
> >  #define TVP5150_MAX_CONNECTORS	3 /* Check dt-bindings for more information */
> >  
> > @@ -64,6 +71,7 @@ struct tvp5150 {
> >  
> >  	struct media_pad pads[TVP5150_NUM_PADS];
> >  	struct tvp5150_connector connectors[TVP5150_MAX_CONNECTORS];
> > +	struct tvp5150_connector *cur_connector;
> >  	unsigned int connectors_num;
> >  
> >  	struct v4l2_ctrl_handler hdl;
> > @@ -783,17 +791,33 @@ static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
> >  static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
> >  {
> >  	struct tvp5150 *decoder = to_tvp5150(sd);
> > +	struct tvp5150_connector *cur_con = decoder->cur_connector;
> > +	v4l2_std_id supported_stds;
> >  
> >  	if (decoder->norm == std)
> >  		return 0;
> >  
> > +	/* In case of no of-connectors are available no limitations are made */
> > +	if (!decoder->connectors_num)
> > +		supported_stds = V4L2_STD_ALL;
> > +	else
> > +		supported_stds = cur_con->base.connector.analog.sdtv_stds;
> > +
> > +	/*
> > +	 * Check if requested std or group of std's is/are supported by the
> > +	 * connector.
> > +	 */
> > +	if ((supported_stds & std) == 0)
> > +		return -EINVAL;
> > +
> >  	/* Change cropping height limits */
> >  	if (std & V4L2_STD_525_60)
> >  		decoder->rect.height = TVP5150_V_MAX_525_60;
> >  	else
> >  		decoder->rect.height = TVP5150_V_MAX_OTHERS;
> >  
> > -	decoder->norm = std;
> > +	/* Set only the specific supported std in case of group of std's. */
> > +	decoder->norm = supported_stds & std;
> >  
> >  	return tvp5150_set_std(sd, std);
> >  }
> > @@ -1331,6 +1355,8 @@ static int tvp5150_link_setup(struct media_entity *entity,
> >  			  TVP5150_BLACK_SCREEN, 0);
> >  
> >  	if (flags & MEDIA_LNK_FL_ENABLED) {
> > +		u32 new_norm;
> > +
> >  		/*
> >  		 * S-Video connector is conneted to both ports AIP1A and AIP1B.
> >  		 * Both links must be enabled in one-shot regardless which link
> > @@ -1342,6 +1368,29 @@ static int tvp5150_link_setup(struct media_entity *entity,
> >  			if (err)
> >  				return err;
> >  		}
> > +
> > +		if (!decoder->connectors_num)
> > +			return 0;
> > +
> > +		/* Update the current connector */
> > +		decoder->cur_connector =
> > +			container_of(remote, struct tvp5150_connector, pad);
> > +
> > +		/*
> > +		 * Do nothing if the new connector supports the same tv-norms as
> > +		 * the old one.
> > +		 */
> > +		new_norm = decoder->norm &
> > +			decoder->cur_connector->base.connector.analog.sdtv_stds;
> > +		if (decoder->norm == new_norm)
> > +			return 0;
> > +
> > +		/*
> > +		 * Fallback to the new connector tv-norms if we can't find any
> > +		 * common between the current tv-norm and the new one.
> > +		 */
> > +		tvp5150_s_std(sd, new_norm ? new_norm :
> > +			decoder->cur_connector->base.connector.analog.sdtv_stds);
> >  	}
> >  
> >  	return 0;
> > @@ -1559,6 +1608,9 @@ static int tvp5150_registered(struct v4l2_subdev *sd)
> >  				TVP5150_COMPOSITE1;
> >  
> >  			tvp5150_selmux(sd);
> > +			decoder->cur_connector = &decoder->connectors[i];
> > +			tvp5150_s_std(sd,
> > +				decoder->connectors[i].base.connector.analog.sdtv_stds);
> >  		}
> >  	}
> >  
> > @@ -1848,6 +1900,13 @@ tvp5150_add_connector(struct tvp5150 *decoder, struct device_node *np,
> >  		goto err;
> >  	}
> >  
> > +	if (!(vc->connector.analog.sdtv_stds & TVP5150_STD_MASK)) {
> > +		dev_err(dev, "Unsupported tv-norm on connector %s\n",
> > +			remote_np->name);
> > +		ret = -EINVAL;
> > +		goto err;
> > +	}
> > +
> >  	c->ent.flags = MEDIA_ENT_FL_CONNECTOR;
> >  	c->ent.name = vc->label;
> >  	c->ent.name = kasprintf(GFP_KERNEL, "%s %s", remote_np->name,
> > @@ -1945,6 +2004,7 @@ static int tvp5150_probe(struct i2c_client *c)
> >  	struct v4l2_subdev *sd;
> >  	struct device_node *np = c->dev.of_node;
> >  	struct regmap *map;
> > +	unsigned int i;
> >  	int res;
> >  
> >  	/* Check if the adapter supports the needed features */
> > @@ -1989,7 +2049,21 @@ static int tvp5150_probe(struct i2c_client *c)
> >  	if (res < 0)
> >  		return res;
> >  
> > -	core->norm = V4L2_STD_ALL;	/* Default is autodetect */
> > +	/*
> > +	 * Iterate over all available connectors in case they are supported and
> > +	 * successfully parsed. Fallback to default autodetect in case they
> > +	 * aren't supported.
> > +	 */
> > +	for (i = 0; i < core->connectors_num; i++) {
> > +		struct v4l2_fwnode_connector *vc;
> > +
> > +		vc = &core->connectors[i].base;
> > +		core->norm |= vc->connector.analog.sdtv_stds;
> > +	}
> > +
> > +	if (!core->connectors_num)
> > +		core->norm = V4L2_STD_ALL;
> > +
> >  	core->detected_norm = V4L2_STD_UNKNOWN;
> >  	core->input = TVP5150_COMPOSITE1;
> >  	core->enable = true;
> > 
> 
>
Marco Felsch Sept. 27, 2019, 12:09 p.m. UTC | #7
Hi Hans,

On 19-09-27 10:54, Hans Verkuil wrote:
> On 8/30/19 12:16 PM, Marco Felsch wrote:
> > This patch adds the of_graph support to describe the tvp input connections.
> > Physical the TVP5150 has three ports: AIP1A, AIP1B and YOUT. As result
> > of discussion [1],[2] the device-tree maps these ports 1:1. Look at the
> > Documentation for more information. Since the TVP5150 is a converter/bridge
> > the device-tree must contain at least 1-input and 1-output port. The
> > mc-connectors and mc-links are only created if the device-tree contains the
> > corresponding connector nodes. If more than one connector is available the
> > media_entity_operations.link_setup() callback ensures that only one
> > connector is active.
> > 
> > [1] https://www.spinics.net/lists/linux-media/msg138545.html
> > [2] https://www.spinics.net/lists/linux-media/msg138546.html
> > 
> > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> 
> More checkpatch warnings:
> 
> WARNING: line over 80 characters
> #110: FILE: drivers/media/i2c/tvp5150.c:1244:
> +                       is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;
> 
> CHECK: No space is necessary after a cast
> #157: FILE: drivers/media/i2c/tvp5150.c:1291:
> +                       err = tvp5150_set_link((struct media_pad *) remote,
> 
> WARNING: line over 80 characters
> #192: FILE: drivers/media/i2c/tvp5150.c:1466:
> +               unsigned int port = decoder->connectors[i].base.links[0].remote_port;
> 
> WARNING: line over 80 characters
> #194: FILE: drivers/media/i2c/tvp5150.c:1468:
> +               bool is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;
> 
> Here too adding a local pointer to decoder->connectors[i].base would
> prevent these warnings, and make it more readable.

I fixed it, thanks.

Regards,
  Marco

> 
> Regards,
> 
> 	Hans
> 
> > ---
> > Changelog:
> > 
> > [1] https://patchwork.kernel.org/cover/10794703/
> > [2] https://patchwork.kernel.org/cover/10786553/
> > 
> > v8:
> > - fix rebasing issue
> > - fix error handling during tvp5150_registered()
> > - adapt to new v4l2_connector_type enum
> > 
> > v7:
> > - don't init enum tvp5150_pads with TVP5150_COMPOSITE0 functionality
> > - break some 80 character limitation to improve readability
> > - fix comment style -> always start with capital letters and end with dot
> > - fix some minor style issues
> > - fix tvp5150_registered error handling
> > - simplify tvp5150_mc_init
> > - make connectors static
> >   -> since now only three connectors are possible (as described in DT)
> >   -> drop tvp5150.endpoints storage
> >   -> squash tvp5150_parse_dt and tvp5150_add_of_connectors
> > - improve tvp5150_parse_dt:
> >   -> make parsing stricter and fix not detected missconfigured dt-data
> >   -> svideo must be connected now to port@[0,1]/endpoint@1
> > 
> > v6:
> > - fix misspelled comments
> > - use 'unsigned int' where it's possible
> > - cleanup ifdef part-2:
> >   * tvp5150_mc_init, tvp5150_add_of_connectors: add surrounding
> >     CONFIG_MEDIA_CONTROLLER if def and stubs to improve quality
> > - tvp5150_mc_init: uniform interface, use 'struct tvp5150' since all
> >   internal function do this.
> > - tvp5150_add_of_connectors: call within probe() to make it cleaner
> > - tvp5150_parse_dt: move local loop vars within the loop.
> > 
> > v5:
> > - Fixing build deps:
> >   - tvp5150_mc_init: fix CONFIG_MEDIA_CONTROLLER deps
> >   - struct tvp5150: drop CONFIG_MEDIA_CONTROLLER conditional property
> >     includes. This leads into to complex deps for futher development.
> >   - tvp5150_dt_cleanup: enable function only if CONFIG_OF is enabled
> >   - tvp5150_parse_dt: enable function only if CONFIG_OF is enabled
> >   - tvp5150_probe: call tvp5150_dt_cleanup only if CONFIG_OF is enabled
> > 
> > - Simplify link_setup routine:
> >   - use generic connector parsing since both series [1,2] are squashed into
> >     one
> >   - struct tvp5150: drop pads_state and modify_second_link property
> >     due to link_setup() rework.
> >   - tvp5150_link_setup: add more comments
> >   - tvp5150_link_setup: simply the link setup routine a lot. Edit the 2nd
> >     link directly within the driver instead of a recursive media-framework
> >     call (__media_entity_setup_link). This improves the readability and
> >     shrinks the driver code.
> >   - tvp5150_link_setup: disable all active links in case user switches
> >     connectors without disable it first.
> >   - tvp5150_registered: simplify default link enable path due to link_setup()
> >     rework.
> > 
> > - General cleanups
> >   - tvp5150_parse_dt: drop unecessary test
> >   - tvp5150_parse_dt: add err message due to misconfiguration
> >   - tvp5150_parse_dt: make use of V4L2_MBUS_UNKNOWN definition
> >   - s/dev_dbg/dev_dbg_lvl
> > 
> > v4:
> >  - rebase on top of media_tree/master, fix merge conflict due to commit
> >    60359a28d592 ("media: v4l: fwnode: Initialise the V4L2 fwnode endpoints
> >    to zero")
> > 
> > v3:
> > - probe(): s/err/err_free_v4l2_ctrls
> > - drop MC dependency for tvp5150_pads
> > 
> > v2:
> > - adapt commit message
> > - unify ifdef switches
> > - rename tvp5150_valid_input -> tvp5150_of_valid_input, to be more precise
> > - mc: use 2-input and 1-output pad
> > - mc: link svideo connector to both input pads
> > - mc: enable/disable svideo links in one go
> > - mc: change link_setup() behaviour, switch the input src don't require a
> >       explicite disable before.
> > - mc: rename 'local' media_pad param to tvp5150_pad to avoid confusion
> > - mc: enable link to the first available connector and set the
> >       corresponding tvp5150 input src per default during registered()
> > - mc/of: factor out oftree connector allocation
> > - of: drop svideo dt port
> > - of: move svideo connector to port@0/endpoint@1
> > - of: require at least 1-in and 1-out endpoint
> > 
> >  drivers/media/i2c/tvp5150.c | 378 +++++++++++++++++++++++++++++++++---
> >  1 file changed, 348 insertions(+), 30 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> > index 11a5fd7e2f58..477a929d4f89 100644
> > --- a/drivers/media/i2c/tvp5150.c
> > +++ b/drivers/media/i2c/tvp5150.c
> > @@ -32,6 +32,8 @@
> >  #define TVP5150_FIELD		V4L2_FIELD_ALTERNATE
> >  #define TVP5150_COLORSPACE	V4L2_COLORSPACE_SMPTE170M
> >  
> > +#define TVP5150_MAX_CONNECTORS	3 /* Check dt-bindings for more information */
> > +
> >  MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver");
> >  MODULE_AUTHOR("Mauro Carvalho Chehab");
> >  MODULE_LICENSE("GPL v2");
> > @@ -44,16 +46,25 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");
> >  #define dprintk0(__dev, __arg...) dev_dbg_lvl(__dev, 0, 0, __arg)
> >  
> >  enum tvp5150_pads {
> > -	TVP5150_PAD_IF_INPUT,
> > +	TVP5150_PAD_AIP1A,
> > +	TVP5150_PAD_AIP1B,
> >  	TVP5150_PAD_VID_OUT,
> >  	TVP5150_NUM_PADS
> >  };
> >  
> > +struct tvp5150_connector {
> > +	struct v4l2_fwnode_connector base;
> > +	struct media_entity ent;
> > +	struct media_pad pad;
> > +};
> > +
> >  struct tvp5150 {
> >  	struct v4l2_subdev sd;
> > -#ifdef CONFIG_MEDIA_CONTROLLER
> > +
> >  	struct media_pad pads[TVP5150_NUM_PADS];
> > -#endif
> > +	struct tvp5150_connector connectors[TVP5150_MAX_CONNECTORS];
> > +	unsigned int connectors_num;
> > +
> >  	struct v4l2_ctrl_handler hdl;
> >  	struct v4l2_rect rect;
> >  	struct regmap *regmap;
> > @@ -1167,6 +1178,130 @@ static int tvp5150_enum_frame_size(struct v4l2_subdev *sd,
> >  	return 0;
> >  }
> >  
> > +/****************************************************************************
> > + *			Media entity ops
> > + ****************************************************************************/
> > +#if defined(CONFIG_MEDIA_CONTROLLER)
> > +static int tvp5150_set_link(struct media_pad *connector_pad,
> > +			    struct media_pad *tvp5150_pad, u32 flags)
> > +{
> > +	struct media_link *link;
> > +
> > +	link = media_entity_find_link(connector_pad, tvp5150_pad);
> > +	if (!link)
> > +		return -EINVAL;
> > +
> > +	link->flags = flags;
> > +	link->reverse->flags = link->flags;
> > +
> > +	return 0;
> > +}
> > +
> > +static int tvp5150_disable_all_input_links(struct tvp5150 *decoder)
> > +{
> > +	struct media_pad *connector_pad;
> > +	unsigned int i;
> > +	int err;
> > +
> > +	for (i = 0; i < TVP5150_NUM_PADS - 1; i++) {
> > +		connector_pad = media_entity_remote_pad(&decoder->pads[i]);
> > +		if (!connector_pad)
> > +			continue;
> > +
> > +		err = tvp5150_set_link(connector_pad, &decoder->pads[i], 0);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int tvp5150_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
> > +			     u32 config);
> > +
> > +static int tvp5150_link_setup(struct media_entity *entity,
> > +			      const struct media_pad *tvp5150_pad,
> > +			      const struct media_pad *remote, u32 flags)
> > +{
> > +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> > +	struct tvp5150 *decoder = to_tvp5150(sd);
> > +	struct media_pad *other_tvp5150_pad =
> > +		&decoder->pads[tvp5150_pad->index ^ 1];
> > +	bool is_svideo = false;
> > +	unsigned int i;
> > +	int err;
> > +
> > +	/*
> > +	 * The TVP5150 state is determined by the enabled sink pad link(s).
> > +	 * Enabling or disabling the source pad link has no effect.
> > +	 */
> > +	if (tvp5150_pad->flags & MEDIA_PAD_FL_SOURCE)
> > +		return 0;
> > +
> > +	/* Check if the svideo connector should be enabled */
> > +	for (i = 0; i < decoder->connectors_num; i++) {
> > +		if (remote->entity == &decoder->connectors[i].ent) {
> > +			is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;
> > +			break;
> > +		}
> > +	}
> > +
> > +	dev_dbg_lvl(sd->dev, 1, debug, "link setup '%s':%d->'%s':%d[%d]",
> > +		    remote->entity->name, remote->index,
> > +		    tvp5150_pad->entity->name, tvp5150_pad->index,
> > +		    flags & MEDIA_LNK_FL_ENABLED);
> > +	if (is_svideo)
> > +		dev_dbg_lvl(sd->dev, 1, debug,
> > +			    "link setup '%s':%d->'%s':%d[%d]",
> > +			    remote->entity->name, remote->index,
> > +			    other_tvp5150_pad->entity->name,
> > +			    other_tvp5150_pad->index,
> > +			    flags & MEDIA_LNK_FL_ENABLED);
> > +
> > +	/*
> > +	 * The TVP5150 has an internal mux which allows the following setup:
> > +	 *
> > +	 * comp-connector1  --\
> > +	 *		       |---> AIP1A
> > +	 *		      /
> > +	 * svideo-connector -|
> > +	 *		      \
> > +	 *		       |---> AIP1B
> > +	 * comp-connector2  --/
> > +	 *
> > +	 * We can't rely on user space that the current connector gets disabled
> > +	 * first before enabling the new connector. Disable all active
> > +	 * connector links to be on the safe side.
> > +	 */
> > +	err = tvp5150_disable_all_input_links(decoder);
> > +	if (err)
> > +		return err;
> > +
> > +	tvp5150_s_routing(sd, is_svideo ? TVP5150_SVIDEO : tvp5150_pad->index,
> > +			  flags & MEDIA_LNK_FL_ENABLED ? TVP5150_NORMAL :
> > +			  TVP5150_BLACK_SCREEN, 0);
> > +
> > +	if (flags & MEDIA_LNK_FL_ENABLED) {
> > +		/*
> > +		 * S-Video connector is conneted to both ports AIP1A and AIP1B.
> > +		 * Both links must be enabled in one-shot regardless which link
> > +		 * the user requests.
> > +		 */
> > +		if (is_svideo) {
> > +			err = tvp5150_set_link((struct media_pad *) remote,
> > +					       other_tvp5150_pad, flags);
> > +			if (err)
> > +				return err;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct media_entity_operations tvp5150_sd_media_ops = {
> > +	.link_setup = tvp5150_link_setup,
> > +};
> > +#endif
> >  /****************************************************************************
> >  			I2C Command
> >   ****************************************************************************/
> > @@ -1314,6 +1449,71 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
> >  	return 0;
> >  }
> >  
> > +static int tvp5150_registered(struct v4l2_subdev *sd)
> > +{
> > +#if defined(CONFIG_MEDIA_CONTROLLER)
> > +	struct tvp5150 *decoder = to_tvp5150(sd);
> > +	unsigned int i;
> > +	int ret;
> > +
> > +	/*
> > +	 * Setup connector pads and links. Enable the link to the first
> > +	 * available connector per default.
> > +	 */
> > +	for (i = 0; i < decoder->connectors_num; i++) {
> > +		struct media_entity *con = &decoder->connectors[i].ent;
> > +		struct media_pad *pad = &decoder->connectors[i].pad;
> > +		unsigned int port = decoder->connectors[i].base.links[0].remote_port;
> > +		unsigned int flags = i ? 0 : MEDIA_LNK_FL_ENABLED;
> > +		bool is_svideo = decoder->connectors[i].base.type == V4L2_CONN_SVIDEO;
> > +
> > +		pad->flags = MEDIA_PAD_FL_SOURCE;
> > +		ret = media_entity_pads_init(con, 1, pad);
> > +		if (ret < 0)
> > +			goto err;
> > +
> > +		ret = media_device_register_entity(sd->v4l2_dev->mdev, con);
> > +		if (ret < 0)
> > +			goto err;
> > +
> > +		ret = media_create_pad_link(con, 0, &sd->entity, port, flags);
> > +		if (ret < 0)
> > +			goto err;
> > +
> > +		if (is_svideo) {
> > +			/*
> > +			 * Check tvp5150_link_setup() comments for more
> > +			 * information.
> > +			 */
> > +			port = decoder->connectors[i].base.links[1].remote_port;
> > +			ret = media_create_pad_link(con, 0, &sd->entity, port,
> > +						    flags);
> > +			if (ret < 0)
> > +				goto err;
> > +		}
> > +
> > +		/* Enable default input. */
> > +		if (flags == MEDIA_LNK_FL_ENABLED) {
> > +			decoder->input =
> > +				is_svideo ? TVP5150_SVIDEO :
> > +				port == 0 ? TVP5150_COMPOSITE0 :
> > +				TVP5150_COMPOSITE1;
> > +
> > +			tvp5150_selmux(sd);
> > +		}
> > +	}
> > +
> > +	return 0;
> > +
> > +err:
> > +	for (i = 0; i < decoder->connectors_num; i++)
> > +		media_device_unregister_entity(&decoder->connectors[i].ent);
> > +	return ret;
> > +#endif
> > +
> > +	return 0;
> > +}
> > +
> >  /* ----------------------------------------------------------------------- */
> >  
> >  static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
> > @@ -1367,6 +1567,10 @@ static const struct v4l2_subdev_ops tvp5150_ops = {
> >  	.pad = &tvp5150_pad_ops,
> >  };
> >  
> > +static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = {
> > +	.registered = tvp5150_registered,
> > +};
> > +
> >  /****************************************************************************
> >  			I2C Client & Driver
> >   ****************************************************************************/
> > @@ -1515,36 +1719,159 @@ static int tvp5150_init(struct i2c_client *c)
> >  	return 0;
> >  }
> >  
> > -static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
> > +#if defined(CONFIG_MEDIA_CONTROLLER)
> > +static int tvp5150_mc_init(struct tvp5150 *decoder)
> >  {
> > -	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
> > -	struct device_node *ep;
> > -	unsigned int flags;
> > -	int ret = 0;
> > +	struct v4l2_subdev *sd = &decoder->sd;
> > +	unsigned int i;
> >  
> > -	ep = of_graph_get_next_endpoint(np, NULL);
> > -	if (!ep)
> > -		return -EINVAL;
> > +	sd->entity.ops = &tvp5150_sd_media_ops;
> > +	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
> > +
> > +	for (i = 0; i < TVP5150_NUM_PADS - 1; i++) {
> > +		decoder->pads[i].flags = MEDIA_PAD_FL_SINK;
> > +		decoder->pads[i].sig_type = PAD_SIGNAL_ANALOG;
> > +	}
> > +
> > +	decoder->pads[i].flags = MEDIA_PAD_FL_SOURCE;
> > +	decoder->pads[i].sig_type = PAD_SIGNAL_DV;
> >  
> > -	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
> > +	return media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS,
> > +				      decoder->pads);
> > +}
> > +
> > +#else /* !defined(CONFIG_MEDIA_CONTROLLER) */
> > +
> > +static inline int tvp5150_mc_init(struct tvp5150 *decoder)
> > +{
> > +	return 0;
> > +}
> > +#endif /* defined(CONFIG_MEDIA_CONTROLLER) */
> > +
> > +static int
> > +tvp5150_add_connector(struct tvp5150 *decoder, struct device_node *np,
> > +		      unsigned int portn, unsigned int epn, unsigned int svideo)
> > +{
> > +	struct device *dev = decoder->sd.dev;
> > +	struct device_node *ep_np, *remote_np;
> > +	struct v4l2_fwnode_connector *vc;
> > +	struct tvp5150_connector *c;
> > +	unsigned int next_connector = decoder->connectors_num;
> > +	int ret;
> > +
> > +	/* Get free connector entry. */
> > +	if (next_connector >= TVP5150_MAX_CONNECTORS)
> > +		return 0;
> > +
> > +	c = &decoder->connectors[next_connector];
> > +	vc = &c->base;
> > +	ep_np = of_graph_get_endpoint_by_regs(np, portn, epn);
> > +	remote_np = of_graph_get_remote_port_parent(ep_np);
> > +
> > +	ret = v4l2_fwnode_connector_alloc_parse(of_fwnode_handle(ep_np), vc);
> >  	if (ret)
> >  		goto err;
> >  
> > -	flags = bus_cfg.bus.parallel.flags;
> > +	if ((!svideo && vc->type != V4L2_CONN_COMPOSITE) ||
> > +	    (svideo && vc->type != V4L2_CONN_SVIDEO)) {
> > +		dev_err(dev, "Invalid connector type for port@%u/endpoint@%u\n",
> > +			portn, epn);
> > +		ret = -EINVAL;
> > +		goto err;
> > +	}
> >  
> > +	if (svideo &&
> > +	    (vc->nr_of_links != 2 ||
> > +	     vc->links[0].remote_port == vc->links[1].remote_port)) {
> > +		dev_err(dev, "Invalid Svideo connector configuration\n");
> > +		ret = -EINVAL;
> > +		goto err;
> > +	}
> > +
> > +	c->ent.flags = MEDIA_ENT_FL_CONNECTOR;
> > +	c->ent.name = vc->label;
> > +	c->ent.name = kasprintf(GFP_KERNEL, "%s %s", remote_np->name,
> > +				vc->label ? vc->label : "");
> > +	c->ent.function = vc->type == V4L2_CONN_SVIDEO ?
> > +		MEDIA_ENT_F_CONN_SVIDEO : MEDIA_ENT_F_CONN_COMPOSITE;
> > +
> > +	decoder->connectors_num++;
> > +
> > +	of_node_put(remote_np);
> > +	of_node_put(ep_np);
> > +	return 0;
> > +
> > +err:
> > +	of_node_put(remote_np);
> > +	of_node_put(ep_np);
> > +	v4l2_fwnode_connector_free(vc);
> > +	return ret;
> > +}
> > +
> > +static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np)
> > +{
> > +	struct device *dev = decoder->sd.dev;
> > +	struct device_node *ep_np;
> > +	struct v4l2_fwnode_endpoint bus_cfg = {
> > +		.bus_type = V4L2_MBUS_UNKNOWN
> > +	};
> > +	unsigned int flags, ep_num;
> > +	int ret;
> > +
> > +	/* At least 1 output and 1 input. */
> > +	ep_num = of_graph_get_endpoint_count(np);
> > +	if (ep_num < 2 || ep_num > 5) {
> > +		dev_err(dev, "At least 1 input and 1 output must be connected to the device.\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* Layout if all connectors are used
> > +	 *
> > +	 * tvp-5150 port@0 (AIP1A)
> > +	 *	endpoint@0 -----------> Comp0-Con  port
> > +	 *	endpoint@1 --------+--> Svideo-Con port
> > +	 * tvp-5150 port@1 (AIP1B) |
> > +	 *	endpoint@1 --------+
> > +	 *	endpoint@0 -----------> Comp1-Con  port
> > +	 * tvp-5150 port@2
> > +	 *	endpoint (video bitstream output at YOUT[0-7] parallel bus)
> > +	 */
> > +	for_each_endpoint_of_node(np, ep_np) {
> > +		struct of_endpoint ep;
> > +
> > +		of_graph_parse_endpoint(ep_np, &ep);
> > +		if (ep.port > 1)
> > +			continue;
> > +
> > +		ret = tvp5150_add_connector(decoder, np, ep.port, ep.id,
> > +					    ep.id ? 1 : 0);
> > +		if (ret) {
> > +			of_node_put(ep_np);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0);
> > +	if (!ep_np) {
> > +		dev_err(dev, "Error no output endpoint available\n");
> > +		return -EINVAL;
> > +	}
> > +	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_np), &bus_cfg);
> > +	of_node_put(ep_np);
> > +	if (ret)
> > +		return ret;
> > +
> > +	flags = bus_cfg.bus.parallel.flags;
> >  	if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL &&
> >  	    !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH &&
> >  	      flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH &&
> >  	      flags & V4L2_MBUS_FIELD_EVEN_LOW)) {
> > -		ret = -EINVAL;
> > -		goto err;
> > +		return -EINVAL;
> >  	}
> >  
> >  	decoder->mbus_type = bus_cfg.bus_type;
> >  
> > -err:
> > -	of_node_put(ep);
> > -	return ret;
> > +	return decoder->connectors_num ? 0 : -ENODEV;
> >  }
> >  
> >  static const char * const tvp5150_test_patterns[2] = {
> > @@ -1592,22 +1919,13 @@ static int tvp5150_probe(struct i2c_client *c)
> >  	}
> >  
> >  	v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
> > +	sd->internal_ops = &tvp5150_internal_ops;
> >  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> >  
> > -#if defined(CONFIG_MEDIA_CONTROLLER)
> > -	core->pads[TVP5150_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
> > -	core->pads[TVP5150_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG;
> > -	core->pads[TVP5150_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
> > -	core->pads[TVP5150_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV;
> > -
> > -	sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
> > -
> > -	res = media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS, core->pads);
> > -	if (res < 0)
> > +	res = tvp5150_mc_init(core);
> > +	if (res)
> >  		return res;
> >  
> > -#endif
> > -
> >  	res = tvp5150_detect_version(core);
> >  	if (res < 0)
> >  		return res;
> > 
> 
>
Marco Felsch Sept. 27, 2019, 12:13 p.m. UTC | #8
Hi Hans,

since you review contained many checkpatch issues I wanna ask if it okay
to keep this line as it before I will send a v11. Please check my inline
comment.

On 19-08-30 12:16, Marco Felsch wrote:
> Currently a local copy of sel->r is made and adapted to the hardware
> constraints. After the adaption the value is applied to the hardware but
> the driver forgot to reflect the adapted value to the user space.
> 
> Drop the local copy and work directly on the requested rectangle
> instead to fix this.
> 
> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> ---
> 
> v10:
> - new patch
> 
>  drivers/media/i2c/tvp5150.c | 32 ++++++++++++++++----------------
>  1 file changed, 16 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
> index 477a929d4f89..c1542a89e8c8 100644
> --- a/drivers/media/i2c/tvp5150.c
> +++ b/drivers/media/i2c/tvp5150.c
> @@ -1024,7 +1024,7 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>  				 struct v4l2_subdev_selection *sel)
>  {
>  	struct tvp5150 *decoder = to_tvp5150(sd);
> -	struct v4l2_rect rect = sel->r;
> +	struct v4l2_rect *rect = &sel->r;
>  	v4l2_std_id std;
>  	int hmax;
>  
> @@ -1033,11 +1033,11 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>  		return -EINVAL;
>  
>  	dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
> -		__func__, rect.left, rect.top, rect.width, rect.height);
> +		__func__, rect->left, rect->top, rect->width, rect->height);
>  
>  	/* tvp5150 has some special limits */
> -	rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
> -	rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
> +	rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
> +	rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
>  
>  	/* Calculate height based on current standard */
>  	if (decoder->norm == V4L2_STD_ALL)
> @@ -1055,26 +1055,26 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>  	 *  - width = 2 due to UYVY colorspace
>  	 *  - height, image = no special alignment
>  	 */
> -	v4l_bound_align_image(&rect.width,
> -			      TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
> -			      TVP5150_H_MAX - rect.left, 1, &rect.height,
> -			      hmax - TVP5150_MAX_CROP_TOP - rect.top,
> -			      hmax - rect.top, 0, 0);
> +	v4l_bound_align_image(&rect->width,
> +			      TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect->left,

Now checkpatch complains about this line because it is 81 characters
long. Is it okay to keep this as single line for readability?

Regards,
  Marco


> +			      TVP5150_H_MAX - rect->left, 1, &rect->height,
> +			      hmax - TVP5150_MAX_CROP_TOP - rect->top,
> +			      hmax - rect->top, 0, 0);
>  
> -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect.top);
> +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
>  	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
> -		     rect.top + rect.height - hmax);
> +		     rect->top + rect->height - hmax);
>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
> -		     rect.left >> TVP5150_CROP_SHIFT);
> +		     rect->left >> TVP5150_CROP_SHIFT);
>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
> -		     rect.left | (1 << TVP5150_CROP_SHIFT));
> +		     rect->left | (1 << TVP5150_CROP_SHIFT));
>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
> -		     (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
> +		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
>  		     TVP5150_CROP_SHIFT);
>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
> -		     rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
> +		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
>  
> -	decoder->rect = rect;
> +	decoder->rect = *rect;
>  
>  	return 0;
>  }
> -- 
> 2.20.1
> 
>
Hans Verkuil Sept. 27, 2019, 12:14 p.m. UTC | #9
On 9/27/19 2:13 PM, Marco Felsch wrote:
> Hi Hans,
> 
> since you review contained many checkpatch issues I wanna ask if it okay
> to keep this line as it before I will send a v11. Please check my inline
> comment.
> 
> On 19-08-30 12:16, Marco Felsch wrote:
>> Currently a local copy of sel->r is made and adapted to the hardware
>> constraints. After the adaption the value is applied to the hardware but
>> the driver forgot to reflect the adapted value to the user space.
>>
>> Drop the local copy and work directly on the requested rectangle
>> instead to fix this.
>>
>> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
>> ---
>>
>> v10:
>> - new patch
>>
>>  drivers/media/i2c/tvp5150.c | 32 ++++++++++++++++----------------
>>  1 file changed, 16 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
>> index 477a929d4f89..c1542a89e8c8 100644
>> --- a/drivers/media/i2c/tvp5150.c
>> +++ b/drivers/media/i2c/tvp5150.c
>> @@ -1024,7 +1024,7 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>>  				 struct v4l2_subdev_selection *sel)
>>  {
>>  	struct tvp5150 *decoder = to_tvp5150(sd);
>> -	struct v4l2_rect rect = sel->r;
>> +	struct v4l2_rect *rect = &sel->r;
>>  	v4l2_std_id std;
>>  	int hmax;
>>  
>> @@ -1033,11 +1033,11 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>>  		return -EINVAL;
>>  
>>  	dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n",
>> -		__func__, rect.left, rect.top, rect.width, rect.height);
>> +		__func__, rect->left, rect->top, rect->width, rect->height);
>>  
>>  	/* tvp5150 has some special limits */
>> -	rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
>> -	rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
>> +	rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT);
>> +	rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP);
>>  
>>  	/* Calculate height based on current standard */
>>  	if (decoder->norm == V4L2_STD_ALL)
>> @@ -1055,26 +1055,26 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
>>  	 *  - width = 2 due to UYVY colorspace
>>  	 *  - height, image = no special alignment
>>  	 */
>> -	v4l_bound_align_image(&rect.width,
>> -			      TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
>> -			      TVP5150_H_MAX - rect.left, 1, &rect.height,
>> -			      hmax - TVP5150_MAX_CROP_TOP - rect.top,
>> -			      hmax - rect.top, 0, 0);
>> +	v4l_bound_align_image(&rect->width,
>> +			      TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect->left,
> 
> Now checkpatch complains about this line because it is 81 characters
> long. Is it okay to keep this as single line for readability?

Yes, in this case splitting it up just makes it harder to read, so keep it as-is.

Regards,

	Hans

> 
> Regards,
>   Marco
> 
> 
>> +			      TVP5150_H_MAX - rect->left, 1, &rect->height,
>> +			      hmax - TVP5150_MAX_CROP_TOP - rect->top,
>> +			      hmax - rect->top, 0, 0);
>>  
>> -	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect.top);
>> +	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top);
>>  	regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP,
>> -		     rect.top + rect.height - hmax);
>> +		     rect->top + rect->height - hmax);
>>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB,
>> -		     rect.left >> TVP5150_CROP_SHIFT);
>> +		     rect->left >> TVP5150_CROP_SHIFT);
>>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB,
>> -		     rect.left | (1 << TVP5150_CROP_SHIFT));
>> +		     rect->left | (1 << TVP5150_CROP_SHIFT));
>>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB,
>> -		     (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
>> +		     (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >>
>>  		     TVP5150_CROP_SHIFT);
>>  	regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB,
>> -		     rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
>> +		     rect->left + rect->width - TVP5150_MAX_CROP_LEFT);
>>  
>> -	decoder->rect = rect;
>> +	decoder->rect = *rect;
>>  
>>  	return 0;
>>  }
>> -- 
>> 2.20.1
>>
>>
>
Sakari Ailus Oct. 2, 2019, 7:03 a.m. UTC | #10
Hi Marco,

On Fri, Aug 30, 2019 at 12:16:35PM +0200, Marco Felsch wrote:
> The patch adds the initial connector parsing code, so we can move from a
> driver specific parsing code to a generic one. Currently only the
> generic fields and the analog-connector specific fields are parsed. Parsing
> the other connector specific fields can be added by a simple callbacks.
> 
> Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> ---
> [1] https://patchwork.kernel.org/cover/10794703/
> 
> v10:
> - drop V4L2_CONN_HDMI support
> - adapt pr_err msg to reflect new state (-> connector is unkown)
> 
> v9:
> - Fix leading semicolon found by kbuild semicolon.cocci
> 
> v8:
> - V4L2_CON_* -> V4L2_CONN_*
> - tvnorms -> sdtv-standards
> - adapt to new v4l2_fwnode_connector_analog member
> - return error in case of V4L2_CONN_HDMI
> 
> v7:
> @Jacopo: I dropped your r b tag becuase of the amount of changes I
> made..
> 
> - drop unnecessary comments
> - fix commet style
> - s/v4l2_fwnode_connector_conv.name/v4l2_fwnode_connector_conv.compatible/
> - make label size variable and drop V4L2_CONNECTOR_MAX_LABEL usage
> - do not assign a default label in case of no label was specified
> - remove useless /* fall through */ comments
> - add support for N connector links
> - rename local variables to be more meaningful
> - adjust kernedoc
> - add v4l2_fwnode_connector_free()
> - improve error handling (use different error values)
> - make use of pr_warn_once()
> 
> v6:
> - use unsigned count var
> - fix comment and style issues
> - place '/* fall through */' to correct places
> - fix error handling and cleanup by releasing fwnode
> - drop vga and dvi parsing support as those connectors are rarely used
>   these days
> 
> v5:
> - s/strlcpy/strscpy/
> 
> v2-v4:
> - nothing since the patch was squashed from series [1] into this
>   series.
> 
>  drivers/media/v4l2-core/v4l2-fwnode.c | 129 ++++++++++++++++++++++++++
>  include/media/v4l2-fwnode.h           |  38 ++++++++
>  2 files changed, 167 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> index 3bd1888787eb..0bfa7cbf78df 100644
> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> @@ -595,6 +595,135 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
>  }
>  EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
>  
> +static const struct v4l2_fwnode_connector_conv {
> +	enum v4l2_connector_type type;
> +	const char *compatible;
> +} connectors[] = {
> +	{
> +		.type = V4L2_CONN_COMPOSITE,
> +		.compatible = "composite-video-connector",
> +	}, {
> +		.type = V4L2_CONN_SVIDEO,
> +		.compatible = "svideo-connector",
> +	},
> +};
> +
> +static enum v4l2_connector_type
> +v4l2_fwnode_string_to_connector_type(const char *con_str)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(connectors); i++)
> +		if (!strcmp(con_str, connectors[i].compatible))
> +			return connectors[i].type;
> +
> +	return V4L2_CONN_UNKNOWN;
> +}
> +
> +static int
> +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
> +				   struct v4l2_fwnode_connector *vc)
> +{
> +	u32 stds;
> +	int ret;
> +
> +	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
> +
> +	/* The property is optional. */
> +	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
> +
> +	return 0;
> +}
> +
> +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
> +{
> +	unsigned int i;
> +
> +	if (IS_ERR_OR_NULL(connector))
> +		return;
> +
> +	for (i = 0; i < connector->nr_of_links; i++)
> +		v4l2_fwnode_put_link(&connector->links[i]);
> +	kfree(connector->links);

Please assign connector->links NULL here, and nr_of_links to zero.

> +}
> +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
> +
> +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> +				      struct v4l2_fwnode_connector *connector)
> +{
> +	struct fwnode_handle *remote_pp, *remote_ep;
> +	const char *type_name;
> +	unsigned int i = 0, ep_num = 0;
> +	int err;
> +
> +	memset(connector, 0, sizeof(*connector));
> +
> +	remote_pp = fwnode_graph_get_remote_port_parent(fwnode);

How do you know a remote endpoint is a connector, and not another device's
endpoint?

> +	if (!remote_pp)
> +		return -ENOLINK;
> +
> +	/* Parse all common properties first. */
> +	fwnode_graph_for_each_endpoint(remote_pp, remote_ep)
> +		ep_num++;
> +
> +	connector->nr_of_links = ep_num;
> +	connector->links = kmalloc_array(ep_num, sizeof(*connector->links),
> +					 GFP_KERNEL);
> +	if (!connector->links) {
> +		err = -ENOMEM;
> +		goto err_put_fwnode;
> +	}
> +
> +	fwnode_graph_for_each_endpoint(remote_pp, remote_ep) {
> +		err = v4l2_fwnode_parse_link(remote_ep, &connector->links[i]);
> +		if (err) {
> +			fwnode_handle_put(remote_ep);
> +			goto err_free_links;
> +		}
> +		i++;
> +	}
> +
> +	/*
> +	 * Links reference counting guarantees access -> no duplication needed
> +	 */
> +	fwnode_property_read_string(remote_pp, "label", &connector->label);
> +
> +	/* The connector-type is stored within the compatible string. */
> +	err = fwnode_property_read_string(remote_pp, "compatible", &type_name);
> +	if (err) {
> +		err = -EINVAL;
> +		goto err_free_links;
> +	}
> +	connector->type = v4l2_fwnode_string_to_connector_type(type_name);
> +
> +	/* Now parse the connector specific properties. */
> +	switch (connector->type) {
> +	case V4L2_CONN_COMPOSITE:
> +	case V4L2_CONN_SVIDEO:
> +		err = v4l2_fwnode_connector_parse_analog(remote_pp, connector);
> +		break;
> +	case V4L2_CONN_UNKNOWN:
> +	default:
> +		pr_err("Unknown connector type\n");
> +		err = -EINVAL;
> +		goto err_free_links;
> +	}
> +
> +	fwnode_handle_put(remote_pp);
> +
> +	return err;
> +
> +err_free_links:
> +	for (i = 0; i < ep_num; i++)
> +		v4l2_fwnode_put_link(&connector->links[i]);
> +	kfree(connector->links);
> +err_put_fwnode:
> +	fwnode_handle_put(remote_pp);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_alloc_parse);
> +
>  static int
>  v4l2_async_notifier_fwnode_parse_endpoint(struct device *dev,
>  					  struct v4l2_async_notifier *notifier,
> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> index 65da646b579e..800302aa84d8 100644
> --- a/include/media/v4l2-fwnode.h
> +++ b/include/media/v4l2-fwnode.h
> @@ -276,6 +276,44 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode,
>   */
>  void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link);
>  
> +/**
> + * v4l2_fwnode_connector_free() - free the V4L2 connector acquired by
> + * v4l2_fwnode_parse_connector()
> + * @connector: the V4L2 connector the resources of which are to be released
> + *
> + * Drop references to the connection link partners and free the memory allocated
> + * by v4l2_fwnode_parse_connector() call.
> + *
> + * It is safe to call this function with NULL argument or on a V4L2 connector
> + * the parsing of which failed.
> + */
> +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector);
> +
> +/**
> + * v4l2_fwnode_parse_connector() - parse the connector on endpoint
> + * @fwnode: pointer to the endpoint's fwnode handle where the connector is
> + *          connected to
> + * @connector: pointer to the V4L2 fwnode connector data structure
> + *
> + * Fill the connector data structure with the connector type, label, all found
> + * links between the host and the connector as well as connector specific data.
> + * Since the label is optional it can be %NULL if no one was found.
> + *
> + * A reference is taken to both the connector and the connector destination
> + * where the connector belongs to. This must be dropped when no longer needed.
> + * Also the memory it has allocated to store the variable size data must be
> + * freed. Both dropping the references and freeing the memory is done by
> + * v4l2_fwnode_connector_free().
> + *
> + * Return:
> + * * %0 on success or a negative error code on failure:
> + * * %-ENOMEM if memory allocation failed
> + * * %-ENOLINK if the connector can't be found
> + * * %-EINVAL on parsing failure
> + */
> +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> +				      struct v4l2_fwnode_connector *connector);
> +
>  /**
>   * typedef parse_endpoint_func - Driver's callback function to be called on
>   *	each V4L2 fwnode endpoint.
Marco Felsch Oct. 2, 2019, 8:07 a.m. UTC | #11
Hi Sakari,

On 19-10-02 10:03, Sakari Ailus wrote:
> Hi Marco,
> 
> On Fri, Aug 30, 2019 at 12:16:35PM +0200, Marco Felsch wrote:
> > The patch adds the initial connector parsing code, so we can move from a
> > driver specific parsing code to a generic one. Currently only the
> > generic fields and the analog-connector specific fields are parsed. Parsing
> > the other connector specific fields can be added by a simple callbacks.
> > 
> > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> > ---
> > [1] https://patchwork.kernel.org/cover/10794703/
> > 
> > v10:
> > - drop V4L2_CONN_HDMI support
> > - adapt pr_err msg to reflect new state (-> connector is unkown)
> > 
> > v9:
> > - Fix leading semicolon found by kbuild semicolon.cocci
> > 
> > v8:
> > - V4L2_CON_* -> V4L2_CONN_*
> > - tvnorms -> sdtv-standards
> > - adapt to new v4l2_fwnode_connector_analog member
> > - return error in case of V4L2_CONN_HDMI
> > 
> > v7:
> > @Jacopo: I dropped your r b tag becuase of the amount of changes I
> > made..
> > 
> > - drop unnecessary comments
> > - fix commet style
> > - s/v4l2_fwnode_connector_conv.name/v4l2_fwnode_connector_conv.compatible/
> > - make label size variable and drop V4L2_CONNECTOR_MAX_LABEL usage
> > - do not assign a default label in case of no label was specified
> > - remove useless /* fall through */ comments
> > - add support for N connector links
> > - rename local variables to be more meaningful
> > - adjust kernedoc
> > - add v4l2_fwnode_connector_free()
> > - improve error handling (use different error values)
> > - make use of pr_warn_once()
> > 
> > v6:
> > - use unsigned count var
> > - fix comment and style issues
> > - place '/* fall through */' to correct places
> > - fix error handling and cleanup by releasing fwnode
> > - drop vga and dvi parsing support as those connectors are rarely used
> >   these days
> > 
> > v5:
> > - s/strlcpy/strscpy/
> > 
> > v2-v4:
> > - nothing since the patch was squashed from series [1] into this
> >   series.
> > 
> >  drivers/media/v4l2-core/v4l2-fwnode.c | 129 ++++++++++++++++++++++++++
> >  include/media/v4l2-fwnode.h           |  38 ++++++++
> >  2 files changed, 167 insertions(+)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> > index 3bd1888787eb..0bfa7cbf78df 100644
> > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > @@ -595,6 +595,135 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
> >  }
> >  EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
> >  
> > +static const struct v4l2_fwnode_connector_conv {
> > +	enum v4l2_connector_type type;
> > +	const char *compatible;
> > +} connectors[] = {
> > +	{
> > +		.type = V4L2_CONN_COMPOSITE,
> > +		.compatible = "composite-video-connector",
> > +	}, {
> > +		.type = V4L2_CONN_SVIDEO,
> > +		.compatible = "svideo-connector",
> > +	},
> > +};
> > +
> > +static enum v4l2_connector_type
> > +v4l2_fwnode_string_to_connector_type(const char *con_str)
> > +{
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(connectors); i++)
> > +		if (!strcmp(con_str, connectors[i].compatible))
> > +			return connectors[i].type;
> > +
> > +	return V4L2_CONN_UNKNOWN;
> > +}
> > +
> > +static int
> > +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
> > +				   struct v4l2_fwnode_connector *vc)
> > +{
> > +	u32 stds;
> > +	int ret;
> > +
> > +	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
> > +
> > +	/* The property is optional. */
> > +	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
> > +
> > +	return 0;
> > +}
> > +
> > +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
> > +{
> > +	unsigned int i;
> > +
> > +	if (IS_ERR_OR_NULL(connector))
> > +		return;
> > +
> > +	for (i = 0; i < connector->nr_of_links; i++)
> > +		v4l2_fwnode_put_link(&connector->links[i]);
> > +	kfree(connector->links);
> 
> Please assign connector->links NULL here, and nr_of_links to zero.

Okay, I can do that.

> > +}
> > +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
> > +
> > +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> > +				      struct v4l2_fwnode_connector *connector)
> > +{
> > +	struct fwnode_handle *remote_pp, *remote_ep;
> > +	const char *type_name;
> > +	unsigned int i = 0, ep_num = 0;
> > +	int err;
> > +
> > +	memset(connector, 0, sizeof(*connector));
> > +
> > +	remote_pp = fwnode_graph_get_remote_port_parent(fwnode);
> 
> How do you know a remote endpoint is a connector, and not another device's
> endpoint?

Well, I think that the caller won't use this function if it isn't a
connector. If it helps I can check if the compatible of the remote ends
with "-connector".

Regards,
  Marco

> > +	if (!remote_pp)
> > +		return -ENOLINK;
> > +
> > +	/* Parse all common properties first. */
> > +	fwnode_graph_for_each_endpoint(remote_pp, remote_ep)
> > +		ep_num++;
> > +
> > +	connector->nr_of_links = ep_num;
> > +	connector->links = kmalloc_array(ep_num, sizeof(*connector->links),
> > +					 GFP_KERNEL);
> > +	if (!connector->links) {
> > +		err = -ENOMEM;
> > +		goto err_put_fwnode;
> > +	}
> > +
> > +	fwnode_graph_for_each_endpoint(remote_pp, remote_ep) {
> > +		err = v4l2_fwnode_parse_link(remote_ep, &connector->links[i]);
> > +		if (err) {
> > +			fwnode_handle_put(remote_ep);
> > +			goto err_free_links;
> > +		}
> > +		i++;
> > +	}
> > +
> > +	/*
> > +	 * Links reference counting guarantees access -> no duplication needed
> > +	 */
> > +	fwnode_property_read_string(remote_pp, "label", &connector->label);
> > +
> > +	/* The connector-type is stored within the compatible string. */
> > +	err = fwnode_property_read_string(remote_pp, "compatible", &type_name);
> > +	if (err) {
> > +		err = -EINVAL;
> > +		goto err_free_links;
> > +	}
> > +	connector->type = v4l2_fwnode_string_to_connector_type(type_name);
> > +
> > +	/* Now parse the connector specific properties. */
> > +	switch (connector->type) {
> > +	case V4L2_CONN_COMPOSITE:
> > +	case V4L2_CONN_SVIDEO:
> > +		err = v4l2_fwnode_connector_parse_analog(remote_pp, connector);
> > +		break;
> > +	case V4L2_CONN_UNKNOWN:
> > +	default:
> > +		pr_err("Unknown connector type\n");
> > +		err = -EINVAL;
> > +		goto err_free_links;
> > +	}
> > +
> > +	fwnode_handle_put(remote_pp);
> > +
> > +	return err;
> > +
> > +err_free_links:
> > +	for (i = 0; i < ep_num; i++)
> > +		v4l2_fwnode_put_link(&connector->links[i]);
> > +	kfree(connector->links);
> > +err_put_fwnode:
> > +	fwnode_handle_put(remote_pp);
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_alloc_parse);
> > +
> >  static int
> >  v4l2_async_notifier_fwnode_parse_endpoint(struct device *dev,
> >  					  struct v4l2_async_notifier *notifier,
> > diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> > index 65da646b579e..800302aa84d8 100644
> > --- a/include/media/v4l2-fwnode.h
> > +++ b/include/media/v4l2-fwnode.h
> > @@ -276,6 +276,44 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode,
> >   */
> >  void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link);
> >  
> > +/**
> > + * v4l2_fwnode_connector_free() - free the V4L2 connector acquired by
> > + * v4l2_fwnode_parse_connector()
> > + * @connector: the V4L2 connector the resources of which are to be released
> > + *
> > + * Drop references to the connection link partners and free the memory allocated
> > + * by v4l2_fwnode_parse_connector() call.
> > + *
> > + * It is safe to call this function with NULL argument or on a V4L2 connector
> > + * the parsing of which failed.
> > + */
> > +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector);
> > +
> > +/**
> > + * v4l2_fwnode_parse_connector() - parse the connector on endpoint
> > + * @fwnode: pointer to the endpoint's fwnode handle where the connector is
> > + *          connected to
> > + * @connector: pointer to the V4L2 fwnode connector data structure
> > + *
> > + * Fill the connector data structure with the connector type, label, all found
> > + * links between the host and the connector as well as connector specific data.
> > + * Since the label is optional it can be %NULL if no one was found.
> > + *
> > + * A reference is taken to both the connector and the connector destination
> > + * where the connector belongs to. This must be dropped when no longer needed.
> > + * Also the memory it has allocated to store the variable size data must be
> > + * freed. Both dropping the references and freeing the memory is done by
> > + * v4l2_fwnode_connector_free().
> > + *
> > + * Return:
> > + * * %0 on success or a negative error code on failure:
> > + * * %-ENOMEM if memory allocation failed
> > + * * %-ENOLINK if the connector can't be found
> > + * * %-EINVAL on parsing failure
> > + */
> > +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> > +				      struct v4l2_fwnode_connector *connector);
> > +
> >  /**
> >   * typedef parse_endpoint_func - Driver's callback function to be called on
> >   *	each V4L2 fwnode endpoint.
> 
> -- 
> Kind regards,
> 
> Sakari Ailus
>
Marco Felsch Oct. 22, 2019, 10:17 a.m. UTC | #12
Hi Sakari,

gentle ping.

On 19-10-02 10:07, Marco Felsch wrote:
> Hi Sakari,
> 
> On 19-10-02 10:03, Sakari Ailus wrote:
> > Hi Marco,
> > 
> > On Fri, Aug 30, 2019 at 12:16:35PM +0200, Marco Felsch wrote:
> > > The patch adds the initial connector parsing code, so we can move from a
> > > driver specific parsing code to a generic one. Currently only the
> > > generic fields and the analog-connector specific fields are parsed. Parsing
> > > the other connector specific fields can be added by a simple callbacks.
> > > 
> > > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> > > ---
> > > [1] https://patchwork.kernel.org/cover/10794703/
> > > 
> > > v10:
> > > - drop V4L2_CONN_HDMI support
> > > - adapt pr_err msg to reflect new state (-> connector is unkown)
> > > 
> > > v9:
> > > - Fix leading semicolon found by kbuild semicolon.cocci
> > > 
> > > v8:
> > > - V4L2_CON_* -> V4L2_CONN_*
> > > - tvnorms -> sdtv-standards
> > > - adapt to new v4l2_fwnode_connector_analog member
> > > - return error in case of V4L2_CONN_HDMI
> > > 
> > > v7:
> > > @Jacopo: I dropped your r b tag becuase of the amount of changes I
> > > made..
> > > 
> > > - drop unnecessary comments
> > > - fix commet style
> > > - s/v4l2_fwnode_connector_conv.name/v4l2_fwnode_connector_conv.compatible/
> > > - make label size variable and drop V4L2_CONNECTOR_MAX_LABEL usage
> > > - do not assign a default label in case of no label was specified
> > > - remove useless /* fall through */ comments
> > > - add support for N connector links
> > > - rename local variables to be more meaningful
> > > - adjust kernedoc
> > > - add v4l2_fwnode_connector_free()
> > > - improve error handling (use different error values)
> > > - make use of pr_warn_once()
> > > 
> > > v6:
> > > - use unsigned count var
> > > - fix comment and style issues
> > > - place '/* fall through */' to correct places
> > > - fix error handling and cleanup by releasing fwnode
> > > - drop vga and dvi parsing support as those connectors are rarely used
> > >   these days
> > > 
> > > v5:
> > > - s/strlcpy/strscpy/
> > > 
> > > v2-v4:
> > > - nothing since the patch was squashed from series [1] into this
> > >   series.
> > > 
> > >  drivers/media/v4l2-core/v4l2-fwnode.c | 129 ++++++++++++++++++++++++++
> > >  include/media/v4l2-fwnode.h           |  38 ++++++++
> > >  2 files changed, 167 insertions(+)
> > > 
> > > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > index 3bd1888787eb..0bfa7cbf78df 100644
> > > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > @@ -595,6 +595,135 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
> > >  }
> > >  EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
> > >  
> > > +static const struct v4l2_fwnode_connector_conv {
> > > +	enum v4l2_connector_type type;
> > > +	const char *compatible;
> > > +} connectors[] = {
> > > +	{
> > > +		.type = V4L2_CONN_COMPOSITE,
> > > +		.compatible = "composite-video-connector",
> > > +	}, {
> > > +		.type = V4L2_CONN_SVIDEO,
> > > +		.compatible = "svideo-connector",
> > > +	},
> > > +};
> > > +
> > > +static enum v4l2_connector_type
> > > +v4l2_fwnode_string_to_connector_type(const char *con_str)
> > > +{
> > > +	unsigned int i;
> > > +
> > > +	for (i = 0; i < ARRAY_SIZE(connectors); i++)
> > > +		if (!strcmp(con_str, connectors[i].compatible))
> > > +			return connectors[i].type;
> > > +
> > > +	return V4L2_CONN_UNKNOWN;
> > > +}
> > > +
> > > +static int
> > > +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
> > > +				   struct v4l2_fwnode_connector *vc)
> > > +{
> > > +	u32 stds;
> > > +	int ret;
> > > +
> > > +	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
> > > +
> > > +	/* The property is optional. */
> > > +	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
> > > +{
> > > +	unsigned int i;
> > > +
> > > +	if (IS_ERR_OR_NULL(connector))
> > > +		return;
> > > +
> > > +	for (i = 0; i < connector->nr_of_links; i++)
> > > +		v4l2_fwnode_put_link(&connector->links[i]);
> > > +	kfree(connector->links);
> > 
> > Please assign connector->links NULL here, and nr_of_links to zero.
> 
> Okay, I can do that.
> 
> > > +}
> > > +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
> > > +
> > > +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> > > +				      struct v4l2_fwnode_connector *connector)
> > > +{
> > > +	struct fwnode_handle *remote_pp, *remote_ep;
> > > +	const char *type_name;
> > > +	unsigned int i = 0, ep_num = 0;
> > > +	int err;
> > > +
> > > +	memset(connector, 0, sizeof(*connector));
> > > +
> > > +	remote_pp = fwnode_graph_get_remote_port_parent(fwnode);
> > 
> > How do you know a remote endpoint is a connector, and not another device's
> > endpoint?
> 
> Well, I think that the caller won't use this function if it isn't a
> connector. If it helps I can check if the compatible of the remote ends
> with "-connector".
> 
> Regards,
>   Marco
Sakari Ailus Oct. 23, 2019, 10:57 a.m. UTC | #13
Hi Marco,

Apologies for the delay.

On Wed, Oct 02, 2019 at 10:07:35AM +0200, Marco Felsch wrote:
> Hi Sakari,
> 
> On 19-10-02 10:03, Sakari Ailus wrote:
> > Hi Marco,
> > 
> > On Fri, Aug 30, 2019 at 12:16:35PM +0200, Marco Felsch wrote:
> > > The patch adds the initial connector parsing code, so we can move from a
> > > driver specific parsing code to a generic one. Currently only the
> > > generic fields and the analog-connector specific fields are parsed. Parsing
> > > the other connector specific fields can be added by a simple callbacks.
> > > 
> > > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> > > ---
> > > [1] https://patchwork.kernel.org/cover/10794703/
> > > 
> > > v10:
> > > - drop V4L2_CONN_HDMI support
> > > - adapt pr_err msg to reflect new state (-> connector is unkown)
> > > 
> > > v9:
> > > - Fix leading semicolon found by kbuild semicolon.cocci
> > > 
> > > v8:
> > > - V4L2_CON_* -> V4L2_CONN_*
> > > - tvnorms -> sdtv-standards
> > > - adapt to new v4l2_fwnode_connector_analog member
> > > - return error in case of V4L2_CONN_HDMI
> > > 
> > > v7:
> > > @Jacopo: I dropped your r b tag becuase of the amount of changes I
> > > made..
> > > 
> > > - drop unnecessary comments
> > > - fix commet style
> > > - s/v4l2_fwnode_connector_conv.name/v4l2_fwnode_connector_conv.compatible/
> > > - make label size variable and drop V4L2_CONNECTOR_MAX_LABEL usage
> > > - do not assign a default label in case of no label was specified
> > > - remove useless /* fall through */ comments
> > > - add support for N connector links
> > > - rename local variables to be more meaningful
> > > - adjust kernedoc
> > > - add v4l2_fwnode_connector_free()
> > > - improve error handling (use different error values)
> > > - make use of pr_warn_once()
> > > 
> > > v6:
> > > - use unsigned count var
> > > - fix comment and style issues
> > > - place '/* fall through */' to correct places
> > > - fix error handling and cleanup by releasing fwnode
> > > - drop vga and dvi parsing support as those connectors are rarely used
> > >   these days
> > > 
> > > v5:
> > > - s/strlcpy/strscpy/
> > > 
> > > v2-v4:
> > > - nothing since the patch was squashed from series [1] into this
> > >   series.
> > > 
> > >  drivers/media/v4l2-core/v4l2-fwnode.c | 129 ++++++++++++++++++++++++++
> > >  include/media/v4l2-fwnode.h           |  38 ++++++++
> > >  2 files changed, 167 insertions(+)
> > > 
> > > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > index 3bd1888787eb..0bfa7cbf78df 100644
> > > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > @@ -595,6 +595,135 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
> > >  }
> > >  EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
> > >  
> > > +static const struct v4l2_fwnode_connector_conv {
> > > +	enum v4l2_connector_type type;
> > > +	const char *compatible;
> > > +} connectors[] = {
> > > +	{
> > > +		.type = V4L2_CONN_COMPOSITE,
> > > +		.compatible = "composite-video-connector",
> > > +	}, {
> > > +		.type = V4L2_CONN_SVIDEO,
> > > +		.compatible = "svideo-connector",
> > > +	},
> > > +};
> > > +
> > > +static enum v4l2_connector_type
> > > +v4l2_fwnode_string_to_connector_type(const char *con_str)
> > > +{
> > > +	unsigned int i;
> > > +
> > > +	for (i = 0; i < ARRAY_SIZE(connectors); i++)
> > > +		if (!strcmp(con_str, connectors[i].compatible))
> > > +			return connectors[i].type;
> > > +
> > > +	return V4L2_CONN_UNKNOWN;
> > > +}
> > > +
> > > +static int
> > > +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
> > > +				   struct v4l2_fwnode_connector *vc)
> > > +{
> > > +	u32 stds;
> > > +	int ret;
> > > +
> > > +	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
> > > +
> > > +	/* The property is optional. */
> > > +	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
> > > +{
> > > +	unsigned int i;
> > > +
> > > +	if (IS_ERR_OR_NULL(connector))
> > > +		return;
> > > +
> > > +	for (i = 0; i < connector->nr_of_links; i++)
> > > +		v4l2_fwnode_put_link(&connector->links[i]);
> > > +	kfree(connector->links);
> > 
> > Please assign connector->links NULL here, and nr_of_links to zero.
> 
> Okay, I can do that.
> 
> > > +}
> > > +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
> > > +
> > > +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> > > +				      struct v4l2_fwnode_connector *connector)
> > > +{
> > > +	struct fwnode_handle *remote_pp, *remote_ep;
> > > +	const char *type_name;
> > > +	unsigned int i = 0, ep_num = 0;
> > > +	int err;
> > > +
> > > +	memset(connector, 0, sizeof(*connector));
> > > +
> > > +	remote_pp = fwnode_graph_get_remote_port_parent(fwnode);
> > 
> > How do you know a remote endpoint is a connector, and not another device's
> > endpoint?
> 
> Well, I think that the caller won't use this function if it isn't a
> connector. If it helps I can check if the compatible of the remote ends
> with "-connector".

The function is called by a driver. A driver shouldn't know what's at the
other end of the graph arc; the information should come from the firmware
instead.

On some board there could be another device where you have a connector now.

As the connector has its own compatible string, there could be a connector
driver to tell this is actually a connector, even if there's nothing to
control. It'd be a very tiny driver.
Marco Felsch Oct. 23, 2019, 12:21 p.m. UTC | #14
Hi Sakari,

On 19-10-23 13:57, Sakari Ailus wrote:
> Hi Marco,
> 
> Apologies for the delay.

No problem.

> On Wed, Oct 02, 2019 at 10:07:35AM +0200, Marco Felsch wrote:
> > Hi Sakari,
> > 
> > On 19-10-02 10:03, Sakari Ailus wrote:
> > > Hi Marco,
> > > 
> > > On Fri, Aug 30, 2019 at 12:16:35PM +0200, Marco Felsch wrote:
> > > > The patch adds the initial connector parsing code, so we can move from a
> > > > driver specific parsing code to a generic one. Currently only the
> > > > generic fields and the analog-connector specific fields are parsed. Parsing
> > > > the other connector specific fields can be added by a simple callbacks.
> > > > 
> > > > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> > > > ---
> > > > [1] https://patchwork.kernel.org/cover/10794703/
> > > > 
> > > > v10:
> > > > - drop V4L2_CONN_HDMI support
> > > > - adapt pr_err msg to reflect new state (-> connector is unkown)
> > > > 
> > > > v9:
> > > > - Fix leading semicolon found by kbuild semicolon.cocci
> > > > 
> > > > v8:
> > > > - V4L2_CON_* -> V4L2_CONN_*
> > > > - tvnorms -> sdtv-standards
> > > > - adapt to new v4l2_fwnode_connector_analog member
> > > > - return error in case of V4L2_CONN_HDMI
> > > > 
> > > > v7:
> > > > @Jacopo: I dropped your r b tag becuase of the amount of changes I
> > > > made..
> > > > 
> > > > - drop unnecessary comments
> > > > - fix commet style
> > > > - s/v4l2_fwnode_connector_conv.name/v4l2_fwnode_connector_conv.compatible/
> > > > - make label size variable and drop V4L2_CONNECTOR_MAX_LABEL usage
> > > > - do not assign a default label in case of no label was specified
> > > > - remove useless /* fall through */ comments
> > > > - add support for N connector links
> > > > - rename local variables to be more meaningful
> > > > - adjust kernedoc
> > > > - add v4l2_fwnode_connector_free()
> > > > - improve error handling (use different error values)
> > > > - make use of pr_warn_once()
> > > > 
> > > > v6:
> > > > - use unsigned count var
> > > > - fix comment and style issues
> > > > - place '/* fall through */' to correct places
> > > > - fix error handling and cleanup by releasing fwnode
> > > > - drop vga and dvi parsing support as those connectors are rarely used
> > > >   these days
> > > > 
> > > > v5:
> > > > - s/strlcpy/strscpy/
> > > > 
> > > > v2-v4:
> > > > - nothing since the patch was squashed from series [1] into this
> > > >   series.
> > > > 
> > > >  drivers/media/v4l2-core/v4l2-fwnode.c | 129 ++++++++++++++++++++++++++
> > > >  include/media/v4l2-fwnode.h           |  38 ++++++++
> > > >  2 files changed, 167 insertions(+)
> > > > 
> > > > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > index 3bd1888787eb..0bfa7cbf78df 100644
> > > > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > @@ -595,6 +595,135 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
> > > >  }
> > > >  EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
> > > >  
> > > > +static const struct v4l2_fwnode_connector_conv {
> > > > +	enum v4l2_connector_type type;
> > > > +	const char *compatible;
> > > > +} connectors[] = {
> > > > +	{
> > > > +		.type = V4L2_CONN_COMPOSITE,
> > > > +		.compatible = "composite-video-connector",
> > > > +	}, {
> > > > +		.type = V4L2_CONN_SVIDEO,
> > > > +		.compatible = "svideo-connector",
> > > > +	},
> > > > +};
> > > > +
> > > > +static enum v4l2_connector_type
> > > > +v4l2_fwnode_string_to_connector_type(const char *con_str)
> > > > +{
> > > > +	unsigned int i;
> > > > +
> > > > +	for (i = 0; i < ARRAY_SIZE(connectors); i++)
> > > > +		if (!strcmp(con_str, connectors[i].compatible))
> > > > +			return connectors[i].type;
> > > > +
> > > > +	return V4L2_CONN_UNKNOWN;
> > > > +}
> > > > +
> > > > +static int
> > > > +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
> > > > +				   struct v4l2_fwnode_connector *vc)
> > > > +{
> > > > +	u32 stds;
> > > > +	int ret;
> > > > +
> > > > +	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
> > > > +
> > > > +	/* The property is optional. */
> > > > +	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
> > > > +{
> > > > +	unsigned int i;
> > > > +
> > > > +	if (IS_ERR_OR_NULL(connector))
> > > > +		return;
> > > > +
> > > > +	for (i = 0; i < connector->nr_of_links; i++)
> > > > +		v4l2_fwnode_put_link(&connector->links[i]);
> > > > +	kfree(connector->links);
> > > 
> > > Please assign connector->links NULL here, and nr_of_links to zero.
> > 
> > Okay, I can do that.
> > 
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
> > > > +
> > > > +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> > > > +				      struct v4l2_fwnode_connector *connector)
> > > > +{
> > > > +	struct fwnode_handle *remote_pp, *remote_ep;
> > > > +	const char *type_name;
> > > > +	unsigned int i = 0, ep_num = 0;
> > > > +	int err;
> > > > +
> > > > +	memset(connector, 0, sizeof(*connector));
> > > > +
> > > > +	remote_pp = fwnode_graph_get_remote_port_parent(fwnode);
> > > 
> > > How do you know a remote endpoint is a connector, and not another device's
> > > endpoint?
> > 
> > Well, I think that the caller won't use this function if it isn't a
> > connector. If it helps I can check if the compatible of the remote ends
> > with "-connector".
> 
> The function is called by a driver. A driver shouldn't know what's at the
> other end of the graph arc; the information should come from the firmware
> instead.
> 
> On some board there could be another device where you have a connector now.
> 
> As the connector has its own compatible string, there could be a connector
> driver to tell this is actually a connector, even if there's nothing to
> control. It'd be a very tiny driver.

Yes I know a connector driver would be the best. This also have the
advantage to do drop the connector handling in each subdev driver.. But
unfortunately I haven't the time yet. Would it be okay for you too check
that the remote is a connector and if not to exit?

Regards,
  Marco

> -- 
> Regards,
> 
> Sakari Ailus
>
Sakari Ailus Oct. 24, 2019, 12:02 p.m. UTC | #15
Hi Marco,

On Wed, Oct 23, 2019 at 02:21:57PM +0200, Marco Felsch wrote:
> Hi Sakari,
> 
> On 19-10-23 13:57, Sakari Ailus wrote:
> > Hi Marco,
> > 
> > Apologies for the delay.
> 
> No problem.
> 
> > On Wed, Oct 02, 2019 at 10:07:35AM +0200, Marco Felsch wrote:
> > > Hi Sakari,
> > > 
> > > On 19-10-02 10:03, Sakari Ailus wrote:
> > > > Hi Marco,
> > > > 
> > > > On Fri, Aug 30, 2019 at 12:16:35PM +0200, Marco Felsch wrote:
> > > > > The patch adds the initial connector parsing code, so we can move from a
> > > > > driver specific parsing code to a generic one. Currently only the
> > > > > generic fields and the analog-connector specific fields are parsed. Parsing
> > > > > the other connector specific fields can be added by a simple callbacks.
> > > > > 
> > > > > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> > > > > ---
> > > > > [1] https://patchwork.kernel.org/cover/10794703/
> > > > > 
> > > > > v10:
> > > > > - drop V4L2_CONN_HDMI support
> > > > > - adapt pr_err msg to reflect new state (-> connector is unkown)
> > > > > 
> > > > > v9:
> > > > > - Fix leading semicolon found by kbuild semicolon.cocci
> > > > > 
> > > > > v8:
> > > > > - V4L2_CON_* -> V4L2_CONN_*
> > > > > - tvnorms -> sdtv-standards
> > > > > - adapt to new v4l2_fwnode_connector_analog member
> > > > > - return error in case of V4L2_CONN_HDMI
> > > > > 
> > > > > v7:
> > > > > @Jacopo: I dropped your r b tag becuase of the amount of changes I
> > > > > made..
> > > > > 
> > > > > - drop unnecessary comments
> > > > > - fix commet style
> > > > > - s/v4l2_fwnode_connector_conv.name/v4l2_fwnode_connector_conv.compatible/
> > > > > - make label size variable and drop V4L2_CONNECTOR_MAX_LABEL usage
> > > > > - do not assign a default label in case of no label was specified
> > > > > - remove useless /* fall through */ comments
> > > > > - add support for N connector links
> > > > > - rename local variables to be more meaningful
> > > > > - adjust kernedoc
> > > > > - add v4l2_fwnode_connector_free()
> > > > > - improve error handling (use different error values)
> > > > > - make use of pr_warn_once()
> > > > > 
> > > > > v6:
> > > > > - use unsigned count var
> > > > > - fix comment and style issues
> > > > > - place '/* fall through */' to correct places
> > > > > - fix error handling and cleanup by releasing fwnode
> > > > > - drop vga and dvi parsing support as those connectors are rarely used
> > > > >   these days
> > > > > 
> > > > > v5:
> > > > > - s/strlcpy/strscpy/
> > > > > 
> > > > > v2-v4:
> > > > > - nothing since the patch was squashed from series [1] into this
> > > > >   series.
> > > > > 
> > > > >  drivers/media/v4l2-core/v4l2-fwnode.c | 129 ++++++++++++++++++++++++++
> > > > >  include/media/v4l2-fwnode.h           |  38 ++++++++
> > > > >  2 files changed, 167 insertions(+)
> > > > > 
> > > > > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > > index 3bd1888787eb..0bfa7cbf78df 100644
> > > > > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > > @@ -595,6 +595,135 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
> > > > >  }
> > > > >  EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
> > > > >  
> > > > > +static const struct v4l2_fwnode_connector_conv {
> > > > > +	enum v4l2_connector_type type;
> > > > > +	const char *compatible;
> > > > > +} connectors[] = {
> > > > > +	{
> > > > > +		.type = V4L2_CONN_COMPOSITE,
> > > > > +		.compatible = "composite-video-connector",
> > > > > +	}, {
> > > > > +		.type = V4L2_CONN_SVIDEO,
> > > > > +		.compatible = "svideo-connector",
> > > > > +	},
> > > > > +};
> > > > > +
> > > > > +static enum v4l2_connector_type
> > > > > +v4l2_fwnode_string_to_connector_type(const char *con_str)
> > > > > +{
> > > > > +	unsigned int i;
> > > > > +
> > > > > +	for (i = 0; i < ARRAY_SIZE(connectors); i++)
> > > > > +		if (!strcmp(con_str, connectors[i].compatible))
> > > > > +			return connectors[i].type;
> > > > > +
> > > > > +	return V4L2_CONN_UNKNOWN;
> > > > > +}
> > > > > +
> > > > > +static int
> > > > > +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
> > > > > +				   struct v4l2_fwnode_connector *vc)
> > > > > +{
> > > > > +	u32 stds;
> > > > > +	int ret;
> > > > > +
> > > > > +	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
> > > > > +
> > > > > +	/* The property is optional. */
> > > > > +	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
> > > > > +
> > > > > +	return 0;
> > > > > +}
> > > > > +
> > > > > +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
> > > > > +{
> > > > > +	unsigned int i;
> > > > > +
> > > > > +	if (IS_ERR_OR_NULL(connector))
> > > > > +		return;
> > > > > +
> > > > > +	for (i = 0; i < connector->nr_of_links; i++)
> > > > > +		v4l2_fwnode_put_link(&connector->links[i]);
> > > > > +	kfree(connector->links);
> > > > 
> > > > Please assign connector->links NULL here, and nr_of_links to zero.
> > > 
> > > Okay, I can do that.
> > > 
> > > > > +}
> > > > > +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
> > > > > +
> > > > > +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> > > > > +				      struct v4l2_fwnode_connector *connector)
> > > > > +{
> > > > > +	struct fwnode_handle *remote_pp, *remote_ep;
> > > > > +	const char *type_name;
> > > > > +	unsigned int i = 0, ep_num = 0;
> > > > > +	int err;
> > > > > +
> > > > > +	memset(connector, 0, sizeof(*connector));
> > > > > +
> > > > > +	remote_pp = fwnode_graph_get_remote_port_parent(fwnode);
> > > > 
> > > > How do you know a remote endpoint is a connector, and not another device's
> > > > endpoint?
> > > 
> > > Well, I think that the caller won't use this function if it isn't a
> > > connector. If it helps I can check if the compatible of the remote ends
> > > with "-connector".
> > 
> > The function is called by a driver. A driver shouldn't know what's at the
> > other end of the graph arc; the information should come from the firmware
> > instead.
> > 
> > On some board there could be another device where you have a connector now.
> > 
> > As the connector has its own compatible string, there could be a connector
> > driver to tell this is actually a connector, even if there's nothing to
> > control. It'd be a very tiny driver.
> 
> Yes I know a connector driver would be the best. This also have the
> advantage to do drop the connector handling in each subdev driver.. But
> unfortunately I haven't the time yet. Would it be okay for you too check
> that the remote is a connector and if not to exit?

The current design is also problematic in the sense that it parses remote DT
graph endpoints (as well as device nodes) that are not under the device's
own scope.

I wonder what kind of changes would that require, and how much more
difficult would the changes be to implement later on if a number of drivers
uses the newly added APIs.

v4l2_fwnode_parse_endpoint() should be considered as well. This is the
current API to parse endpoints. Could connectors be meaningfully parsed
within v4l2_fwnode_parse_endpoint()?

What you're doing with connectors looks very much the same as what one would
do with async sub-devices, and if this is re-implemented for connectors,
there should be a good reason to do so.
Marco Felsch Nov. 8, 2019, 8:58 a.m. UTC | #16
Hi Sakari,

sorry for my delay now ^^

On 19-10-24 15:02, Sakari Ailus wrote:
> Hi Marco,
> 
> On Wed, Oct 23, 2019 at 02:21:57PM +0200, Marco Felsch wrote:
> > Hi Sakari,
> > 
> > On 19-10-23 13:57, Sakari Ailus wrote:
> > > Hi Marco,
> > > 
> > > Apologies for the delay.
> > 
> > No problem.
> > 
> > > On Wed, Oct 02, 2019 at 10:07:35AM +0200, Marco Felsch wrote:
> > > > Hi Sakari,
> > > > 
> > > > On 19-10-02 10:03, Sakari Ailus wrote:
> > > > > Hi Marco,
> > > > > 
> > > > > On Fri, Aug 30, 2019 at 12:16:35PM +0200, Marco Felsch wrote:
> > > > > > The patch adds the initial connector parsing code, so we can move from a
> > > > > > driver specific parsing code to a generic one. Currently only the
> > > > > > generic fields and the analog-connector specific fields are parsed. Parsing
> > > > > > the other connector specific fields can be added by a simple callbacks.
> > > > > > 
> > > > > > Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> > > > > > ---
> > > > > > [1] https://patchwork.kernel.org/cover/10794703/
> > > > > > 
> > > > > > v10:
> > > > > > - drop V4L2_CONN_HDMI support
> > > > > > - adapt pr_err msg to reflect new state (-> connector is unkown)
> > > > > > 
> > > > > > v9:
> > > > > > - Fix leading semicolon found by kbuild semicolon.cocci
> > > > > > 
> > > > > > v8:
> > > > > > - V4L2_CON_* -> V4L2_CONN_*
> > > > > > - tvnorms -> sdtv-standards
> > > > > > - adapt to new v4l2_fwnode_connector_analog member
> > > > > > - return error in case of V4L2_CONN_HDMI
> > > > > > 
> > > > > > v7:
> > > > > > @Jacopo: I dropped your r b tag becuase of the amount of changes I
> > > > > > made..
> > > > > > 
> > > > > > - drop unnecessary comments
> > > > > > - fix commet style
> > > > > > - s/v4l2_fwnode_connector_conv.name/v4l2_fwnode_connector_conv.compatible/
> > > > > > - make label size variable and drop V4L2_CONNECTOR_MAX_LABEL usage
> > > > > > - do not assign a default label in case of no label was specified
> > > > > > - remove useless /* fall through */ comments
> > > > > > - add support for N connector links
> > > > > > - rename local variables to be more meaningful
> > > > > > - adjust kernedoc
> > > > > > - add v4l2_fwnode_connector_free()
> > > > > > - improve error handling (use different error values)
> > > > > > - make use of pr_warn_once()
> > > > > > 
> > > > > > v6:
> > > > > > - use unsigned count var
> > > > > > - fix comment and style issues
> > > > > > - place '/* fall through */' to correct places
> > > > > > - fix error handling and cleanup by releasing fwnode
> > > > > > - drop vga and dvi parsing support as those connectors are rarely used
> > > > > >   these days
> > > > > > 
> > > > > > v5:
> > > > > > - s/strlcpy/strscpy/
> > > > > > 
> > > > > > v2-v4:
> > > > > > - nothing since the patch was squashed from series [1] into this
> > > > > >   series.
> > > > > > 
> > > > > >  drivers/media/v4l2-core/v4l2-fwnode.c | 129 ++++++++++++++++++++++++++
> > > > > >  include/media/v4l2-fwnode.h           |  38 ++++++++
> > > > > >  2 files changed, 167 insertions(+)
> > > > > > 
> > > > > > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > > > index 3bd1888787eb..0bfa7cbf78df 100644
> > > > > > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > > > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > > > > > @@ -595,6 +595,135 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link)
> > > > > >  }
> > > > > >  EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link);
> > > > > >  
> > > > > > +static const struct v4l2_fwnode_connector_conv {
> > > > > > +	enum v4l2_connector_type type;
> > > > > > +	const char *compatible;
> > > > > > +} connectors[] = {
> > > > > > +	{
> > > > > > +		.type = V4L2_CONN_COMPOSITE,
> > > > > > +		.compatible = "composite-video-connector",
> > > > > > +	}, {
> > > > > > +		.type = V4L2_CONN_SVIDEO,
> > > > > > +		.compatible = "svideo-connector",
> > > > > > +	},
> > > > > > +};
> > > > > > +
> > > > > > +static enum v4l2_connector_type
> > > > > > +v4l2_fwnode_string_to_connector_type(const char *con_str)
> > > > > > +{
> > > > > > +	unsigned int i;
> > > > > > +
> > > > > > +	for (i = 0; i < ARRAY_SIZE(connectors); i++)
> > > > > > +		if (!strcmp(con_str, connectors[i].compatible))
> > > > > > +			return connectors[i].type;
> > > > > > +
> > > > > > +	return V4L2_CONN_UNKNOWN;
> > > > > > +}
> > > > > > +
> > > > > > +static int
> > > > > > +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode,
> > > > > > +				   struct v4l2_fwnode_connector *vc)
> > > > > > +{
> > > > > > +	u32 stds;
> > > > > > +	int ret;
> > > > > > +
> > > > > > +	ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds);
> > > > > > +
> > > > > > +	/* The property is optional. */
> > > > > > +	vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds;
> > > > > > +
> > > > > > +	return 0;
> > > > > > +}
> > > > > > +
> > > > > > +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector)
> > > > > > +{
> > > > > > +	unsigned int i;
> > > > > > +
> > > > > > +	if (IS_ERR_OR_NULL(connector))
> > > > > > +		return;
> > > > > > +
> > > > > > +	for (i = 0; i < connector->nr_of_links; i++)
> > > > > > +		v4l2_fwnode_put_link(&connector->links[i]);
> > > > > > +	kfree(connector->links);
> > > > > 
> > > > > Please assign connector->links NULL here, and nr_of_links to zero.
> > > > 
> > > > Okay, I can do that.
> > > > 
> > > > > > +}
> > > > > > +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free);
> > > > > > +
> > > > > > +int v4l2_fwnode_connector_alloc_parse(struct fwnode_handle *fwnode,
> > > > > > +				      struct v4l2_fwnode_connector *connector)
> > > > > > +{
> > > > > > +	struct fwnode_handle *remote_pp, *remote_ep;
> > > > > > +	const char *type_name;
> > > > > > +	unsigned int i = 0, ep_num = 0;
> > > > > > +	int err;
> > > > > > +
> > > > > > +	memset(connector, 0, sizeof(*connector));
> > > > > > +
> > > > > > +	remote_pp = fwnode_graph_get_remote_port_parent(fwnode);
> > > > > 
> > > > > How do you know a remote endpoint is a connector, and not another device's
> > > > > endpoint?
> > > > 
> > > > Well, I think that the caller won't use this function if it isn't a
> > > > connector. If it helps I can check if the compatible of the remote ends
> > > > with "-connector".
> > > 
> > > The function is called by a driver. A driver shouldn't know what's at the
> > > other end of the graph arc; the information should come from the firmware
> > > instead.
> > > 
> > > On some board there could be another device where you have a connector now.
> > > 
> > > As the connector has its own compatible string, there could be a connector
> > > driver to tell this is actually a connector, even if there's nothing to
> > > control. It'd be a very tiny driver.
> > 
> > Yes I know a connector driver would be the best. This also have the
> > advantage to do drop the connector handling in each subdev driver.. But
> > unfortunately I haven't the time yet. Would it be okay for you too check
> > that the remote is a connector and if not to exit?
> 
> The current design is also problematic in the sense that it parses remote DT
> graph endpoints (as well as device nodes) that are not under the device's
> own scope.

You are right that is not good. Would it be okay with you to parse only
the local node so the caller must pass the connector node?

> I wonder what kind of changes would that require, and how much more
> difficult would the changes be to implement later on if a number of drivers
> uses the newly added APIs.
> 
> v4l2_fwnode_parse_endpoint() should be considered as well. This is the
> current API to parse endpoints. Could connectors be meaningfully parsed
> within v4l2_fwnode_parse_endpoint()?

I think v4l2_fwnode_endpoint_parse() isn't the correct place. Of course
it is a endpoint but I don't think that a connector should be placed
there. Currently the endpoint is mostly used to describe the connection
between the isp and a sensor. I think we shouldn't add something
unrelated just because it's an fw-endpoint. The connector just describes
who users can interact with the device. A connector isn't connected to a
chip using mipi or something else.

> What you're doing with connectors looks very much the same as what one would
> do with async sub-devices, and if this is re-implemented for connectors,
> there should be a good reason to do so.

I don't think that it has something to do with a async device because
connectors shouldn't disappear so there is no need to probe it async.
Also we shouldn't probe it async only because we can add a custom probe
parse handler and furthermore we can't fill the connector struct..

Regards,
  Marco

> -- 
> Kind regards,
> 
> Kind regards,
> 
> Sakari Ailus
> sakari.ailus@linux.intel.com
>