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@xxxxxxxxxxxxxx> 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; >