[PATCH] thermal: rcar_gen3_thermal: Add Standby-mode function support
From: Hoan Nguyen An According to the hardware manual, Gen3 supports Standby-mode, Add this function, and we should use this function while suspend to reduce the energy consumption. Signed-off-by: Hoan Nguyen An --- drivers/thermal/rcar_gen3_thermal.c | 21 - 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 7aed533..7f3bd7f 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -447,11 +447,30 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) return ret; } +static int rcar_gen3_thermal_standby(struct rcar_gen3_thermal_priv* priv) +{ + unsigned int i; + u32 reg_val; + + for (i = 0; i < TSC_MAX_NUM; i++) { + struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; + + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, 0); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); + + reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); + rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val & ~THCTR_THSST); + + + usleep_range(1000, 2000); + } +} + static int __maybe_unused rcar_gen3_thermal_suspend(struct device *dev) { struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); - rcar_thermal_irq_set(priv, false); + rcar_gen3_thermal_standby(priv); return 0; } -- 2.7.4
Re: [PATCH v3 2/3] mmc: tmio: fix reset operation
On Thu, Nov 1, 2018 at 8:53 AM Niklas Söderlund wrote: > > From: Niklas Söderlund > > SD / MMC did not operate properly when suspend transition failed. > Because the SCC was not reset at resume, issue of the command failed. > Call the host specific reset function and reset the hardware in order to > add reset of SCC. This change also fixes tuning on some stubborn cards > on Gen2. > > Based on work from Masaharu Hayakawa. > > Signed-off-by: Niklas Söderlund > > --- > * Changes sine v1 > - Merge tmio_mmc_reset() into tmio_mmc_hw_reset() as it's now the only > caller. > > * Changes since v2 > - Rebased on mmc/next caused small refactoring of the code. > --- > drivers/mmc/host/tmio_mmc_core.c | 26 +++--- > 1 file changed, 15 insertions(+), 11 deletions(-) > > diff --git a/drivers/mmc/host/tmio_mmc_core.c > b/drivers/mmc/host/tmio_mmc_core.c > index 953562a12a0d6ebc..662161be03b6d52e 100644 > --- a/drivers/mmc/host/tmio_mmc_core.c > +++ b/drivers/mmc/host/tmio_mmc_core.c > @@ -171,6 +171,18 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host) > } > } > > +static void tmio_mmc_hw_reset(struct mmc_host *mmc) > +{ > + struct tmio_mmc_host *host = mmc_priv(mmc); > + > + host->reset(host); > + > + tmio_mmc_abort_dma(host); > + > + if (host->hw_reset) > + host->hw_reset(host); > +} > + > static void tmio_mmc_reset_work(struct work_struct *work) > { > struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host, > @@ -209,7 +221,7 @@ static void tmio_mmc_reset_work(struct work_struct *work) > > spin_unlock_irqrestore(&host->lock, flags); > > - host->reset(host); > + tmio_mmc_hw_reset(host->mmc); > > /* Ready for new calls */ > host->mrq = NULL; I see tmio_mmc_abort_dma() a few lines below. If you add tmio_mmc_abort_dma() into tmio_mmc_hw_reset(), you do not need to abort DMA twice, don't you? tmio_mmc_hw_reset(host->mmc); /* Ready for new calls */ host->mrq = NULL; tmio_mmc_abort_dma(host); /* <-- abort DMA again? */ mmc_request_done(host->mmc, mrq); } > @@ -696,14 +708,6 @@ static int tmio_mmc_start_data(struct tmio_mmc_host > *host, > return 0; > } > > -static void tmio_mmc_hw_reset(struct mmc_host *mmc) > -{ > - struct tmio_mmc_host *host = mmc_priv(mmc); > - > - if (host->hw_reset) > - host->hw_reset(host); > -} > - > static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) > { > struct tmio_mmc_host *host = mmc_priv(mmc); > @@ -1228,7 +1232,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) > _host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; > > _host->set_clock(_host, 0); > - _host->reset(_host); > + tmio_mmc_hw_reset(mmc); I think it is weird to call tmio_mmc_abort_dma() before tmio_mmc_request_dma(). > _host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, > CTL_IRQ_MASK); > tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL); > @@ -1329,7 +1333,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev) > struct tmio_mmc_host *host = dev_get_drvdata(dev); > > tmio_mmc_clk_enable(host); > - host->reset(host); > + tmio_mmc_hw_reset(host->mmc); > > if (host->clk_cache) > host->set_clock(host, host->clk_cache); > -- > 2.19.1 > -- Best Regards Masahiro Yamada
Re: [PATCH v3 1/3] mmc: tmio: enable module clock before resetting when resuming
On Thu, Nov 1, 2018 at 8:52 AM Niklas Söderlund wrote: > > From: Niklas Söderlund > > On runtime power management resume, the host clock needs to be > enabled before calling tmio_mmc_reset. If the mmc device has a power > domain entry, the host clock is enabled via genpd_runtime_resume, > running before tmio_mmc_host_runtime_resume. If the mmc device has no > power domain entry, however, genpd_runtime_resume is not called. This > patch changes tmio_mmc_host_runtime_resume to enable the host clock > before calling tmio_mmc_reset. > > Based on work from Masaharu Hayakawa. > > Signed-off-by: Niklas Söderlund Reviewed-by: Masahiro Yamada > --- > drivers/mmc/host/tmio_mmc_core.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/mmc/host/tmio_mmc_core.c > b/drivers/mmc/host/tmio_mmc_core.c > index 8d64f6196f33e882..953562a12a0d6ebc 100644 > --- a/drivers/mmc/host/tmio_mmc_core.c > +++ b/drivers/mmc/host/tmio_mmc_core.c > @@ -1328,8 +1328,8 @@ int tmio_mmc_host_runtime_resume(struct device *dev) > { > struct tmio_mmc_host *host = dev_get_drvdata(dev); > > - host->reset(host); > tmio_mmc_clk_enable(host); > + host->reset(host); > > if (host->clk_cache) > host->set_clock(host, host->clk_cache); > -- > 2.19.1 > -- Best Regards Masahiro Yamada
[PATCH v2 15/30] media: entity: Look for indirect routes
From: Sakari Ailus Two pads are considered having an active route for the purpose of has_route() if an indirect active route can be found between the two pads. An simple example of this is that a source pad has an active route to another source pad if both of the pads have an active route to the same sink pad. Make media_entity_has_route() return true in that case, and do not rely on drivers performing this by themselves. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 32 +++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 42977634d7102852..e45fc2549017615a 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -240,6 +240,9 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init); bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, unsigned int pad1) { + unsigned int i; + bool has_route; + if (pad0 >= entity->num_pads || pad1 >= entity->num_pads) return false; @@ -253,7 +256,34 @@ bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, && entity->pads[pad1].flags & MEDIA_PAD_FL_SINK) swap(pad0, pad1); - return entity->ops->has_route(entity, pad0, pad1); + has_route = entity->ops->has_route(entity, pad0, pad1); + /* A direct route is returned immediately */ + if (has_route || + (entity->pads[pad0].flags & MEDIA_PAD_FL_SINK && +entity->pads[pad1].flags & MEDIA_PAD_FL_SOURCE)) + return true; + + /* Look for indirect routes */ + for (i = 0; i < entity->num_pads; i++) { + if (i == pad0 || i == pad1) + continue; + + /* +* There are no direct routes between same types of +* pads, so skip checking this route +*/ + if (!((entity->pads[pad0].flags ^ entity->pads[i].flags) & + (MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_SINK))) + continue; + + /* Is there an indirect route? */ + if (entity->ops->has_route(entity, i, pad0) && + entity->ops->has_route(entity, i, pad1)) + return true; + } + + return false; + } EXPORT_SYMBOL_GPL(media_entity_has_route); -- 2.19.1
[PATCH v2 17/30] v4l: subdev: compat: Implement handling for VIDIOC_SUBDEV_[GS]_ROUTING
From: Sakari Ailus Implement compat IOCTL handling for VIDIOC_SUBDEV_G_ROUTING and VIDIOC_SUBDEV_S_ROUTING IOCTLs. Signed-off-by: Sakari Ailus Signed-off-by: Niklas Söderlund --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 77 +++ 1 file changed, 77 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 6481212fda772c73..83af332763f41a6b 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -1045,6 +1045,66 @@ static int put_v4l2_event32(struct v4l2_event __user *p64, return 0; } +struct v4l2_subdev_routing32 { + compat_caddr_t routes; + __u32 num_routes; + __u32 reserved[5]; +}; + +static int get_v4l2_subdev_routing(struct v4l2_subdev_routing __user *p64, + struct v4l2_subdev_routing32 __user *p32) +{ + struct v4l2_subdev_route __user *routes; + compat_caddr_t p; + u32 num_routes; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + get_user(p, &p32->routes) || + get_user(num_routes, &p32->num_routes) || + put_user(num_routes, &p64->num_routes) || + copy_in_user(&p64->reserved, &p32->reserved, +sizeof(p64->reserved)) || + num_routes > U32_MAX / sizeof(*p64->routes)) + return -EFAULT; + + routes = compat_ptr(p); + + if (!access_ok(VERIFY_READ, routes, + num_routes * sizeof(*p64->routes))) + return -EFAULT; + + if (put_user((__force struct v4l2_subdev_route *)routes, +&p64->routes)) + return -EFAULT; + + return 0; +} + +static int put_v4l2_subdev_routing(struct v4l2_subdev_routing __user *p64, + struct v4l2_subdev_routing32 __user *p32) +{ + struct v4l2_subdev_route __user *routes; + compat_caddr_t p; + u32 num_routes; + + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + get_user(p, &p32->routes) || + get_user(num_routes, &p64->num_routes) || + put_user(num_routes, &p32->num_routes) || + copy_in_user(&p32->reserved, &p64->reserved, +sizeof(p64->reserved)) || + num_routes > U32_MAX / sizeof(*p64->routes)) + return -EFAULT; + + routes = compat_ptr(p); + + if (!access_ok(VERIFY_WRITE, routes, + num_routes * sizeof(*p64->routes))) + return -EFAULT; + + return 0; +} + struct v4l2_edid32 { __u32 pad; __u32 start_block; @@ -1117,6 +1177,8 @@ static int put_v4l2_edid32(struct v4l2_edid __user *p64, #define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) #define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) #define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) +#define VIDIOC_SUBDEV_G_ROUTING32 _IOWR('V', 38, struct v4l2_subdev_routing32) +#define VIDIOC_SUBDEV_S_ROUTING32 _IOWR('V', 39, struct v4l2_subdev_routing32) #define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) #define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) @@ -1195,6 +1257,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; + case VIDIOC_SUBDEV_G_ROUTING32: cmd = VIDIOC_SUBDEV_G_ROUTING; break; + case VIDIOC_SUBDEV_S_ROUTING32: cmd = VIDIOC_SUBDEV_S_ROUTING; break; case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break; @@ -1227,6 +1291,15 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: + err = alloc_userspace(sizeof(struct v4l2_subdev_routing), + 0, &new_p64); + if (!err) + err = get_v4l2_subdev_routing(new_p64, p32); + compatible_arg = 0; + break; + case VIDIOC_G_EDID: case VIDIOC_S_EDID: err = alloc_userspace(sizeof(struct v4l2_edid), 0, &new_p64); @@ -1368,6 +1441,10 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar if (put_v4l2_edid32(new_p64, p32)) err = -EFAULT; break; + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: + err = put_v4l2_subdev_routing(new_p64, p32); + break; } if (err) return err; -- 2.19.1
[PATCH v2 18/30] v4l: subdev: Take routing information into account in link validation
From: Sakari Ailus The routing information is essential in link validation for multiplexed links: the pads at the ends of a multiplexed link have no single format defined for them. Instead, the format is accessible in the sink (or source) pads of the sub-devices at both ends of that link. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/v4l2-core/v4l2-subdev.c | 217 -- 1 file changed, 203 insertions(+), 14 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 1d3b37cf548fa533..05684c796b184272 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -640,12 +640,17 @@ static int v4l2_subdev_link_validate_get_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) { + dev_dbg(pad->entity->graph_obj.mdev->dev, + "obtaining format on \"%s\":%u\n", pad->entity->name, + pad->index); + if (is_media_entity_v4l2_subdev(pad->entity)) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = pad->index; + return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); } @@ -656,31 +661,215 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, return -EINVAL; } -int v4l2_subdev_link_validate(struct media_link *link) +static int v4l2_subdev_link_validate_one(struct media_link *link, +struct v4l2_subdev_format *source_fmt, +struct v4l2_subdev_format *sink_fmt) { struct v4l2_subdev *sink; - struct v4l2_subdev_format sink_fmt, source_fmt; int rval; - rval = v4l2_subdev_link_validate_get_format( - link->source, &source_fmt); - if (rval < 0) - return 0; - - rval = v4l2_subdev_link_validate_get_format( - link->sink, &sink_fmt); - if (rval < 0) - return 0; - sink = media_entity_to_v4l2_subdev(link->sink->entity); rval = v4l2_subdev_call(sink, pad, link_validate, link, - &source_fmt, &sink_fmt); + source_fmt, sink_fmt); if (rval != -ENOIOCTLCMD) return rval; return v4l2_subdev_link_validate_default( - sink, link, &source_fmt, &sink_fmt); + sink, link, source_fmt, sink_fmt); +} + +/* How many routes to assume there can be per a sub-device? */ +#define LINK_VALIDATE_ROUTES 8 + +#define R_SRC 0 +#define R_SINK 1 +#define NR_R 2 + +int v4l2_subdev_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sink; + struct route_info { + struct v4l2_subdev_route routes[LINK_VALIDATE_ROUTES]; + struct v4l2_subdev_routing routing; + bool has_route; + struct media_pad *pad; + /* Format for a non-multiplexed pad. */ + struct v4l2_subdev_format fmt; + } r[NR_R] = { + { + /* Source end of the link */ + .routing = { + .routes = r[R_SRC].routes, + .num_routes = ARRAY_SIZE(r[R_SRC].routes), + }, + .pad = link->source, + }, + { + /* Sink end of the link */ + .routing = { + .routes = r[R_SINK].routes, + .num_routes = ARRAY_SIZE(r[R_SINK].routes), + }, + .pad = link->sink, + }, + }; + unsigned int i, j; + int rval; + + sink = media_entity_to_v4l2_subdev(link->sink->entity); + + dev_dbg(sink->entity.graph_obj.mdev->dev, + "validating link \"%s\":%u -> \"%s\":%u\n", + link->source->entity->name, link->source->index, + sink->entity.name, link->sink->index); + + for (i = 0; i < NR_R; i++) { + struct route_info *ri = &r[i]; + + ri->has_route = true; + + rval = v4l2_subdev_call( + media_entity_to_v4l2_subdev(ri->pad->entity), + pad, get_routing, &ri->routing); + + switch (rval) { + case 0: + break; + case -ENOIOCTLCMD: + dev_dbg(sink->entity.graph_obj.mdev->dev, + "no routing information on \"%s\":%u\n", + ri->pad->entity->name, ri->pad->index); + ri->has_route = false; + break; + default: +
[PATCH v2 21/30] v4l: Add bus type to frame descriptors
From: Sakari Ailus Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- include/media/v4l2-subdev.h | 9 + 1 file changed, 9 insertions(+) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 5acaeeb9b3cacefa..ac1f7ee4cdb978ad 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -349,12 +349,21 @@ struct v4l2_mbus_frame_desc_entry { #define V4L2_FRAME_DESC_ENTRY_MAX 4 +enum { + V4L2_MBUS_FRAME_DESC_TYPE_PLATFORM, + V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL, + V4L2_MBUS_FRAME_DESC_TYPE_CCP2, + V4L2_MBUS_FRAME_DESC_TYPE_CSI2, +}; + /** * struct v4l2_mbus_frame_desc - media bus data frame description + * @type: type of the bus (V4L2_MBUS_FRAME_DESC_TYPE_*) * @entry: frame descriptors array * @num_entries: number of entries in @entry array */ struct v4l2_mbus_frame_desc { + u32 type; struct v4l2_mbus_frame_desc_entry entry[V4L2_FRAME_DESC_ENTRY_MAX]; unsigned short num_entries; }; -- 2.19.1
[PATCH v2 12/30] media: entity: Add an iterator helper for connected pads
From: Sakari Ailus Add a helper macro for iterating over pads that are connected through enabled routes. This can be used to find all the connected pads within an entity, for instance starting from the pad which has been obtained during the graph walk. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- include/media/media-entity.h | 27 +++ 1 file changed, 27 insertions(+) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 9540d2af80f19805..4bb1b568e1ac4795 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -936,6 +936,33 @@ __must_check int media_graph_walk_init( bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, unsigned int pad1); +static inline struct media_pad *__media_entity_for_routed_pads_next( + struct media_pad *start, struct media_pad *iter) +{ + struct media_entity *entity = start->entity; + + while (iter < &entity->pads[entity->num_pads] && + !media_entity_has_route(entity, start->index, iter->index)) + iter++; + + return iter; +} + +/** + * media_entity_for_routed_pads - Iterate over entity pads connected by routes + * + * @start: The stating pad + * @iter: The iterator pad + * + * Iterate over all pads connected through routes from a given pad + * within an entity. The iteration will include the starting pad itself. + */ +#define media_entity_for_routed_pads(start, iter) \ + for (iter = __media_entity_for_routed_pads_next(\ +start, (start)->entity->pads); \ +iter < &(start)->entity->pads[(start)->entity->num_pads]; \ +iter = __media_entity_for_routed_pads_next(start, iter + 1)) + /** * media_graph_walk_cleanup - Release resources used by graph walk. * -- 2.19.1
[PATCH v2 20/30] v4l: mc: Add an S_ROUTING helper function for power state changes
From: Sakari Ailus With the addition of the has_route() media entity operation, all pads of an entity are no longer interconnected. The S_ROUTING IOCTL for sub-devices can be used to enable and disable routes for an entity. The consequence is that the routing information has to be taken into account in use count calculation: disabling a route has a very similar effect on use counts as has disabling a link. Add a helper function for drivers implementing VIDIOC_SUBDEV_S_ROUTING IOCTL to take the change into account. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/v4l2-core/v4l2-mc.c | 34 +++ include/media/v4l2-mc.h | 22 2 files changed, 56 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 208cd91ce57ff211..534c5ea4fab42244 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -482,3 +482,37 @@ int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, return ret; } EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify); + +int v4l2_subdev_routing_pm_use(struct media_entity *entity, + struct v4l2_subdev_route *route) +{ + struct media_graph *graph = + &entity->graph_obj.mdev->pm_count_walk; + struct media_pad *source = &entity->pads[route->source_pad]; + struct media_pad *sink = &entity->pads[route->sink_pad]; + int source_use; + int sink_use; + int ret; + + source_use = pipeline_pm_use_count(source, graph); + sink_use = pipeline_pm_use_count(sink, graph); + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) { + /* Route disabled. */ + pipeline_pm_power(source, -sink_use, graph); + pipeline_pm_power(sink, -source_use, graph); + return 0; + } + + /* Route enabled. */ + ret = pipeline_pm_power(source, sink_use, graph); + if (ret < 0) + return ret; + + ret = pipeline_pm_power(sink, source_use, graph); + if (ret < 0) + pipeline_pm_power(source, -sink_use, graph); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_routing_pm_use); diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index bf5043c1ab6b3a32..730922636579a5fe 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -26,6 +26,7 @@ /* We don't need to include pci.h or usb.h here */ struct pci_dev; struct usb_device; +struct v4l2_subdev_route; #ifdef CONFIG_MEDIA_CONTROLLER /** @@ -132,6 +133,22 @@ int v4l2_pipeline_pm_use(struct media_entity *entity, int use); int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification); +/** + * v4l2_subdev_routing_pm_use - Handle power state changes due to S_ROUTING + * @entity: The entity + * @route: The new state of the route + * + * Propagate the use count across a route in a pipeline whenever the + * route is enabled or disabled. The function is called before + * changing the route state when enabling a route, and after changing + * the route state when disabling a route. + * + * Return 0 on success or a negative error code on failure. Powering entities + * off is assumed to never fail. This function will not fail for disconnection + * events. + */ +int v4l2_subdev_routing_pm_use(struct media_entity *entity, + struct v4l2_subdev_route *route); #else /* CONFIG_MEDIA_CONTROLLER */ static inline int v4l2_mc_create_media_graph(struct media_device *mdev) @@ -164,5 +181,10 @@ static inline int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, return 0; } +static inline int v4l2_subdev_routing_pm_use(struct media_entity *entity, +struct v4l2_subdev_route *route) +{ + return 0; +} #endif /* CONFIG_MEDIA_CONTROLLER */ #endif /* _V4L2_MC_H */ -- 2.19.1
[PATCH v2 26/30] adv748x: csi2: describe the multiplexed stream
The adv748x CSI-2 transmitter can only transmit one stream over the CSI-2 link, however it can choose which virtual channel is used. This choice effects the CSI-2 receiver and needs to be captured in the frame descriptor information, solve this by implementing .get_frame_desc(). Signed-off-by: Niklas Söderlund --- drivers/media/i2c/adv748x/adv748x-csi2.c | 31 +++- drivers/media/i2c/adv748x/adv748x.h | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 9f2c49221a8ddebc..d83ae8e5d802a3bd 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -222,9 +222,37 @@ static int adv748x_csi2_set_format(struct v4l2_subdev *sd, return ret; } +static int adv748x_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + struct v4l2_mbus_framefmt *mbusformat; + + memset(fd, 0, sizeof(*fd)); + + if (pad != ADV748X_CSI2_SOURCE) + return -EINVAL; + + mbusformat = adv748x_csi2_get_pad_format(sd, NULL, ADV748X_CSI2_SINK, +V4L2_SUBDEV_FORMAT_ACTIVE); + if (!mbusformat) + return -EINVAL; + + fd->entry->stream = tx->vc; + fd->entry->bus.csi2.channel = tx->vc; + fd->entry->bus.csi2.data_type = + adv748x_csi2_code_to_datatype(mbusformat->code); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 1; + + return 0; +} + static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { .get_fmt = adv748x_csi2_get_format, .set_fmt = adv748x_csi2_set_format, + .get_frame_desc = adv748x_csi2_get_frame_desc, }; /* - @@ -291,7 +319,8 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) return 0; /* Initialise the virtual channel */ - adv748x_csi2_set_virtual_channel(tx, 0); + tx->vc = 0; + adv748x_csi2_set_virtual_channel(tx, tx->vc); adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops, MEDIA_ENT_F_VID_IF_BRIDGE, diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 39c2fdc3b41667d8..b24e5ea1fe0f8c8d 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -76,6 +76,7 @@ enum adv748x_csi2_pads { struct adv748x_csi2 { struct adv748x_state *state; + unsigned int vc; struct v4l2_mbus_framefmt format; unsigned int page; unsigned int port; -- 2.19.1
[PATCH v2 30/30] rcar-csi2: expose the subdevice internal routing
Expose the subdevice internal routing from the single multiplexed sink pad to its source pads by implementing .get_routing(). This information is used to do link validation at stream start and allows user-space to view the route configuration. Signed-off-by: Niklas Söderlund --- drivers/media/platform/rcar-vin/rcar-csi2.c | 53 + 1 file changed, 53 insertions(+) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 00564c969dd021db..f51b6fbc6042ac1d 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -342,6 +342,14 @@ static int rcsi2_pad_to_vc(unsigned int pad) return pad - RCAR_CSI2_SOURCE_VC0; } +static int rcsi2_vc_to_pad(unsigned int vc) +{ + if (vc > 3) + return -EINVAL; + + return vc + RCAR_CSI2_SOURCE_VC0; +} + struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); int (*confirm_start)(struct rcar_csi2 *priv); @@ -705,9 +713,54 @@ static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = { .s_stream = rcsi2_s_stream, }; +static int rcsi2_get_routing(struct v4l2_subdev *sd, +struct v4l2_subdev_routing *routing) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + struct v4l2_mbus_frame_desc fd; + struct v4l2_subdev_route *r = routing->routes; + unsigned int i; + int ret; + + /* Get information about multiplexed link */ + ret = rcsi2_get_remote_frame_desc(priv, &fd); + if (ret) + return ret; + + if (routing->num_routes < fd.num_entries) { + routing->num_routes = fd.num_entries; + return -ENOSPC; + } + + routing->num_routes = fd.num_entries; + + for (i = 0; i < fd.num_entries; i++) { + struct v4l2_mbus_frame_desc_entry *entry = &fd.entry[i]; + int source_pad; + + source_pad = rcsi2_vc_to_pad(entry->bus.csi2.channel); + if (source_pad < 0) { + dev_err(priv->dev, "Virtual Channel out of range: %u\n", + entry->bus.csi2.channel); + return -ENOSPC; + } + + r->sink_pad = RCAR_CSI2_SINK; + r->sink_stream = entry->stream; + r->source_pad = source_pad; + r->source_stream = 0; + r->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE | + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE; + r++; + } + + return 0; +} + static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = { .set_fmt = rcsi2_set_pad_format, .get_fmt = rcsi2_get_pad_format, + .get_routing = rcsi2_get_routing, }; static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { -- 2.19.1
[PATCH v2 24/30] adv748x: csi2: add translation from pixelcode to CSI-2 datatype
Prepare to implement frame descriptors to support multiplexed streams by adding a function to map pixelcode to CSI-2 datatype. This is needed to properly be able to fill out the struct v4l2_mbus_frame_desc. Signed-off-by: Niklas Söderlund --- drivers/media/i2c/adv748x/adv748x-csi2.c | 22 ++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 6ce21542ed485811..8a7cc713c7adfcc1 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -14,6 +14,28 @@ #include "adv748x.h" +struct adv748x_csi2_format { + unsigned int code; + unsigned int datatype; +}; + +static const struct adv748x_csi2_format adv748x_csi2_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24,.datatype = 0x24, }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_YUYV10_2X10,.datatype = 0x1e, }, +}; + +static unsigned int adv748x_csi2_code_to_datatype(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adv748x_csi2_formats); i++) + if (adv748x_csi2_formats[i].code == code) + return adv748x_csi2_formats[i].datatype; + return 0; +} + static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc) { -- 2.19.1
[PATCH v2 13/30] media: entity: Add only connected pads to the pipeline
From: Sakari Ailus A single entity may contain multiple pipelines. Only add pads that were connected to the pad through which the entity was reached to the pipeline. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 20 ++-- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index cdf3805dec755ec5..a5bb257d5a68f755 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -460,15 +460,13 @@ __must_check int __media_pipeline_start(struct media_pad *pad, while ((pad = media_graph_walk_next(graph))) { struct media_entity *entity = pad->entity; - unsigned int i; + struct media_pad *iter; bool skip_validation = pad->pipe; DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); - for (i = 0; i < entity->num_pads; i++) { - struct media_pad *iter = &entity->pads[i]; - + media_entity_for_routed_pads(pad, iter) { if (iter->pipe && WARN_ON(iter->pipe != pipe)) ret = -EBUSY; else @@ -553,12 +551,9 @@ __must_check int __media_pipeline_start(struct media_pad *pad, media_graph_walk_start(graph, pad_err); while ((pad_err = media_graph_walk_next(graph))) { - struct media_entity *entity_err = pad_err->entity; - unsigned int i; - - for (i = 0; i < entity_err->num_pads; i++) { - struct media_pad *iter = &entity_err->pads[i]; + struct media_pad *iter; + media_entity_for_routed_pads(pad_err, iter) { /* Sanity check for negative stream_count */ if (!WARN_ON_ONCE(iter->stream_count <= 0)) { --iter->stream_count; @@ -611,12 +606,9 @@ void __media_pipeline_stop(struct media_pad *pad) media_graph_walk_start(graph, pad); while ((pad = media_graph_walk_next(graph))) { - struct media_entity *entity = pad->entity; - unsigned int i; - - for (i = 0; i < entity->num_pads; i++) { - struct media_pad *iter = &entity->pads[i]; + struct media_pad *iter; + media_entity_for_routed_pads(pad, iter) { /* Sanity check for negative stream_count */ if (!WARN_ON_ONCE(iter->stream_count <= 0)) { iter->stream_count--; -- 2.19.1
[PATCH v2 19/30] v4l: subdev: Improve link format validation debug messages
From: Sakari Ailus The existing link format validation failure debug message in media-entity.c helped to poinpoint the point of failure but provided no additional information what's wrong. Tell the user exactly why the validation failed. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/v4l2-core/v4l2-subdev.c | 40 ++- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 05684c796b184272..5c1459d3661e1de6 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -618,21 +618,47 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { + bool pass = true; + /* The width, height and code must match. */ - if (source_fmt->format.width != sink_fmt->format.width - || source_fmt->format.height != sink_fmt->format.height - || source_fmt->format.code != sink_fmt->format.code) - return -EPIPE; + if (source_fmt->format.width != sink_fmt->format.width) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: width does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.width, sink_fmt->format.width); + pass = false; + } + + if (source_fmt->format.height != sink_fmt->format.height) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: height does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.height, sink_fmt->format.height); + pass = false; + } + + if (source_fmt->format.code != sink_fmt->format.code) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: media bus code does not match (source 0x%8.8x, sink 0x%8.8x)\n", + __func__, + source_fmt->format.code, sink_fmt->format.code); + pass = false; + } /* The field order must match, or the sink field order must be NONE * to support interlaced hardware connected to bridges that support * progressive formats only. */ if (source_fmt->format.field != sink_fmt->format.field && - sink_fmt->format.field != V4L2_FIELD_NONE) - return -EPIPE; + sink_fmt->format.field != V4L2_FIELD_NONE) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: field does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.field, sink_fmt->format.field); + pass = false; + } - return 0; + return pass ? 0 : -EPIPE; } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); -- 2.19.1
[PATCH v2 22/30] v4l: Add CSI-2 bus configuration to frame descriptors
From: Sakari Ailus Add CSI-2 bus specific configuration to the frame descriptors. This allows obtaining the virtual channel and data type information for each stream the transmitter is sending. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- include/media/v4l2-subdev.h | 16 1 file changed, 16 insertions(+) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index ac1f7ee4cdb978ad..ffd98e4f368358a6 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -317,6 +317,17 @@ struct v4l2_subdev_audio_ops { int (*s_stream)(struct v4l2_subdev *sd, int enable); }; +/** + * struct v4l2_mbus_frame_desc_entry_csi2 + * + * @channel: CSI-2 virtual channel + * @data_type: CSI-2 data type ID + */ +struct v4l2_mbus_frame_desc_entry_csi2 { + u8 channel; + u8 data_type; +}; + /** * enum v4l2_mbus_frame_desc_entry - media bus frame description flags * @@ -340,11 +351,16 @@ enum v4l2_mbus_frame_desc_flags { * %FRAME_DESC_FL_BLOB is not set. * @length:number of octets per frame, valid if @flags * %V4L2_MBUS_FRAME_DESC_FL_LEN_MAX is set. + * @bus: Bus specific frame descriptor parameters + * @bus.csi2: CSI-2 specific bus configuration */ struct v4l2_mbus_frame_desc_entry { enum v4l2_mbus_frame_desc_flags flags; u32 pixelcode; u32 length; + union { + struct v4l2_mbus_frame_desc_entry_csi2 csi2; + } bus; }; #define V4L2_FRAME_DESC_ENTRY_MAX 4 -- 2.19.1
[PATCH v2 23/30] v4l: Add stream to frame descriptor
From: Sakari Ailus The stream field identifies the stream this frame descriptor applies to in routing configuration across a multiplexed link. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- include/media/v4l2-subdev.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index ffd98e4f368358a6..5fbce1932107a990 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -347,6 +347,7 @@ enum v4l2_mbus_frame_desc_flags { * struct v4l2_mbus_frame_desc_entry - media bus frame description structure * * @flags: bitmask flags, as defined by &enum v4l2_mbus_frame_desc_flags. + * @stream:stream in routing configuration * @pixelcode: media bus pixel code, valid if @flags * %FRAME_DESC_FL_BLOB is not set. * @length:number of octets per frame, valid if @flags @@ -356,6 +357,7 @@ enum v4l2_mbus_frame_desc_flags { */ struct v4l2_mbus_frame_desc_entry { enum v4l2_mbus_frame_desc_flags flags; + u32 stream; u32 pixelcode; u32 length; union { -- 2.19.1
[PATCH v2 29/30] rcar-csi2: use frame description information to configure CSI-2 bus
The driver can now access frame descriptor information, use it when configuring the CSI-2 bus. Only enable the virtual channels which are described in the frame descriptor and calculate the link based on all enabled streams. With multiplexed stream supported it's now meaningful to have different formats on the different source pads. Make source formats independent off each other and disallowing a format on the multiplexed sink pad. Signed-off-by: Niklas Söderlund --- drivers/media/platform/rcar-vin/rcar-csi2.c | 135 ++-- 1 file changed, 98 insertions(+), 37 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index b0044a08e71ed017..00564c969dd021db 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -304,25 +304,22 @@ static const struct rcsi2_mbps_reg hsfreqrange_m3w_h3es1[] = { #define CSI0CLKFREQRANGE(n)((n & 0x3f) << 16) struct rcar_csi2_format { - u32 code; unsigned int datatype; unsigned int bpp; }; static const struct rcar_csi2_format rcar_csi2_formats[] = { - { .code = MEDIA_BUS_FMT_RGB888_1X24,.datatype = 0x24, .bpp = 24 }, - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, - { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, - { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, - { .code = MEDIA_BUS_FMT_YUYV10_2X10,.datatype = 0x1e, .bpp = 20 }, + { .datatype = 0x1e, .bpp = 16 }, + { .datatype = 0x24, .bpp = 24 }, }; -static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) +static const struct rcar_csi2_format +*rcsi2_datatype_to_fmt(unsigned int datatype) { unsigned int i; for (i = 0; i < ARRAY_SIZE(rcar_csi2_formats); i++) - if (rcar_csi2_formats[i].code == code) + if (rcar_csi2_formats[i].datatype == datatype) return &rcar_csi2_formats[i]; return NULL; @@ -337,6 +334,14 @@ enum rcar_csi2_pads { NR_OF_RCAR_CSI2_PAD, }; +static int rcsi2_pad_to_vc(unsigned int pad) +{ + if (pad < RCAR_CSI2_SOURCE_VC0 || pad > RCAR_CSI2_SOURCE_VC3) + return -EINVAL; + + return pad - RCAR_CSI2_SOURCE_VC0; +} + struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); int (*confirm_start)(struct rcar_csi2 *priv); @@ -357,7 +362,7 @@ struct rcar_csi2 { struct v4l2_async_subdev asd; struct v4l2_subdev *remote; - struct v4l2_mbus_framefmt mf; + struct v4l2_mbus_framefmt mf[4]; struct mutex lock; int stream_count; @@ -393,6 +398,32 @@ static void rcsi2_reset(struct rcar_csi2 *priv) rcsi2_write(priv, SRST_REG, 0); } +static int rcsi2_get_remote_frame_desc(struct rcar_csi2 *priv, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *pad; + int ret; + + if (!priv->remote) + return -ENODEV; + + pad = media_entity_remote_pad(&priv->pads[RCAR_CSI2_SINK]); + if (!pad) + return -ENODEV; + + ret = v4l2_subdev_call(priv->remote, pad, get_frame_desc, + pad->index, fd); + if (ret) + return -ENODEV; + + if (fd->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_err(priv->dev, "Frame desc do not describe CSI-2 link"); + return -EINVAL; + } + + return 0; +} + static int rcsi2_wait_phy_start(struct rcar_csi2 *priv) { unsigned int timeout; @@ -431,10 +462,12 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) return 0; } -static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp) +static int rcsi2_calc_mbps(struct rcar_csi2 *priv, + struct v4l2_mbus_frame_desc *fd) { struct v4l2_subdev *source; struct v4l2_ctrl *ctrl; + unsigned int i, bpp = 0; u64 mbps; if (!priv->remote) @@ -450,6 +483,21 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp) return -EINVAL; } + /* Calculate total bpp */ + for (i = 0; i < fd->num_entries; i++) { + const struct rcar_csi2_format *format; + + format = rcsi2_datatype_to_fmt( + fd->entry[i].bus.csi2.data_type); + if (!format) { + dev_err(priv->dev, "Unknown data type: %d\n", + fd->entry[i].bus.csi2.data_type); + return -EINVAL; + } + + bpp += format->bpp; + } + /* * Calculate the phypll in mbps. * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes) @@ -463,42 +511,40 @@ static int rcsi2_
[PATCH v2 11/30] media: entity: Skip link validation for pads to which there is no route to
From: Sakari Ailus Links are validated along the pipeline which is about to start streaming. Not all the pads in entities that are traversed along that pipeline are part of the pipeline, however. Skip the link validation for such pads. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 4d10bc186e1e7a10..cdf3805dec755ec5 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -493,6 +493,11 @@ __must_check int __media_pipeline_start(struct media_pad *pad, struct media_pad *other_pad = link->sink->entity == entity ? link->sink : link->source; + /* Ignore pads to which there is no route. */ + if (!media_entity_has_route(entity, pad->index, + other_pad->index)) + continue; + /* Mark that a pad is connected by a link. */ bitmap_clear(has_no_links, other_pad->index, 1); -- 2.19.1
[PATCH v2 09/30] media: entity: Swap pads if route is checked from source to sink
From: Sakari Ailus This way the pads are always passed to the has_route() op sink pad first. Makes sense. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 3c0e7425c8983b45..33f00e35ccd92c6f 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -249,6 +249,10 @@ bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, if (!entity->ops || !entity->ops->has_route) return true; + if (entity->pads[pad0].flags & MEDIA_PAD_FL_SOURCE + && entity->pads[pad1].flags & MEDIA_PAD_FL_SINK) + swap(pad0, pad1); + return entity->ops->has_route(entity, pad0, pad1); } EXPORT_SYMBOL_GPL(media_entity_has_route); -- 2.19.1
[PATCH v2 25/30] adv748x: csi2: only allow formats on sink pads
Once the CSI-2 subdevice of the ADV748X becomes aware of multiplexed streams the format of the source pad is of no value as it carries multiple streams. Prepare for this by explicitly denying setting a format on anything but the sink pad. Signed-off-by: Niklas Söderlund --- drivers/media/i2c/adv748x/adv748x-csi2.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 8a7cc713c7adfcc1..9f2c49221a8ddebc 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -163,6 +163,9 @@ static int adv748x_csi2_get_format(struct v4l2_subdev *sd, struct adv748x_state *state = tx->state; struct v4l2_mbus_framefmt *mbusformat; + if (sdformat->pad != ADV748X_CSI2_SINK) + return -EINVAL; + mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad, sdformat->which); if (!mbusformat) @@ -186,6 +189,9 @@ static int adv748x_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mbusformat; int ret = 0; + if (sdformat->pad != ADV748X_CSI2_SINK) + return -EINVAL; + mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad, sdformat->which); if (!mbusformat) -- 2.19.1
[PATCH v2 27/30] adv748x: csi2: add internal routing configuration
Add support to get and set the internal routing between the adv748x CSI-2 transmitters sink pad and its multiplexed source pad. This routing includes which stream of the multiplexed pad to use, allowing the user to select which CSI-2 virtual channel to use when transmitting the stream. Signed-off-by: Niklas Söderlund --- drivers/media/i2c/adv748x/adv748x-csi2.c | 65 1 file changed, 65 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index d83ae8e5d802a3bd..ef588567313574c7 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -14,6 +14,8 @@ #include "adv748x.h" +#define ADV748X_CSI2_ROUTES_MAX 4 + struct adv748x_csi2_format { unsigned int code; unsigned int datatype; @@ -249,10 +251,73 @@ static int adv748x_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, return 0; } +static int adv748x_csi2_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *routing) +{ + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + struct v4l2_subdev_route *r = routing->routes; + unsigned int vc; + + if (routing->num_routes < ADV748X_CSI2_ROUTES_MAX) { + routing->num_routes = ADV748X_CSI2_ROUTES_MAX; + return -ENOSPC; + } + + routing->num_routes = ADV748X_CSI2_ROUTES_MAX; + + for (vc = 0; vc < ADV748X_CSI2_ROUTES_MAX; vc++) { + r->sink_pad = ADV748X_CSI2_SINK; + r->sink_stream = 0; + r->source_pad = ADV748X_CSI2_SOURCE; + r->source_stream = vc; + r->flags = vc == tx->vc ? V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0; + r++; + } + + return 0; +} + +static int adv748x_csi2_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *routing) +{ + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + struct v4l2_subdev_route *r = routing->routes; + unsigned int i; + int vc = -1; + + if (routing->num_routes > ADV748X_CSI2_ROUTES_MAX) + return -ENOSPC; + + for (i = 0; i < routing->num_routes; i++) { + if (r->sink_pad != ADV748X_CSI2_SINK || + r->sink_stream != 0 || + r->source_pad != ADV748X_CSI2_SOURCE || + r->source_stream >= ADV748X_CSI2_ROUTES_MAX) + return -EINVAL; + + if (r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) { + if (vc != -1) + return -EMLINK; + + vc = r->source_stream; + } + r++; + } + + if (vc != -1) + tx->vc = vc; + + adv748x_csi2_set_virtual_channel(tx, tx->vc); + + return 0; +} + static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { .get_fmt = adv748x_csi2_get_format, .set_fmt = adv748x_csi2_set_format, .get_frame_desc = adv748x_csi2_get_frame_desc, + .get_routing = adv748x_csi2_get_routing, + .set_routing = adv748x_csi2_set_routing, }; /* - -- 2.19.1
[PATCH v2 28/30] adv748x: afe: add routing support
The adv748x afe has eight analog sink pads, currently one of them is chosen to be the active route based on device tree configuration. With the new routing API it's possible to expose and change which of the eight sink pads are routed to the source pad. Signed-off-by: Niklas Söderlund --- drivers/media/i2c/adv748x/adv748x-afe.c | 65 + 1 file changed, 65 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 71714634efb08bd4..6b7fd4685aa4c177 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -39,6 +39,9 @@ #define ADV748X_AFE_STD_PAL_SECAM 0xe #define ADV748X_AFE_STD_PAL_SECAM_PED 0xf +#define ADV748X_AFE_ROUTES_MAX ((ADV748X_AFE_SINK_AIN7 - \ + ADV748X_AFE_SINK_AIN0) + 1) + static int adv748x_afe_read_ro_map(struct adv748x_state *state, u8 reg) { int ret; @@ -383,10 +386,72 @@ static int adv748x_afe_set_format(struct v4l2_subdev *sd, return 0; } +static int adv748x_afe_get_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *routing) +{ + struct adv748x_afe *afe = adv748x_sd_to_afe(sd); + struct v4l2_subdev_route *r = routing->routes; + unsigned int i; + + /* There is one possible route from each sink */ + if (routing->num_routes < ADV748X_AFE_ROUTES_MAX) { + routing->num_routes = ADV748X_AFE_ROUTES_MAX; + return -ENOSPC; + } + + routing->num_routes = ADV748X_AFE_ROUTES_MAX; + + for (i = ADV748X_AFE_SINK_AIN0; i <= ADV748X_AFE_SINK_AIN7; i++) { + r->sink_pad = i; + r->sink_stream = 0; + r->source_pad = ADV748X_AFE_SOURCE; + r->source_stream = 0; + r->flags = afe->input == i ? V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0; + r++; + } + + return 0; +} + +static int adv748x_afe_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *routing) +{ + struct adv748x_afe *afe = adv748x_sd_to_afe(sd); + struct v4l2_subdev_route *r = routing->routes; + int input = -1; + unsigned int i; + + if (routing->num_routes > ADV748X_AFE_ROUTES_MAX) + return -ENOSPC; + + for (i = 0; i < routing->num_routes; i++) { + if (r->sink_pad > ADV748X_AFE_SINK_AIN7 || + r->sink_stream != 0 || + r->source_pad != ADV748X_AFE_SOURCE || + r->source_stream != 0) + return -EINVAL; + + if (r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE) { + if (input != -1) + return -EMLINK; + + input = r->sink_pad; + } + r++; + } + + if (input != -1) + afe->input = input; + + return 0; +} + static const struct v4l2_subdev_pad_ops adv748x_afe_pad_ops = { .enum_mbus_code = adv748x_afe_enum_mbus_code, .set_fmt = adv748x_afe_set_format, .get_fmt = adv748x_afe_get_format, + .get_routing = adv748x_afe_get_routing, + .set_routing = adv748x_afe_set_routing, }; /* - -- 2.19.1
[PATCH v2 07/30] media: entity: Add has_route entity operation
From: Laurent Pinchart The optional operation can be used by entities to report whether two pads are internally connected. Signed-off-by: Laurent Pinchart Signed-off-by: Michal Simek Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- include/media/media-entity.h | 5 + 1 file changed, 5 insertions(+) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 8378f700389635ea..cd482814654616b5 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -223,6 +223,9 @@ struct media_pad { * @link_validate: Return whether a link is valid from the entity point of * view. The media_pipeline_start() function * validates all links by calling this operation. Optional. + * @has_route: Return whether a route exists inside the entity between + * two given pads. Optional. If the operation isn't + * implemented all pads will be considered as connected. * * .. note:: * @@ -235,6 +238,8 @@ struct media_entity_operations { const struct media_pad *local, const struct media_pad *remote, u32 flags); int (*link_validate)(struct media_link *link); + bool (*has_route)(struct media_entity *entity, unsigned int pad0, + unsigned int pad1); }; /** -- 2.19.1
[PATCH v2 10/30] media: entity: Use routing information during graph traversal
From: Laurent Pinchart Take internal routing information as reported by the entity has_route operation into account during graph traversal to avoid following unrelated links. Signed-off-by: Laurent Pinchart Signed-off-by: Michal Simek Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 44 ++-- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 33f00e35ccd92c6f..4d10bc186e1e7a10 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -257,15 +257,6 @@ bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, } EXPORT_SYMBOL_GPL(media_entity_has_route); -static struct media_pad * -media_entity_other(struct media_pad *pad, struct media_link *link) -{ - if (link->source == pad) - return link->sink; - else - return link->source; -} - /* push an entity to traversal stack */ static void stack_push(struct media_graph *graph, struct media_pad *pad) { @@ -336,7 +327,8 @@ static void media_graph_walk_iter(struct media_graph *graph) { struct media_pad *pad = stack_top(graph); struct media_link *link; - struct media_pad *next; + struct media_pad *remote; + struct media_pad *local; link = list_entry(link_top(graph), typeof(*link), list); @@ -350,23 +342,41 @@ static void media_graph_walk_iter(struct media_graph *graph) return; } - /* Get the entity in the other end of the link . */ - next = media_entity_other(pad, link); + /* +* Get the local pad, the remote pad and the entity at the other +* end of the link. +*/ + if (link->source->entity == pad->entity) { + remote = link->sink; + local = link->source; + } else { + remote = link->source; + local = link->sink; + } + + /* +* Are the local pad and the pad we came from connected +* internally in the entity ? +*/ + if (!media_entity_has_route(pad->entity, pad->index, local->index)) { + link_top(graph) = link_top(graph)->next; + return; + } /* Has the entity already been visited? */ - if (media_entity_enum_test_and_set(&graph->ent_enum, next->entity)) { + if (media_entity_enum_test_and_set(&graph->ent_enum, remote->entity)) { link_top(graph) = link_top(graph)->next; dev_dbg(pad->graph_obj.mdev->dev, "walk: skipping entity '%s' (already seen)\n", - next->entity->name); + remote->entity->name); return; } /* Push the new entity to stack and start over. */ link_top(graph) = link_top(graph)->next; - stack_push(graph, next); - dev_dbg(next->graph_obj.mdev->dev, "walk: pushing '%s':%u on stack\n", - next->entity->name, next->index); + stack_push(graph, remote); + dev_dbg(remote->graph_obj.mdev->dev, "walk: pushing '%s':%u on stack\n", + remote->entity->name, remote->index); } struct media_pad *media_graph_walk_next(struct media_graph *graph) -- 2.19.1
[PATCH v2 05/30] media: entity: Move the pipeline from entity to pads
From: Sakari Ailus This moves the pipe and stream_count fields from struct media_entity to struct media_pad. Effectively streams become pad-specific rather than being stream specific, allowing several independent streams to traverse a single entity. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 61 --- drivers/media/platform/exynos4-is/fimc-isp.c | 2 +- drivers/media/platform/exynos4-is/fimc-lite.c | 2 +- drivers/media/platform/omap3isp/isp.c | 2 +- drivers/media/platform/omap3isp/ispvideo.c| 2 +- drivers/media/platform/omap3isp/ispvideo.h| 2 +- drivers/media/platform/rcar-vin/rcar-dma.c| 2 +- drivers/media/platform/xilinx/xilinx-dma.c| 2 +- drivers/media/platform/xilinx/xilinx-dma.h| 2 +- drivers/staging/media/imx/imx-media-utils.c | 2 +- drivers/staging/media/omap4iss/iss.c | 2 +- drivers/staging/media/omap4iss/iss_video.c| 2 +- drivers/staging/media/omap4iss/iss_video.h| 2 +- include/media/media-entity.h | 17 -- 14 files changed, 61 insertions(+), 41 deletions(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 70db03fa33a21db1..13260149c4dfc90c 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -419,7 +419,7 @@ __must_check int __media_pipeline_start(struct media_entity *entity, struct media_pad *pad = entity->pads; struct media_pad *pad_err = pad; struct media_link *link; - int ret; + int ret = 0; if (!pipe->streaming_count++) { ret = media_graph_walk_init(&pipe->graph, mdev); @@ -431,21 +431,27 @@ __must_check int __media_pipeline_start(struct media_entity *entity, while ((pad = media_graph_walk_next(graph))) { struct media_entity *entity = pad->entity; + unsigned int i; + bool skip_validation = pad->pipe; DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); - entity->stream_count++; + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *iter = &entity->pads[i]; - if (WARN_ON(entity->pipe && entity->pipe != pipe)) { - ret = -EBUSY; - goto error; + if (iter->pipe && WARN_ON(iter->pipe != pipe)) + ret = -EBUSY; + else + iter->pipe = pipe; + iter->stream_count++; } - entity->pipe = pipe; + if (ret) + goto error; /* Already streaming --- no need to check. */ - if (entity->stream_count > 1) + if (skip_validation) continue; if (!entity->ops || !entity->ops->link_validate) @@ -514,19 +520,24 @@ __must_check int __media_pipeline_start(struct media_entity *entity, while ((pad_err = media_graph_walk_next(graph))) { struct media_entity *entity_err = pad_err->entity; + unsigned int i; + + for (i = 0; i < entity_err->num_pads; i++) { + struct media_pad *iter = &entity_err->pads[i]; - /* Sanity check for negative stream_count */ - if (!WARN_ON_ONCE(entity_err->stream_count <= 0)) { - entity_err->stream_count--; - if (entity_err->stream_count == 0) - entity_err->pipe = NULL; + /* Sanity check for negative stream_count */ + if (!WARN_ON_ONCE(iter->stream_count <= 0)) { + --iter->stream_count; + if (iter->stream_count == 0) + iter->pipe = NULL; + } } /* * We haven't increased stream_count further than this * so we quit here. */ - if (pad_err == pad) + if (pad_err->entity == pad->entity) break; } @@ -553,7 +564,7 @@ EXPORT_SYMBOL_GPL(media_pipeline_start); void __media_pipeline_stop(struct media_entity *entity) { - struct media_pipeline *pipe = entity->pipe; + struct media_pipeline *pipe = entity->pads->pipe; struct media_graph *graph = &pipe->graph; struct media_pad *pad; @@ -567,13 +578,17 @@ void __media_pipeline_stop(struct media_entity *entity) media_graph_walk_start(graph, entity->pads); while ((pad = media_graph_walk_next(graph))) { - struct media_entity *entity = pad->entity; + unsigned int i; - /* Sanity che
[PATCH v2 06/30] media: entity: Use pad as the starting point for a pipeline
From: Sakari Ailus The pipeline will be moved from the entity to the pads; reflect this in the media pipeline function API. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- Documentation/media/kapi/mc-core.rst | 6 ++-- drivers/media/media-entity.c | 25 +++--- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 6 ++-- .../media/platform/exynos4-is/fimc-capture.c | 8 ++--- .../platform/exynos4-is/fimc-isp-video.c | 8 ++--- drivers/media/platform/exynos4-is/fimc-lite.c | 8 ++--- drivers/media/platform/omap3isp/ispvideo.c| 6 ++-- .../media/platform/qcom/camss/camss-video.c | 6 ++-- drivers/media/platform/rcar-vin/rcar-dma.c| 6 ++-- .../media/platform/s3c-camif/camif-capture.c | 6 ++-- drivers/media/platform/vimc/vimc-capture.c| 6 ++-- drivers/media/platform/vsp1/vsp1_video.c | 6 ++-- drivers/media/platform/xilinx/xilinx-dma.c| 6 ++-- drivers/media/usb/au0828/au0828-core.c| 4 +-- drivers/staging/media/imx/imx-media-utils.c | 6 ++-- drivers/staging/media/omap4iss/iss_video.c| 6 ++-- include/media/media-entity.h | 33 ++- 17 files changed, 76 insertions(+), 76 deletions(-) diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst index 849b87439b7a9772..ede7e946f6a82ac0 100644 --- a/Documentation/media/kapi/mc-core.rst +++ b/Documentation/media/kapi/mc-core.rst @@ -211,11 +211,11 @@ When starting streaming, drivers must notify all entities in the pipeline to prevent link states from being modified during streaming by calling :c:func:`media_pipeline_start()`. -The function will mark all entities connected to the given entity through -enabled links, either directly or indirectly, as streaming. +The function will mark all entities connected to the given pad through +enabled routes and links, either directly or indirectly, as streaming. The struct :c:type:`media_pipeline` instance pointed to by -the pipe argument will be stored in every entity in the pipeline. +the pipe argument will be stored in every pad in the pipeline. Drivers should embed the struct :c:type:`media_pipeline` in higher-level pipeline structures and can then access the pipeline through the struct :c:type:`media_entity` diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 13260149c4dfc90c..f2fa0b7826dbc2f3 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -411,12 +411,11 @@ EXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad); * Pipeline management */ -__must_check int __media_pipeline_start(struct media_entity *entity, +__must_check int __media_pipeline_start(struct media_pad *pad, struct media_pipeline *pipe) { - struct media_device *mdev = entity->graph_obj.mdev; + struct media_device *mdev = pad->graph_obj.mdev; struct media_graph *graph = &pipe->graph; - struct media_pad *pad = entity->pads; struct media_pad *pad_err = pad; struct media_link *link; int ret = 0; @@ -549,24 +548,23 @@ __must_check int __media_pipeline_start(struct media_entity *entity, } EXPORT_SYMBOL_GPL(__media_pipeline_start); -__must_check int media_pipeline_start(struct media_entity *entity, +__must_check int media_pipeline_start(struct media_pad *pad, struct media_pipeline *pipe) { - struct media_device *mdev = entity->graph_obj.mdev; + struct media_device *mdev = pad->graph_obj.mdev; int ret; mutex_lock(&mdev->graph_mutex); - ret = __media_pipeline_start(entity, pipe); + ret = __media_pipeline_start(pad, pipe); mutex_unlock(&mdev->graph_mutex); return ret; } EXPORT_SYMBOL_GPL(media_pipeline_start); -void __media_pipeline_stop(struct media_entity *entity) +void __media_pipeline_stop(struct media_pad *pad) { - struct media_pipeline *pipe = entity->pads->pipe; + struct media_pipeline *pipe = pad->pipe; struct media_graph *graph = &pipe->graph; - struct media_pad *pad; /* * If the following check fails, the driver has performed an @@ -575,9 +573,10 @@ void __media_pipeline_stop(struct media_entity *entity) if (WARN_ON(!pipe)) return; - media_graph_walk_start(graph, entity->pads); + media_graph_walk_start(graph, pad); while ((pad = media_graph_walk_next(graph))) { + struct media_entity *entity = pad->entity; unsigned int i; for (i = 0; i < entity->num_pads; i++) { @@ -598,12 +597,12 @@ void __media_pipeline_stop(struct media_entity *entity) } EXPORT_SYMBOL_GPL(__media_pipeline_stop); -void media_pipeline_stop(struct media_entity *entity) +void media_pipeline_stop(struct media_pad *pad) { - struct media_device *mdev = entity->graph_obj.mdev; + struct media_device *mdev = pad->g
[PATCH v2 16/30] v4l: subdev: Add [GS]_ROUTING subdev ioctls and operations
From: Laurent Pinchart Signed-off-by: Laurent Pinchart Signed-off-by: Michal Simek - Add sink and source streams for multiplexed links - Copy the argument back in case of an error. This is needed to let the caller know the number of routes. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/v4l2-core/v4l2-ioctl.c | 20 +- drivers/media/v4l2-core/v4l2-subdev.c | 28 +++ include/media/v4l2-subdev.h | 7 + include/uapi/linux/v4l2-subdev.h | 40 +++ 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 7de041bae84fb2f2..40406acb51ec0906 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -2924,6 +2925,23 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, } break; } + + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *route = parg; + + if (route->num_routes > 0) { + if (route->num_routes > 256) + return -EINVAL; + + *user_ptr = (void __user *)route->routes; + *kernel_ptr = (void *)&route->routes; + *array_size = sizeof(struct v4l2_subdev_route) + * route->num_routes; + ret = 1; + } + break; + } } return ret; @@ -3033,7 +3051,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, * Some ioctls can return an error, but still have valid * results that must be returned. */ - if (err < 0 && !always_copy) + if (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING) goto out; out_array_args: diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 792f41dffe2329b9..1d3b37cf548fa533 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -516,7 +516,35 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); + + case VIDIOC_SUBDEV_G_ROUTING: + return v4l2_subdev_call(sd, pad, get_routing, arg); + + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *route = arg; + unsigned int i; + + if (route->num_routes > sd->entity.num_pads) + return -EINVAL; + + for (i = 0; i < route->num_routes; ++i) { + unsigned int sink = route->routes[i].sink_pad; + unsigned int source = route->routes[i].source_pad; + struct media_pad *pads = sd->entity.pads; + + if (sink >= sd->entity.num_pads || + source >= sd->entity.num_pads) + return -EINVAL; + + if (!(pads[sink].flags & MEDIA_PAD_FL_SINK) || + !(pads[source].flags & MEDIA_PAD_FL_SOURCE)) + return -EINVAL; + } + + return v4l2_subdev_call(sd, pad, set_routing, route); + } #endif + default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9102d6ca566e01f2..5acaeeb9b3cacefa 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -679,6 +679,9 @@ struct v4l2_subdev_pad_config { * * @set_frame_desc: set the low level media bus frame parameters, @fd array * may be adjusted by the subdev driver to device capabilities. + * + * @get_routing: callback for VIDIOC_SUBDEV_G_ROUTING IOCTL handler. + * @set_routing: callback for VIDIOC_SUBDEV_S_ROUTING IOCTL handler. */ struct v4l2_subdev_pad_ops { int (*init_cfg)(struct v4l2_subdev *sd, @@ -719,6 +722,10 @@ struct v4l2_subdev_pad_ops { struct v4l2_mbus_frame_desc *fd); int (*set_frame_desc)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd); + int (*get_routing)(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route); + int (*set_routing)(struct v4l2_subdev *sd, + struct v4l2_subdev_routing *route); }; /** diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index 03970ce3074193e6..af069bfb10ca23a5 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/incl
[PATCH v2 08/30] media: entity: Add media_has_route() function
From: Laurent Pinchart This is a wrapper around the media entity has_route operation. Signed-off-by: Laurent Pinchart Signed-off-by: Michal Simek Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 16 include/media/media-entity.h | 17 + 2 files changed, 33 insertions(+) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index f2fa0b7826dbc2f3..3c0e7425c8983b45 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -237,6 +237,22 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init); * Graph traversal */ +bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1) +{ + if (pad0 >= entity->num_pads || pad1 >= entity->num_pads) + return false; + + if (pad0 == pad1) + return true; + + if (!entity->ops || !entity->ops->has_route) + return true; + + return entity->ops->has_route(entity, pad0, pad1); +} +EXPORT_SYMBOL_GPL(media_entity_has_route); + static struct media_pad * media_entity_other(struct media_pad *pad, struct media_link *link) { diff --git a/include/media/media-entity.h b/include/media/media-entity.h index cd482814654616b5..9540d2af80f19805 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -919,6 +919,23 @@ int media_entity_get_fwnode_pad(struct media_entity *entity, __must_check int media_graph_walk_init( struct media_graph *graph, struct media_device *mdev); +/** + * media_entity_has_route - Check if two entity pads are connected internally + * + * @entity: The entity + * @pad0: The first pad index + * @pad1: The second pad index + * + * This function can be used to check whether two pads of an entity are + * connected internally in the entity. + * + * The caller must hold entity->graph_obj.mdev->mutex. + * + * Return: true if the pads are connected internally and false otherwise. + */ +bool media_entity_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1); + /** * media_graph_walk_cleanup - Release resources used by graph walk. * -- 2.19.1
[PATCH v2 14/30] media: entity: Add debug information in graph walk route check
From: Sakari Ailus Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index a5bb257d5a68f755..42977634d7102852 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -360,6 +360,9 @@ static void media_graph_walk_iter(struct media_graph *graph) */ if (!media_entity_has_route(pad->entity, pad->index, local->index)) { link_top(graph) = link_top(graph)->next; + dev_dbg(pad->graph_obj.mdev->dev, + "walk: skipping \"%s\":%u -> %u (no route)\n", + pad->entity->name, pad->index, local->index); return; } -- 2.19.1
[PATCH v2 04/30] v4l: mc: Start walk from a specific pad in use count calculation
From: Sakari Ailus With the addition of the recent has_route() media entity op, the pads of a media entity are no longer all interconnected. This has to be taken into account in power management. Prepare for the addition of a helper function supporting S_ROUTING. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/v4l2-core/v4l2-mc.c | 25 - 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 98edd47b2f0ae747..208cd91ce57ff211 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -332,17 +332,16 @@ EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); /* * pipeline_pm_use_count - Count the number of users of a pipeline - * @entity: The entity + * @pad: The pad * * Return the total number of users of all video device nodes in the pipeline. */ -static int pipeline_pm_use_count(struct media_entity *entity, - struct media_graph *graph) +static int pipeline_pm_use_count(struct media_pad *pad, +struct media_graph *graph) { - struct media_pad *pad; int use = 0; - media_graph_walk_start(graph, entity->pads); + media_graph_walk_start(graph, pad); while ((pad = media_graph_walk_next(graph))) { if (is_media_entity_v4l2_video_device(pad->entity)) @@ -388,7 +387,7 @@ static int pipeline_pm_power_one(struct media_entity *entity, int change) /* * pipeline_pm_power - Apply power change to all entities in a pipeline - * @entity: The entity + * @pad: The pad * @change: Use count change * * Walk the pipeline to update the use count and the power state of all non-node @@ -396,16 +395,16 @@ static int pipeline_pm_power_one(struct media_entity *entity, int change) * * Return 0 on success or a negative error code on failure. */ -static int pipeline_pm_power(struct media_entity *entity, int change, +static int pipeline_pm_power(struct media_pad *pad, int change, struct media_graph *graph) { - struct media_pad *tmp_pad, *pad; + struct media_pad *tmp_pad, *first = pad; int ret = 0; if (!change) return 0; - media_graph_walk_start(graph, entity->pads); + media_graph_walk_start(graph, pad); while (!ret && (pad = media_graph_walk_next(graph))) if (is_media_entity_v4l2_subdev(pad->entity)) @@ -414,7 +413,7 @@ static int pipeline_pm_power(struct media_entity *entity, int change, if (!ret) return ret; - media_graph_walk_start(graph, entity->pads); + media_graph_walk_start(graph, first); while ((tmp_pad = media_graph_walk_next(graph)) && tmp_pad != pad) @@ -437,7 +436,7 @@ int v4l2_pipeline_pm_use(struct media_entity *entity, int use) WARN_ON(entity->use_count < 0); /* Apply power change to connected non-nodes. */ - ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk); + ret = pipeline_pm_power(entity->pads, change, &mdev->pm_count_walk); if (ret < 0) entity->use_count -= change; @@ -451,8 +450,8 @@ int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk; - struct media_entity *source = link->source->entity; - struct media_entity *sink = link->sink->entity; + struct media_pad *source = link->source; + struct media_pad *sink = link->sink; int source_use; int sink_use; int ret = 0; -- 2.19.1
[PATCH v2 02/30] media: entity: Use pads instead of entities in the media graph walk stack
From: Sakari Ailus Change the media graph walk stack structure to use media pads instead of using media entities. In addition to the entity, the pad contains the information which pad in the entity are being dealt with. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- drivers/media/media-entity.c | 53 ++-- include/media/media-entity.h | 6 ++-- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 2bbc07de71aa5e6d..892e64a0a9d8ec42 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -237,40 +237,39 @@ EXPORT_SYMBOL_GPL(media_entity_pads_init); * Graph traversal */ -static struct media_entity * -media_entity_other(struct media_entity *entity, struct media_link *link) +static struct media_pad * +media_entity_other(struct media_pad *pad, struct media_link *link) { - if (link->source->entity == entity) - return link->sink->entity; + if (link->source == pad) + return link->sink; else - return link->source->entity; + return link->source; } /* push an entity to traversal stack */ -static void stack_push(struct media_graph *graph, - struct media_entity *entity) +static void stack_push(struct media_graph *graph, struct media_pad *pad) { if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { WARN_ON(1); return; } graph->top++; - graph->stack[graph->top].link = entity->links.next; - graph->stack[graph->top].entity = entity; + graph->stack[graph->top].link = pad->entity->links.next; + graph->stack[graph->top].pad = pad; } -static struct media_entity *stack_pop(struct media_graph *graph) +static struct media_pad *stack_pop(struct media_graph *graph) { - struct media_entity *entity; + struct media_pad *pad; - entity = graph->stack[graph->top].entity; + pad = graph->stack[graph->top].pad; graph->top--; - return entity; + return pad; } #define link_top(en) ((en)->stack[(en)->top].link) -#define stack_top(en) ((en)->stack[(en)->top].entity) +#define stack_top(en) ((en)->stack[(en)->top].pad) /** * media_graph_walk_init - Allocate resources for graph walk @@ -306,8 +305,8 @@ void media_graph_walk_start(struct media_graph *graph, struct media_pad *pad) media_entity_enum_set(&graph->ent_enum, pad->entity); graph->top = 0; - graph->stack[graph->top].entity = NULL; - stack_push(graph, pad->entity); + graph->stack[graph->top].pad = NULL; + stack_push(graph, pad); dev_dbg(pad->graph_obj.mdev->dev, "begin graph walk at '%s':%u\n", pad->entity->name, pad->index); } @@ -315,16 +314,16 @@ EXPORT_SYMBOL_GPL(media_graph_walk_start); static void media_graph_walk_iter(struct media_graph *graph) { - struct media_entity *entity = stack_top(graph); + struct media_pad *pad = stack_top(graph); struct media_link *link; - struct media_entity *next; + struct media_pad *next; link = list_entry(link_top(graph), typeof(*link), list); /* The link is not enabled so we do not follow. */ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { link_top(graph) = link_top(graph)->next; - dev_dbg(entity->graph_obj.mdev->dev, + dev_dbg(pad->graph_obj.mdev->dev, "walk: skipping disabled link '%s':%u -> '%s':%u\n", link->source->entity->name, link->source->index, link->sink->entity->name, link->sink->index); @@ -332,22 +331,22 @@ static void media_graph_walk_iter(struct media_graph *graph) } /* Get the entity in the other end of the link . */ - next = media_entity_other(entity, link); + next = media_entity_other(pad, link); /* Has the entity already been visited? */ - if (media_entity_enum_test_and_set(&graph->ent_enum, next)) { + if (media_entity_enum_test_and_set(&graph->ent_enum, next->entity)) { link_top(graph) = link_top(graph)->next; - dev_dbg(entity->graph_obj.mdev->dev, + dev_dbg(pad->graph_obj.mdev->dev, "walk: skipping entity '%s' (already seen)\n", - next->name); + next->entity->name); return; } /* Push the new entity to stack and start over. */ link_top(graph) = link_top(graph)->next; stack_push(graph, next); - dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n", - next->name); + dev_dbg(next->graph_obj.mdev->dev, "walk: pushing '%s':%u on stack\n", + next->entity->name, next->index); } struct media_entity *media_graph_walk_next(struct media_
[PATCH v2 01/30] media: entity: Use pad as a starting point for graph walk
From: Sakari Ailus With the upcoming use of the recently added has_route() media entity op, all the pads in an entity will no longer be considered interconnected. This has an effect where the media graph is traversed: the starting pad does make a difference. Prepare for this change by using pad instead of the entity as an argument for the graph walk operations. The actual graph traversal algorithm change is in further patches. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- Documentation/media/kapi/mc-core.rst| 2 +- drivers/media/media-entity.c| 17 - drivers/media/platform/exynos4-is/media-dev.c | 4 ++-- drivers/media/platform/omap3isp/ispvideo.c | 2 +- drivers/media/platform/vsp1/vsp1_video.c| 2 +- drivers/media/platform/xilinx/xilinx-dma.c | 2 +- drivers/media/v4l2-core/v4l2-mc.c | 6 +++--- drivers/staging/media/davinci_vpfe/vpfe_video.c | 6 +++--- drivers/staging/media/omap4iss/iss_video.c | 4 ++-- include/media/media-entity.h| 10 -- 10 files changed, 26 insertions(+), 29 deletions(-) diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst index 0c05503eaf1fa6c7..27aefb9a778b2ad6 100644 --- a/Documentation/media/kapi/mc-core.rst +++ b/Documentation/media/kapi/mc-core.rst @@ -165,7 +165,7 @@ Drivers initiate a graph traversal by calling :c:func:`media_graph_walk_start()` The graph structure, provided by the caller, is initialized to start graph -traversal at the given entity. +traversal at the given pad in an entity. Drivers can then retrieve the next entity by calling :c:func:`media_graph_walk_next()` diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 0b1cb3559140a1fe..2bbc07de71aa5e6d 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -300,17 +300,16 @@ void media_graph_walk_cleanup(struct media_graph *graph) } EXPORT_SYMBOL_GPL(media_graph_walk_cleanup); -void media_graph_walk_start(struct media_graph *graph, - struct media_entity *entity) +void media_graph_walk_start(struct media_graph *graph, struct media_pad *pad) { media_entity_enum_zero(&graph->ent_enum); - media_entity_enum_set(&graph->ent_enum, entity); + media_entity_enum_set(&graph->ent_enum, pad->entity); graph->top = 0; graph->stack[graph->top].entity = NULL; - stack_push(graph, entity); - dev_dbg(entity->graph_obj.mdev->dev, - "begin graph walk at '%s'\n", entity->name); + stack_push(graph, pad->entity); + dev_dbg(pad->graph_obj.mdev->dev, + "begin graph walk at '%s':%u\n", pad->entity->name, pad->index); } EXPORT_SYMBOL_GPL(media_graph_walk_start); @@ -428,7 +427,7 @@ __must_check int __media_pipeline_start(struct media_entity *entity, goto error_graph_walk_start; } - media_graph_walk_start(&pipe->graph, entity); + media_graph_walk_start(&pipe->graph, entity->pads); while ((entity = media_graph_walk_next(graph))) { DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); @@ -509,7 +508,7 @@ __must_check int __media_pipeline_start(struct media_entity *entity, * Link validation on graph failed. We revert what we did and * return the error. */ - media_graph_walk_start(graph, entity_err); + media_graph_walk_start(graph, entity_err->pads); while ((entity_err = media_graph_walk_next(graph))) { /* Sanity check for negative stream_count */ @@ -560,7 +559,7 @@ void __media_pipeline_stop(struct media_entity *entity) if (WARN_ON(!pipe)) return; - media_graph_walk_start(graph, entity); + media_graph_walk_start(graph, entity->pads); while ((entity = media_graph_walk_next(graph))) { /* Sanity check for negative stream_count */ diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 870501b0f351addb..51d2a571c06db6a3 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1144,7 +1144,7 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable, * through active links. This is needed as we cannot power on/off the * subdevs in random order. */ - media_graph_walk_start(graph, entity); + media_graph_walk_start(graph, entity->pads); while ((entity = media_graph_walk_next(graph))) { if (!is_media_entity_v4l2_video_device(entity)) @@ -1159,7 +1159,7 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable, return 0; err: - media_graph_walk_start(graph, entity_err); + media_graph_walk_start(graph, entity_err->pads); while ((entity_err = me
[PATCH v2 03/30] media: entity: Walk the graph based on pads
From: Sakari Ailus Instead of iterating over graph entities during the walk, iterate the pads through which the entity was first reached. This is required in order to make the entity pipeline pad-based rather than entity based. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund --- Documentation/media/kapi/mc-core.rst | 7 ++- drivers/media/media-entity.c | 46 ++ drivers/media/platform/exynos4-is/media-dev.c | 20 drivers/media/platform/omap3isp/ispvideo.c| 17 +++ drivers/media/platform/vsp1/vsp1_video.c | 12 ++--- drivers/media/platform/xilinx/xilinx-dma.c| 12 ++--- drivers/media/v4l2-core/v4l2-mc.c | 25 +- .../staging/media/davinci_vpfe/vpfe_video.c | 47 ++- drivers/staging/media/omap4iss/iss_video.c| 34 +++--- include/media/media-entity.h | 7 +-- 10 files changed, 122 insertions(+), 105 deletions(-) diff --git a/Documentation/media/kapi/mc-core.rst b/Documentation/media/kapi/mc-core.rst index 27aefb9a778b2ad6..849b87439b7a9772 100644 --- a/Documentation/media/kapi/mc-core.rst +++ b/Documentation/media/kapi/mc-core.rst @@ -167,8 +167,11 @@ Drivers initiate a graph traversal by calling The graph structure, provided by the caller, is initialized to start graph traversal at the given pad in an entity. -Drivers can then retrieve the next entity by calling -:c:func:`media_graph_walk_next()` +Drivers can then retrieve the next pad by calling +:c:func:`media_graph_walk_next()`. Only the pad through which the entity +is first reached is returned. If the caller is interested in knowing which +further pads would be connected, the :c:func:`media_entity_has_route()` +function can be used for that. When the graph traversal is complete the function will return ``NULL``. diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 892e64a0a9d8ec42..70db03fa33a21db1 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -349,9 +349,9 @@ static void media_graph_walk_iter(struct media_graph *graph) next->entity->name, next->index); } -struct media_entity *media_graph_walk_next(struct media_graph *graph) +struct media_pad *media_graph_walk_next(struct media_graph *graph) { - struct media_entity *entity; + struct media_pad *pad; if (stack_top(graph) == NULL) return NULL; @@ -364,11 +364,11 @@ struct media_entity *media_graph_walk_next(struct media_graph *graph) while (link_top(graph) != &stack_top(graph)->entity->links) media_graph_walk_iter(graph); - entity = stack_pop(graph)->entity; - dev_dbg(entity->graph_obj.mdev->dev, - "walk: returning entity '%s'\n", entity->name); + pad = stack_pop(graph); + dev_dbg(pad->graph_obj.mdev->dev, + "walk: returning pad '%s':%u\n", pad->entity->name, pad->index); - return entity; + return pad; } EXPORT_SYMBOL_GPL(media_graph_walk_next); @@ -416,7 +416,8 @@ __must_check int __media_pipeline_start(struct media_entity *entity, { struct media_device *mdev = entity->graph_obj.mdev; struct media_graph *graph = &pipe->graph; - struct media_entity *entity_err = entity; + struct media_pad *pad = entity->pads; + struct media_pad *pad_err = pad; struct media_link *link; int ret; @@ -426,9 +427,11 @@ __must_check int __media_pipeline_start(struct media_entity *entity, goto error_graph_walk_start; } - media_graph_walk_start(&pipe->graph, entity->pads); + media_graph_walk_start(&pipe->graph, pad); + + while ((pad = media_graph_walk_next(graph))) { + struct media_entity *entity = pad->entity; - while ((entity = media_graph_walk_next(graph))) { DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); @@ -452,11 +455,11 @@ __must_check int __media_pipeline_start(struct media_entity *entity, bitmap_fill(has_no_links, entity->num_pads); list_for_each_entry(link, &entity->links, list) { - struct media_pad *pad = link->sink->entity == entity - ? link->sink : link->source; + struct media_pad *other_pad = link->sink->entity == entity + ? link->sink : link->source; /* Mark that a pad is connected by a link. */ - bitmap_clear(has_no_links, pad->index, 1); + bitmap_clear(has_no_links, other_pad->index, 1); /* * Pads that either do not need to connect or @@ -465,13 +468,13 @@ __must_check int __media_pipeline_start(struct media_entity *entity, */
[PATCH v2 00/30] v4l: add support for multiplexed streams
Hi all, This series adds support for multiplexed streams within a media device link. The use-case addressed in this series covers CSI-2 Virtual Channels on the Renesas R-Car Gen3 platforms. The v4l2 changes have been a joint effort between Sakari and Laurent and floating around for some time [1]. I have added driver support for the devices used on the Renesas Gen3 platforms, a ADV7482 connected to the R-Car CSI-2 receiver. With these changes I can control which of the analog inputs of the ADV7482 the video source is captured from and on which CSI-2 virtual channel the video is transmitted on to the R-Car CSI-2 receiver. The series adds two new subdev IOCTLs [GS]_ROUTING which allows user-space to get and set routes inside a subdevice. I have added RFC support for these to v4l-utils [2] which can be used to test this series, example: Check the internal routing of the adv748x csi-2 transmitter: v4l2-ctl -d /dev/v4l-subdev24 --get-routing 0/0 -> 1/0 [ENABLED] 0/0 -> 1/1 [] 0/0 -> 1/2 [] 0/0 -> 1/3 [] Select that video should be outputed on VC 2 and check the result: $ v4l2-ctl -d /dev/v4l-subdev24 --set-routing '0/0 -> 1/2 [1]' $ v4l2-ctl -d /dev/v4l-subdev24 --get-routing 0/0 -> 1/0 [] 0/0 -> 1/1 [] 0/0 -> 1/2 [ENABLED] 0/0 -> 1/3 [] This series is tested on R-Car M3-N and for your testing needs this series is available at git://git.ragnatech.se/linux v4l2/mux * Changes since v1 - Rebased on latest media-tree. - Incorporated changes to patch 'v4l: subdev: compat: Implement handling for VIDIOC_SUBDEV_[GS]_ROUTING' by Sakari. - Added review tags. Thanks. 1. git://linuxtv.org/sailus/media_tree.git vc 2. git://git.ragnatech.se/v4l-utils routing Laurent Pinchart (4): media: entity: Add has_route entity operation media: entity: Add media_has_route() function media: entity: Use routing information during graph traversal v4l: subdev: Add [GS]_ROUTING subdev ioctls and operations Niklas Söderlund (7): adv748x: csi2: add translation from pixelcode to CSI-2 datatype adv748x: csi2: only allow formats on sink pads adv748x: csi2: describe the multiplexed stream adv748x: csi2: add internal routing configuration adv748x: afe: add routing support rcar-csi2: use frame description information to configure CSI-2 bus rcar-csi2: expose the subdevice internal routing Sakari Ailus (19): media: entity: Use pad as a starting point for graph walk media: entity: Use pads instead of entities in the media graph walk stack media: entity: Walk the graph based on pads v4l: mc: Start walk from a specific pad in use count calculation media: entity: Move the pipeline from entity to pads media: entity: Use pad as the starting point for a pipeline media: entity: Swap pads if route is checked from source to sink media: entity: Skip link validation for pads to which there is no route to media: entity: Add an iterator helper for connected pads media: entity: Add only connected pads to the pipeline media: entity: Add debug information in graph walk route check media: entity: Look for indirect routes v4l: subdev: compat: Implement handling for VIDIOC_SUBDEV_[GS]_ROUTING v4l: subdev: Take routing information into account in link validation v4l: subdev: Improve link format validation debug messages v4l: mc: Add an S_ROUTING helper function for power state changes v4l: Add bus type to frame descriptors v4l: Add CSI-2 bus configuration to frame descriptors v4l: Add stream to frame descriptor Documentation/media/kapi/mc-core.rst | 15 +- drivers/media/i2c/adv748x/adv748x-afe.c | 65 drivers/media/i2c/adv748x/adv748x-csi2.c | 124 +++- drivers/media/i2c/adv748x/adv748x.h | 1 + drivers/media/media-entity.c | 252 ++-- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 6 +- .../media/platform/exynos4-is/fimc-capture.c | 8 +- .../platform/exynos4-is/fimc-isp-video.c | 8 +- drivers/media/platform/exynos4-is/fimc-isp.c | 2 +- drivers/media/platform/exynos4-is/fimc-lite.c | 10 +- drivers/media/platform/exynos4-is/media-dev.c | 20 +- drivers/media/platform/omap3isp/isp.c | 2 +- drivers/media/platform/omap3isp/ispvideo.c| 25 +- drivers/media/platform/omap3isp/ispvideo.h| 2 +- .../media/platform/qcom/camss/camss-video.c | 6 +- drivers/media/platform/rcar-vin/rcar-csi2.c | 188 +--- drivers/media/platform/rcar-vin/rcar-dma.c| 8 +- .../media/platform/s3c-camif/camif-capture.c | 6 +- drivers/media/platform/vimc/vimc-capture.c| 6 +- drivers/media/platform/vsp1/vsp1_video.c | 18 +- drivers/media/platform/xilinx/xilinx-dma.c| 20 +- drivers/media/platform/xilinx/xilinx-dma.h| 2 +- drivers/media/usb/au0828/au0828-core.c| 4 +- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 77 + drivers/media/v4l2-core/v4l2-ioctl.c | 20 +- drivers/media/v
Re: [PATCH] arm64: dts: renesas: enable HS400 on R-Car Gen3
And this concludes my first patch review session while being on a ship :) Thanks for this SDHI hackathon guys, and looking forward to seeing you again! signature.asc Description: PGP signature
Re: [PATCH] arm64: dts: renesas: enable HS400 on R-Car Gen3
> This patch have quiet a few dependencies I'm afraid, let me know if you > wish to be notified once they all upstream. I don't think it's a good > idea to pick this up before all dependencies are resolved. Yes, we should do that. Ping Simon once all dependencies are in next. It is still fine to have this patch here in case people want to test. BTW did you mention your branch somewhere where you collected all these patches to make HS400 working/enabled? For this patch already: Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH 0/2] renesas: rcar-gen3: add HS400 quirk for SD clock
> This is the result of the SDHI hackathon for a possible solution to the > clock issue on early ES versions. It is based on the Gen2 solution where > a row of the possible clock settings are ignored on the effected SoC+ES > versions. The first row is not effected when reading settings left by > the bootloader, only when the setting the clock. We really thought about exposing both clocks, SDn and SDHn. I got convinced to use this less intrusive approach to get HS400 reliably to work as a first step. "First make it work, then make it beautiful" so to say... signature.asc Description: PGP signature
Re: [PATCH 2/2] clk: renesas: rcar-gen3: add HS400 quirk for SD clock
On Thu, Nov 01, 2018 at 12:25:18AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > On H3 (ES1.0,ES2.0) and M3-W (ES1.0,ES1.1) the clock setting for HS400 > needs a quirk to function properly. The reason for the quirk is that > there are two settings which produces same divider vale for the SDn > clock. On the effected boards the one currently selected results in HS00 > not working. > > This change uses the same method as the Gen2 CPG driver and simply > ignores the first clock setting as this is the offending one when > selecting the settings. Which of the two possible settings is used have > no effect for SDR104. > > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH 1/2] clk: renesas: rcar-gen3: add documentation for SD clocks
On Thu, Nov 01, 2018 at 12:25:17AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > Document the known use cases of the different clock settings. This is > useful as different SoC and ES versions uses different settings to do > the same thing as there are more then one combination to achieve the > same SDn clock speed. > > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v2 0/3] mmc: renesas_sdhi: extend quirk selection to handle ES revisions
All patches tested on M3N: Tested-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v3 0/3] mmc: tmio: fix reset operation
All patches tested on M3N, H2, and E2. Tested-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH] mmc: renesas_sdhi: remove workaround for HS400 clock
On Wed, Oct 31, 2018 at 11:59:44PM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > The driver sets an incorrect clock and depends on the clock driver > knowledge of this incorrect setting to still set a 200Mhz SDn clock. > Instead of spreading the workaround between the two drivers the clock > driver should be made aware of the ES versions where the special clock > handling is needed no need to keep this workaround in the SDHI driver. > > Signed-off-by: Niklas Söderlund Tested-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH] mmc: tmio: delete wait in tuning process
On Thu, Nov 01, 2018 at 12:00:43AM +0100, Niklas Söderlund wrote: > From: Masaharu Hayakawa > > The manual does not contain information that a wait is needed in the > tuning process, this might be a leftover from early development. > Removing the wait don't have any effect on operation so delete the wait > to shorten the initialization time. > > Signed-off-by: Masaharu Hayakawa > Signed-off-by: Takeshi Saito > [Niklas: fixup commit message] > Signed-off-by: Niklas Söderlund Tested-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v2 0/3] mmc: renesas_sdhi: extend quirk selection to handle ES revisions
> Patch 1/3 adds support to select quirks based on SoC + ES revision using > soc_device_match() and converts the only existing quirk. Patch 2/3 > Removes the old method to select quirk based on compatibility string. > While Patch 3/3 adds a new quirk from the BSP which blacklists some > known problematic ES versions for HS400. HS400 is not yet enabled > upstream so blacklisting these ES versions is not a regression of > functionality. This will allow us even to get rid of the TMIO_MMC_HAVE_4TAP_HS400 flag entirely because we now use it only within the Renesas part of SDHI. We want to do this incrementally, though, because we first want to get the HS400 feature enabled as the first step. signature.asc Description: PGP signature
Re: [PATCH v2 2/3] mmc: renesas_sdhi: align compatibility properties for H3 and M3-W
On Thu, Nov 01, 2018 at 12:13:39AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > It was though all ES revisions of H3 and M3-W SoCs required the > TMIO_MMC_HAVE_4TAP_HS400 flag. Recent datasheet updates tells us this is > not true, only early ES revisions of the SoC do. > > Since quirk matching based on ES revisions is now used to handle the > flag it's possible to align all Gen3 compatibility properties. This will > allow later ES revisions of H3 and M3-W to use the correct 8-tap HS400 > mode. > > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v2 3/3] mmc: renesas_sdhi: disable HS400 on H3 ES1.x and M3-W ES1.x
On Thu, Nov 01, 2018 at 12:13:40AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > The Renesas BSP confirms that H3 ES1.x and M3-W ES1.x do not properly > support HS400. Add a quirk to indicate this and disable HS400 in the MMC > capabilities if the quirk is set. > > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v2 1/3] mmc: renesas_sdhi: handle 4tap hs400 mode quirk based on SoC revision
On Thu, Nov 01, 2018 at 12:13:38AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > Latest datasheet makes it clear that not all ES revisions of the H3 and > M3-W have the 4-tap HS400 mode quirk, currently the quirk is set > unconditionally for these two SoCs. Prepare to handle the quirk based on > SoC revision instead of compatibility value by using soc_device_match() > and set the TMIO_MMC_HAVE_4TAP_HS400 flag explicitly. > > The reason for adding a new quirks struct instead of just a flag is that > looking ahead it seems more quirks needs to be handled in a SoC revision > basis. > > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v3 0/3] mmc: tmio: fix reset operation
So, we agreed on this series during our Renesas SDHI hackathon. Comments / testing from Yamada-san would be very welcome because we really don't want to cause regressions on his hardware. signature.asc Description: PGP signature
Re: [PATCH v3 3/3] mmc: renesas_sdhi: add initial setting of interrupt mask register
On Thu, Nov 01, 2018 at 12:05:54AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > The initial value of the interrupt mask register may be different from > the H/W manual at the startup of the kernel by setting from the > bootloader. Since the error interrupts may be unmasked, the driver sets > initial value. > > The initial value is only known for R-Car Gen2 and Gen3 platforms so > limit the initialization to those platforms. > > Based on work from Masaharu Hayakawa. > > Signed-off-by: Niklas Söderlund > Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v3 2/3] mmc: tmio: fix reset operation
On Thu, Nov 01, 2018 at 12:05:53AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > SD / MMC did not operate properly when suspend transition failed. > Because the SCC was not reset at resume, issue of the command failed. > Call the host specific reset function and reset the hardware in order to > add reset of SCC. This change also fixes tuning on some stubborn cards > on Gen2. > > Based on work from Masaharu Hayakawa. > > Signed-off-by: Niklas Söderlund > Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH] mmc: tmio: delete wait in tuning process
On Thu, Nov 01, 2018 at 12:00:43AM +0100, Niklas Söderlund wrote: > From: Masaharu Hayakawa > > The manual does not contain information that a wait is needed in the > tuning process, this might be a leftover from early development. > Removing the wait don't have any effect on operation so delete the wait > to shorten the initialization time. > > Signed-off-by: Masaharu Hayakawa > Signed-off-by: Takeshi Saito > [Niklas: fixup commit message] > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH v3 1/3] mmc: tmio: enable module clock before resetting when resuming
On Thu, Nov 01, 2018 at 12:05:52AM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > On runtime power management resume, the host clock needs to be > enabled before calling tmio_mmc_reset. If the mmc device has a power > domain entry, the host clock is enabled via genpd_runtime_resume, > running before tmio_mmc_host_runtime_resume. If the mmc device has no > power domain entry, however, genpd_runtime_resume is not called. This > patch changes tmio_mmc_host_runtime_resume to enable the host clock > before calling tmio_mmc_reset. > > Based on work from Masaharu Hayakawa. > > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
Re: [PATCH] mmc: renesas_sdhi: remove workaround for HS400 clock
On Wed, Oct 31, 2018 at 11:59:44PM +0100, Niklas Söderlund wrote: > From: Niklas Söderlund > > The driver sets an incorrect clock and depends on the clock driver > knowledge of this incorrect setting to still set a 200Mhz SDn clock. > Instead of spreading the workaround between the two drivers the clock > driver should be made aware of the ES versions where the special clock > handling is needed no need to keep this workaround in the SDHI driver. > > Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang signature.asc Description: PGP signature
RE: [RFC] drm/bridge/sii902x: Fix EDID readback
Hello Peter, Thank you for your feedback! > Subject: Re: [RFC] drm/bridge/sii902x: Fix EDID readback snip > > > > To further detail the problem, the system is vulnerable from before the > > last write > > performed by sii902x_i2c_bypass_select to after we confirm we need the > > switch > > to be closed within sii902x_i2c_bypass_deselect, that's the minimum amount > > of time > > we could keep the parent adapter locked for, I guess I am stuck with a > > parent-locking > > architecture, aren't I? > > If that problem description is correct, then yes, I think the *only* solution > is to combine the three parts "open bypass mode", "edid xfer" and "close > bypass > mode", and to keep the i2c adapter locked during the procedure so that other > xfers do not creep in and crap thing up from the side. And one way to combine > the three parts is to use a parent-locked i2c gate. And since you need to keep > the i2c adapter locked over the whole procedure, you need to use unlocked > xfers > (as you have already discovered). But how do you know that this problem > description is accurate? I basically observed what was going on on the bus (with a logic analyser) while generating traffic on the parent adapter > Why is it not ok for unrelated xfers to creep in > between opening the bypass mode and the edid xfer, and how do you know that > this is not ok? Because those transfers would come with no extra delay between STOP and START conditions while the HDMI transmitter is in passthrough mode > > > But I guess I could release it when it's not actually needed, > > How would you figure out when it's not needed? The moment you tell the HDMI transmitter you are going to talk to the monitor nothing else can flow on the bus, up until you gracefully close the pass through session, which means I wouldn't really need to hold the parent lock during the entire duration of the select callback, I would need to hold the parent lock only from before the last write as that's when we tell the HDMI transmitter to activate the passthrough mode, but I guess it would make the driver hard to maintain (as in others would need to understand this properly before making any changes), wouldn't it? > > > or is this going to be a pain to maintain? Shall I just keep going with the > > parent-locking > > but using bare i2c transactions only within select and deselect and leave > > regmap > > to deal with everything else? > > That's a possibility. Take care to not mess up any cached state in regmap > though. The original version of the driver wasn't using any caching, so I guess I would need to fallback exactly to the same implementation. So, what should I do? Should I keep the parent-locking, the unlocked flavours of rd/wr/rmw from within select/deselect, and put back regmap based rd/wr/rmw with no caching for everything else? Thank you! Fab > The registers you touch from select/deselect should probably be volatile or > something like that? > > Cheers, > Peter Renesas Electronics Europe Ltd, Dukes Meadow, Millboard Road, Bourne End, Buckinghamshire, SL8 5FH, UK. Registered in England & Wales under Registered No. 04586709.
Re: [RFC] drm/bridge/sii902x: Fix EDID readback
On 2018-11-01 17:04, Fabrizio Castro wrote: > Hello Peter, > > Thank you for your feedback! > >>> The "mux-locked" implementation was the one I first tried and I discovered >>> it doesn't work for me, as other traffic on the parent adapter can get in >>> the >>> way. What we need for this device is no other traffic on the bus during the >>> "select transaction deselect" procedure, that's why I went with the parent >>> locking. Also this device needs a delay between stop and start conditions >>> while addressing the monitor. >> >> Ok, I thought the problem was that a delay was needed between the STOP >> of the command opening the gate and the START of the edid eeprom xfer, and >> that everything else worked normally. Too bad this wasn't the actual problem. >> >> Hmm. If it is indeed true that other xfers must never creep into the "select >> xfer deselect" procedure then you are indeed stuck with a parent-locking. >> But is that really the case? Could it be that the extra delay between >> STOP-START is only needed after the absolutely last xfer before the >> command closing the gate? >> >> If that problem description is correct, it should be possible to go back to >> a mux-locked gate, but then in your deselect implementation grab the i2c >> adapter >> lock manually - with i2c_lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER) - before >> the >> 30 us delay, then open code the command to close the gate with an unlocked >> i2c >> access, and finally release the i2c bus lock. That way you have ensured >> silence >> on the bus for the required time before closing the gate. You would still >> need >> to bypass regmap, but only in this one place (but maybe you should bypass >> regmap for opening the gate too, for symmetry). > > To further detail the problem, the system is vulnerable from before the last > write > performed by sii902x_i2c_bypass_select to after we confirm we need the switch > to be closed within sii902x_i2c_bypass_deselect, that's the minimum amount of > time > we could keep the parent adapter locked for, I guess I am stuck with a > parent-locking > architecture, aren't I? If that problem description is correct, then yes, I think the *only* solution is to combine the three parts "open bypass mode", "edid xfer" and "close bypass mode", and to keep the i2c adapter locked during the procedure so that other xfers do not creep in and crap thing up from the side. And one way to combine the three parts is to use a parent-locked i2c gate. And since you need to keep the i2c adapter locked over the whole procedure, you need to use unlocked xfers (as you have already discovered). But how do you know that this problem description is accurate? Why is it not ok for unrelated xfers to creep in between opening the bypass mode and the edid xfer, and how do you know that this is not ok? > But I guess I could release it when it's not actually needed, How would you figure out when it's not needed? > or is this going to be a pain to maintain? Shall I just keep going with the > parent-locking > but using bare i2c transactions only within select and deselect and leave > regmap > to deal with everything else? That's a possibility. Take care to not mess up any cached state in regmap though. The registers you touch from select/deselect should probably be volatile or something like that? Cheers, Peter
RE: [RFC] drm/bridge/sii902x: Fix EDID readback
Hello Peter, Thank you for your feedback! > > The "mux-locked" implementation was the one I first tried and I discovered > > it doesn't work for me, as other traffic on the parent adapter can get in > > the > > way. What we need for this device is no other traffic on the bus during the > > "select transaction deselect" procedure, that's why I went with the parent > > locking. Also this device needs a delay between stop and start conditions > > while addressing the monitor. > > Ok, I thought the problem was that a delay was needed between the STOP > of the command opening the gate and the START of the edid eeprom xfer, and > that everything else worked normally. Too bad this wasn't the actual problem. > > Hmm. If it is indeed true that other xfers must never creep into the "select > xfer deselect" procedure then you are indeed stuck with a parent-locking. > But is that really the case? Could it be that the extra delay between > STOP-START is only needed after the absolutely last xfer before the > command closing the gate? > > If that problem description is correct, it should be possible to go back to > a mux-locked gate, but then in your deselect implementation grab the i2c > adapter > lock manually - with i2c_lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER) - before the > 30 us delay, then open code the command to close the gate with an unlocked i2c > access, and finally release the i2c bus lock. That way you have ensured > silence > on the bus for the required time before closing the gate. You would still need > to bypass regmap, but only in this one place (but maybe you should bypass > regmap for opening the gate too, for symmetry). To further detail the problem, the system is vulnerable from before the last write performed by sii902x_i2c_bypass_select to after we confirm we need the switch to be closed within sii902x_i2c_bypass_deselect, that's the minimum amount of time we could keep the parent adapter locked for, I guess I am stuck with a parent-locking architecture, aren't I? But I guess I could release it when it's not actually needed, or is this going to be a pain to maintain? Shall I just keep going with the parent-locking but using bare i2c transactions only within select and deselect and leave regmap to deal with everything else? snip > > > > Ah, do you think the following is going to cause any issue in the future? > > -edid = drm_get_edid(connector, sii902x->i2c->adapter); > > +edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]); > > No, that was a critical part of the idea to use a gate to introduce the > needed delay. Again, thank you very much for spending your time looking into this, your feedback is very much appreciated. Fab > > Cheers, > Peter Renesas Electronics Europe Ltd, Dukes Meadow, Millboard Road, Bourne End, Buckinghamshire, SL8 5FH, UK. Registered in England & Wales under Registered No. 04586709.
Re: [RFC] drm/bridge/sii902x: Fix EDID readback
On 2018-11-01 12:04, Fabrizio Castro wrote: > Hello Peter, > > Thank you for your feedback! > > snip > >>> Yeah, there is some issue with locking here, and that's coming down >>> from the fact that when we call into drm_get_edid, we implicitly call >>> i2c_transfer which ultimately locks the i2c adapter, and then calls >>> into our sii902x_i2c_bypass_select, which in turn calls into the regmap >>> functions and therefore we try and lock the same i2c adapter again, >>> driving the system into a deadlock. >>> Having the option of using "unlocked" flavours of reads and writes >>> is what we need here, but looking at drivers/base/regmap/regmap-i2c.c >>> I couldn't find anything suitable for my case, maybe Mark could advise >>> on this one? I am sure I overlooked something here, is there a better >>> way to address this? >> >> This recursive locking problem surfaces when an i2c mux is operated >> by writing commands on the same i2c bus that is muxed. When this >> happens for a typical i2c mux chip like nxp,pca9548 this can be kept >> local to that driver. However, when there are interactions with other >> parts of the kernel (e.g. if the i2c-mux is operated by gpio pins, >> and those gpio pins in turn are operated by accesses to the i2c bus >> that is muxed) this locked vs. unlocked thing would spread like >> wildfire. >> >> Since I did not like that wildfire, I came up with the "mux-locked" >> scheme. It is not appropriate everywhere, but in this case I think it >> is a perfect fit. By using it, you should be able to dodge all locking >> issues and it should be possible to keep the regmap usage as-is and the >> patch would in all likelihood be much less intrusive. >> >> For some background on "mux-locked" vs. "parent-locked" (which is what >> you have used in this RFC patch), see Documentation/i2c/i2c-topology. > > The "mux-locked" implementation was the one I first tried and I discovered > it doesn't work for me, as other traffic on the parent adapter can get in the > way. What we need for this device is no other traffic on the bus during the > "select transaction deselect" procedure, that's why I went with the parent > locking. Also this device needs a delay between stop and start conditions > while addressing the monitor. Ok, I thought the problem was that a delay was needed between the STOP of the command opening the gate and the START of the edid eeprom xfer, and that everything else worked normally. Too bad this wasn't the actual problem. Hmm. If it is indeed true that other xfers must never creep into the "select xfer deselect" procedure then you are indeed stuck with a parent-locking. But is that really the case? Could it be that the extra delay between STOP-START is only needed after the absolutely last xfer before the command closing the gate? If that problem description is correct, it should be possible to go back to a mux-locked gate, but then in your deselect implementation grab the i2c adapter lock manually - with i2c_lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER) - before the 30 us delay, then open code the command to close the gate with an unlocked i2c access, and finally release the i2c bus lock. That way you have ensured silence on the bus for the required time before closing the gate. You would still need to bypass regmap, but only in this one place (but maybe you should bypass regmap for opening the gate too, for symmetry). Cheers, Peter >> >> There are a couple of more comments below, inline. >> > > snip > > > +static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 > chan_id) > +{ > + struct sii902x *sii902x = i2c_mux_priv(mux); > + struct device *dev = &sii902x->i2c->dev; > + unsigned long timeout; > + u8 status; > + int ret; > + > + ret = __sii902x_update_bits(sii902x->i2c, SII902X_SYS_CTRL_DATA, > + SII902X_SYS_CTRL_DDC_BUS_REQ, > + SII902X_SYS_CTRL_DDC_BUS_REQ); > + > + timeout = jiffies + > + > msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); > + do { > + ret = __sii902x_read(sii902x->i2c, SII902X_SYS_CTRL_DATA, > +&status); > + if (ret) > + return ret; > + } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) && > +time_before(jiffies, timeout)); > + > + if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) { > + dev_err(dev, "Failed to acquire the i2c bus\n"); > + return -ETIMEDOUT; > + } > + > + ret = __sii902x_write(sii902x->i2c, SII902X_SYS_CTRL_DATA, > status); > + if (ret) > + return ret; >> >> Why do I not see a delay here? I thought the whole point of adding the i2c >> gate >> was that it enabled the introduction of a dela
[PATCH 3/4] ARM: dts: r8a77470: Add QSPI support
Add QSPI[01] support to the RZ/G1C SoC specific device tree. Signed-off-by: Fabrizio Castro --- arch/arm/boot/dts/r8a77470.dtsi | 34 ++ 1 file changed, 34 insertions(+) diff --git a/arch/arm/boot/dts/r8a77470.dtsi b/arch/arm/boot/dts/r8a77470.dtsi index e6407e5..04a8877 100644 --- a/arch/arm/boot/dts/r8a77470.dtsi +++ b/arch/arm/boot/dts/r8a77470.dtsi @@ -20,6 +20,8 @@ i2c2 = &i2c2; i2c3 = &i2c3; i2c4 = &i2c4; + spi0 = &qspi0; + spi1 = &qspi1; }; cpus { @@ -460,6 +462,38 @@ status = "disabled"; }; + qspi0: spi@e6b1 { + compatible = "renesas,qspi-r8a77470", "renesas,qspi"; + reg = <0 0xe6b1 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 918>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, + <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A77470_PD_ALWAYS_ON>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&cpg 918>; + status = "disabled"; + }; + + qspi1: spi@ee20 { + compatible = "renesas,qspi-r8a77470", "renesas,qspi"; + reg = <0 0xee20 0 0x2c>; + interrupts = ; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>, + <&dmac1 0xd1>, <&dmac1 0xd2>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A77470_PD_ALWAYS_ON>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&cpg 917>; + status = "disabled"; + }; + scif0: serial@e6e6 { compatible = "renesas,scif-r8a77470", "renesas,rcar-gen2-scif", "renesas,scif"; -- 2.7.4
[PATCH 1/4] spi: rspi: Add r8a77470 to the compatible list
Add r8a77470 to the list of examples with soctypes. No driver change is needed as "renesas,qspi" will activate the right code within the corresponding driver. Signed-off-by: Fabrizio Castro --- As per Mark Brown's comment on patch "dt-bindings: spi: rspi: Add r8a7744 to the compatible list", this patch doesn't come with the "dt-bindings" prefix in the title. Documentation/devicetree/bindings/spi/spi-rspi.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-rspi.txt b/Documentation/devicetree/bindings/spi/spi-rspi.txt index fc97ad6..421722b 100644 --- a/Documentation/devicetree/bindings/spi/spi-rspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-rspi.txt @@ -15,6 +15,7 @@ Required properties: - "renesas,qspi-r8a7743" (RZ/G1M) - "renesas,qspi-r8a7744" (RZ/G1N) - "renesas,qspi-r8a7745" (RZ/G1E) + - "renesas,qspi-r8a77470" (RZ/G1C) - "renesas,qspi-r8a7790" (R-Car H2) - "renesas,qspi-r8a7791" (R-Car M2-W) - "renesas,qspi-r8a7792" (R-Car V2H) -- 2.7.4
[PATCH 2/4] mtd: spi-nor: Add support for is25lp016d
The is25lp016d is found on the iwg23s from iWave, therefore add driver support for it so that we can upstream board support. Signed-off-by: Fabrizio Castro --- drivers/mtd/spi-nor/spi-nor.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 9407ca5..85d869b 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1352,6 +1352,8 @@ static const struct flash_info spi_nor_ids[] = { { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) }, { "is25lq040b", INFO(0x9d4013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25lp016d", INFO(0x9d6015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "is25lp080d", INFO(0x9d6014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256, -- 2.7.4
[PATCH 4/4] ARM: dts: iwg23s-sbc: Add QSPI flash support
This commit adds QSPI flash support to the iwg23s board specific device tree. Signed-off-by: Fabrizio Castro --- arch/arm/boot/dts/r8a77470-iwg23s-sbc.dts | 26 ++ 1 file changed, 26 insertions(+) diff --git a/arch/arm/boot/dts/r8a77470-iwg23s-sbc.dts b/arch/arm/boot/dts/r8a77470-iwg23s-sbc.dts index 18d2263..5245a1e 100644 --- a/arch/arm/boot/dts/r8a77470-iwg23s-sbc.dts +++ b/arch/arm/boot/dts/r8a77470-iwg23s-sbc.dts @@ -88,6 +88,11 @@ power-source = <1800>; }; + qspi0_pins: qspi0 { + groups = "qspi0_ctrl", "qspi0_data2"; + function = "qspi0"; + }; + scif1_pins: scif1 { groups = "scif1_data_b"; function = "scif1"; @@ -106,6 +111,27 @@ }; }; +&qspi0 { + pinctrl-0 = <&qspi0_pins>; + pinctrl-names = "default"; + + status = "okay"; + + /* WARNING - This device contains the bootloader. Handle with care. */ + flash: flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "issi,is25lp016d", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <13300>; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <1>; + m25p,fast-read; + spi-cpol; + spi-cpha; + }; +}; + &rwdt { timeout-sec = <60>; status = "okay"; -- 2.7.4
[PATCH 0/4] Add QSPI flash support to iwg23s
Dear All, this series includes all that is necessary to add QSPI flash support to the iwg23s board powered by the RZ/G1C SoC (a.k.a. r8a77470). Thanks, Fab Fabrizio Castro (4): spi: rspi: Add r8a77470 to the compatible list mtd: spi-nor: Add support for is25lp016d ARM: dts: r8a77470: Add QSPI support ARM: dts: iwg23s-sbc: Add QSPI flash support Documentation/devicetree/bindings/spi/spi-rspi.txt | 1 + arch/arm/boot/dts/r8a77470-iwg23s-sbc.dts | 26 + arch/arm/boot/dts/r8a77470.dtsi| 34 ++ drivers/mtd/spi-nor/spi-nor.c | 2 ++ 4 files changed, 63 insertions(+) -- 2.7.4
Re: [RFC] drm/bridge/sii902x: Fix EDID readback
On Wed, Oct 31, 2018 at 04:55:53PM +, Fabrizio Castro wrote: > Having the option of using "unlocked" flavours of reads and writes > is what we need here, but looking at drivers/base/regmap/regmap-i2c.c > I couldn't find anything suitable for my case, maybe Mark could advise > on this one? I am sure I overlooked something here, is there a better > way to address this? As Linus said you can go as far as writing your own read and write functions if you want to. That seems to be the easiest thing, though I am suspicous that you're having to use the I2C framework in this way - it doesn't sound terribly clever. You could I guess also just have a custom function for whatever register is doing all this stuff that just ignores regmap and bodge things that way while using regmap as normal for everything else. signature.asc Description: PGP signature
RE: [RFC] drm/bridge/sii902x: Fix EDID readback
Hello Peter, Thank you for your feedback! snip > > Yeah, there is some issue with locking here, and that's coming down > > from the fact that when we call into drm_get_edid, we implicitly call > > i2c_transfer which ultimately locks the i2c adapter, and then calls > > into our sii902x_i2c_bypass_select, which in turn calls into the regmap > > functions and therefore we try and lock the same i2c adapter again, > > driving the system into a deadlock. > > Having the option of using "unlocked" flavours of reads and writes > > is what we need here, but looking at drivers/base/regmap/regmap-i2c.c > > I couldn't find anything suitable for my case, maybe Mark could advise > > on this one? I am sure I overlooked something here, is there a better > > way to address this? > > This recursive locking problem surfaces when an i2c mux is operated > by writing commands on the same i2c bus that is muxed. When this > happens for a typical i2c mux chip like nxp,pca9548 this can be kept > local to that driver. However, when there are interactions with other > parts of the kernel (e.g. if the i2c-mux is operated by gpio pins, > and those gpio pins in turn are operated by accesses to the i2c bus > that is muxed) this locked vs. unlocked thing would spread like > wildfire. > > Since I did not like that wildfire, I came up with the "mux-locked" > scheme. It is not appropriate everywhere, but in this case I think it > is a perfect fit. By using it, you should be able to dodge all locking > issues and it should be possible to keep the regmap usage as-is and the > patch would in all likelihood be much less intrusive. > > For some background on "mux-locked" vs. "parent-locked" (which is what > you have used in this RFC patch), see Documentation/i2c/i2c-topology. The "mux-locked" implementation was the one I first tried and I discovered it doesn't work for me, as other traffic on the parent adapter can get in the way. What we need for this device is no other traffic on the bus during the "select transaction deselect" procedure, that's why I went with the parent locking. Also this device needs a delay between stop and start conditions while addressing the monitor. > > There are a couple of more comments below, inline. > snip > >>> > >>> +static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 > >>> chan_id) > >>> +{ > >>> + struct sii902x *sii902x = i2c_mux_priv(mux); > >>> + struct device *dev = &sii902x->i2c->dev; > >>> + unsigned long timeout; > >>> + u8 status; > >>> + int ret; > >>> + > >>> + ret = __sii902x_update_bits(sii902x->i2c, SII902X_SYS_CTRL_DATA, > >>> + SII902X_SYS_CTRL_DDC_BUS_REQ, > >>> + SII902X_SYS_CTRL_DDC_BUS_REQ); > >>> + > >>> + timeout = jiffies + > >>> + > >>> msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); > >>> + do { > >>> + ret = __sii902x_read(sii902x->i2c, SII902X_SYS_CTRL_DATA, > >>> +&status); > >>> + if (ret) > >>> + return ret; > >>> + } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) && > >>> +time_before(jiffies, timeout)); > >>> + > >>> + if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) { > >>> + dev_err(dev, "Failed to acquire the i2c bus\n"); > >>> + return -ETIMEDOUT; > >>> + } > >>> + > >>> + ret = __sii902x_write(sii902x->i2c, SII902X_SYS_CTRL_DATA, > >>> status); > >>> + if (ret) > >>> + return ret; > > Why do I not see a delay here? I thought the whole point of adding the i2c > gate > was that it enabled the introduction of a delay between opening for edid > reading > and the actual edid reading? The delay is needed between STOP and START condition while in passthrough mode. __i2c_transfer gets called after the select callback (which enables the passthrough mode), when __i2c_transfer is done it generates a STOP condition and then we call into the deselect callback. We need a delay between __i2c_transfer and deselect. > > >>> + return 0; > >>> +} > >>> + > >>> +static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 > >>> chan_id) > >>> +{ > >>> + struct sii902x *sii902x = i2c_mux_priv(mux); > >>> + struct device *dev = &sii902x->i2c->dev; > >>> + unsigned long timeout; > >>> + unsigned int retries; > >>> + u8 status; > >>> + int ret; > >>> + > >>> + udelay(30); Here is where we need the delay snip > >>> @@ -433,6 +558,22 @@ static int sii902x_probe(struct i2c_client *client, > >>> > >>> i2c_set_clientdata(client, sii902x); > >>> > >>> + sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev, > >>> + 1, 0, I2C_MUX_GATE, > > Just use I2C_MUX_GATE | I2C_MUX_LOCKED for the flags argument, and you should > be > able to make normal i2c accesses.