Re: [PATCH 4.19 regression fix 2/2] staging: vboxvideo: Change address of scanout buffer on page-flip
Hi Hans, On 09/10/2018 11:30 AM, Hans de Goede wrote: Commit 2408898e3b6c ("staging: vboxvideo: Add page-flip support") only calls vbox_crtc_do_set_base() on page-flips, but despite that function's name it only pins the new fb, unpins the old fb and sets vbox_crtc->fb_offset. It does not program the hardware to scan out at the new vbox_crtc->fb_offset value. Has that always been the case of vbox_crtc_do_set_base()? Or was there a recent commit that changed that behavior? I tested this patch using a Weston EGL mock navigation test app around 4.14 time-frame, that exercises page flip and it was scanning out the new fb, but maybe what I was looking at was a scan-out of an old/now stale fb from a previous page-flip. In any case thanks for fixing. Steve This was causing only every other frame (assuming page-flipping between 2 buffers) to be shown since we kept scanning out of the old (now unpinned!) buffer. This commit fixes this by adding code to vbox_crtc_page_flip() to tell the hardware to scanout from the new fb_offset. Fixes: 2408898e3b6c ("staging: vboxvideo: Add page-flip support") Cc: Steve Longerbeam Signed-off-by: Hans de Goede --- drivers/staging/vboxvideo/vbox_mode.c | 5 + 1 file changed, 5 insertions(+) diff --git a/drivers/staging/vboxvideo/vbox_mode.c b/drivers/staging/vboxvideo/vbox_mode.c index a83eac8668d0..79836c8fb909 100644 --- a/drivers/staging/vboxvideo/vbox_mode.c +++ b/drivers/staging/vboxvideo/vbox_mode.c @@ -323,6 +323,11 @@ static int vbox_crtc_page_flip(struct drm_crtc *crtc, if (rc) return rc; + mutex_lock(&vbox->hw_mutex); + vbox_set_view(crtc); + vbox_do_modeset(crtc, &crtc->mode); + mutex_unlock(&vbox->hw_mutex); + spin_lock_irqsave(&drm->event_lock, flags); if (event) ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[RESEND PATCH v7 10/17] media: staging/imx: of: Remove recursive graph walk
After moving to subdev notifiers, it's no longer necessary to recursively walk the OF graph, because the subdev notifiers will discover and add devices from the graph for us. So the recursive of_parse_subdev() function is gone, replaced with of_add_csi() which adds only the CSI port fwnodes to the imx-media root notifier. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-of.c | 106 ++- 1 file changed, 8 insertions(+), 98 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index acde372c6795..1c9175433ba6 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,74 +20,19 @@ #include #include "imx-media.h" -static int of_get_port_count(const struct device_node *np) +static int of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np) { - struct device_node *ports, *child; - int num = 0; - - /* check if this node has a ports subnode */ - ports = of_get_child_by_name(np, "ports"); - if (ports) - np = ports; - - for_each_child_of_node(np, child) - if (of_node_cmp(child->name, "port") == 0) - num++; - - of_node_put(ports); - return num; -} - -/* - * find the remote device node given local endpoint node - */ -static bool of_get_remote(struct device_node *epnode, - struct device_node **remote_node) -{ - struct device_node *rp, *rpp; - struct device_node *remote; - bool is_csi_port; - - rp = of_graph_get_remote_port(epnode); - rpp = of_graph_get_remote_port_parent(epnode); - - if (of_device_is_compatible(rpp, "fsl,imx6q-ipu")) { - /* the remote is one of the CSI ports */ - remote = rp; - of_node_put(rpp); - is_csi_port = true; - } else { - remote = rpp; - of_node_put(rp); - is_csi_port = false; - } - - if (!of_device_is_available(remote)) { - of_node_put(remote); - *remote_node = NULL; - } else { - *remote_node = remote; - } - - return is_csi_port; -} - -static int -of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np, - bool is_csi_port) -{ - int i, num_ports, ret; + int ret; - if (!of_device_is_available(sd_np)) { + if (!of_device_is_available(csi_np)) { dev_dbg(imxmd->md.dev, "%s: %s not enabled\n", __func__, - sd_np->name); + csi_np->name); /* unavailable is not an error */ return 0; } - /* register this subdev with async notifier */ - ret = imx_media_add_async_subdev(imxmd, of_fwnode_handle(sd_np), -NULL); + /* add CSI fwnode to async notifier */ + ret = imx_media_add_async_subdev(imxmd, of_fwnode_handle(csi_np), NULL); if (ret) { if (ret == -EEXIST) { /* already added, everything is fine */ @@ -98,42 +43,7 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np, return ret; } - /* -* the ipu-csi has one sink port. The source pads are not -* represented in the device tree by port nodes, but are -* described by the internal pads and links later. -*/ - num_ports = is_csi_port ? 1 : of_get_port_count(sd_np); - - for (i = 0; i < num_ports; i++) { - struct device_node *epnode = NULL, *port, *remote_np; - - port = is_csi_port ? sd_np : of_graph_get_port_by_id(sd_np, i); - if (!port) - continue; - - for_each_child_of_node(port, epnode) { - bool remote_is_csi; - - remote_is_csi = of_get_remote(epnode, &remote_np); - if (!remote_np) - continue; - - ret = of_parse_subdev(imxmd, remote_np, remote_is_csi); - of_node_put(remote_np); - if (ret) - break; - } - - if (port != sd_np) - of_node_put(port); - if (ret) { - of_node_put(epnode); - break; - } - } - - return ret; + return 0; } int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, @@ -147,7 +57,7 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, if (!csi_np) break; - ret = of_parse_subdev(imxmd, csi_np, true); + ret = of_add_csi(im
[RESEND PATCH v7 09/17] media: imx: mipi csi-2: Register a subdev notifier
Parse neighbor remote devices on the MIPI CSI-2 input port, add them to a subdev notifier, and register the subdev notifier for the MIPI CSI-2 receiver, by calling v4l2_async_register_fwnode_subdev(). csi2_parse_endpoints() is modified to be the parse_endpoint callback. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx6-mipi-csi2.c | 31 ++ 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 7b457a4b7df5..d60a52cfc69c 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -551,35 +551,34 @@ static const struct v4l2_subdev_internal_ops csi2_internal_ops = { .registered = csi2_registered, }; -static int csi2_parse_endpoints(struct csi2_dev *csi2) +static int csi2_parse_endpoint(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) { - struct device_node *node = csi2->dev->of_node; - struct device_node *epnode; - struct v4l2_fwnode_endpoint ep; + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct csi2_dev *csi2 = sd_to_dev(sd); - epnode = of_graph_get_endpoint_by_regs(node, 0, -1); - if (!epnode) { - v4l2_err(&csi2->sd, "failed to get sink endpoint node\n"); + if (!fwnode_device_is_available(asd->match.fwnode)) { + v4l2_err(&csi2->sd, "remote is not available\n"); return -EINVAL; } - v4l2_fwnode_endpoint_parse(of_fwnode_handle(epnode), &ep); - of_node_put(epnode); - - if (ep.bus_type != V4L2_MBUS_CSI2) { + if (vep->bus_type != V4L2_MBUS_CSI2) { v4l2_err(&csi2->sd, "invalid bus type, must be MIPI CSI2\n"); return -EINVAL; } - csi2->bus = ep.bus.mipi_csi2; + csi2->bus = vep->bus.mipi_csi2; dev_dbg(csi2->dev, "data lanes: %d\n", csi2->bus.num_data_lanes); dev_dbg(csi2->dev, "flags: 0x%08x\n", csi2->bus.flags); + return 0; } static int csi2_probe(struct platform_device *pdev) { + unsigned int sink_port = 0; struct csi2_dev *csi2; struct resource *res; int ret; @@ -601,10 +600,6 @@ static int csi2_probe(struct platform_device *pdev) csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; csi2->sd.grp_id = IMX_MEDIA_GRP_ID_CSI2; - ret = csi2_parse_endpoints(csi2); - if (ret) - return ret; - csi2->pllref_clk = devm_clk_get(&pdev->dev, "ref"); if (IS_ERR(csi2->pllref_clk)) { v4l2_err(&csi2->sd, "failed to get pll reference clock\n"); @@ -654,7 +649,9 @@ static int csi2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &csi2->sd); - ret = v4l2_async_register_subdev(&csi2->sd); + ret = v4l2_async_register_fwnode_subdev( + &csi2->sd, sizeof(struct v4l2_async_subdev), + &sink_port, 1, csi2_parse_endpoint); if (ret) goto dphy_off; -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[RESEND PATCH v7 11/17] media: staging/imx: Loop through all registered subdevs for media links
The root imx-media notifier no longer sees all bound subdevices because some of them will be bound to subdev notifiers. So imx_media_create_links() now needs to loop through all subdevices registered with the v4l2-device, not just the ones in the root notifier's done list. This should be safe because imx_media_create_of_links() checks if a fwnode link already exists before creating. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-dev.c | 16 ++-- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 1931d1b038dc..718954f969bd 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -175,7 +175,7 @@ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, } /* - * create the media links for all subdevs that registered async. + * Create the media links for all subdevs that registered. * Called after all async subdevs have bound. */ static int imx_media_create_links(struct v4l2_async_notifier *notifier) @@ -184,14 +184,7 @@ static int imx_media_create_links(struct v4l2_async_notifier *notifier) struct v4l2_subdev *sd; int ret; - /* -* Only links are created between subdevices that are known -* to the async notifier. If there are other non-async subdevices, -* they were created internally by some subdevice (smiapp is one -* example). In those cases it is expected the subdevice is -* responsible for creating those internal links. -*/ - list_for_each_entry(sd, ¬ifier->done, async_list) { + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { switch (sd->grp_id) { case IMX_MEDIA_GRP_ID_VDIC: case IMX_MEDIA_GRP_ID_IC_PRP: @@ -211,7 +204,10 @@ static int imx_media_create_links(struct v4l2_async_notifier *notifier) imx_media_create_csi_of_links(imxmd, sd); break; default: - /* this is an external fwnode subdev */ + /* +* if this subdev has fwnode links, create media +* links for them. +*/ imx_media_create_of_links(imxmd, sd); break; } -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[RESEND PATCH v7 08/17] media: imx: csi: Register a subdev notifier
Parse neighbor remote devices on the CSI port, and add them to a subdev notifier, by calling v4l2_async_notifier_parse_fwnode_endpoints_by_port() using the CSI's port id. And register the subdev notifier for the CSI. Signed-off-by: Steve Longerbeam --- Changes since v6: - none Changes since v5: - add call to v4l2_async_notifier_init(). Changes since v4: - none Changes since v3: - v4l2_async_register_fwnode_subdev() no longer supports parsing port sub-devices, so call v4l2_async_notifier_parse_fwnode_endpoints_by_port() directly. --- drivers/staging/media/imx/imx-media-csi.c | 57 ++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 4acdd7ae612b..bca13846ce6d 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1783,6 +1783,61 @@ static const struct v4l2_subdev_internal_ops csi_internal_ops = { .unregistered = csi_unregistered, }; +static int imx_csi_parse_endpoint(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) +{ + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; +} + +static int imx_csi_async_register(struct csi_priv *priv) +{ + struct v4l2_async_notifier *notifier; + struct fwnode_handle *fwnode; + unsigned int port; + int ret; + + notifier = kzalloc(sizeof(*notifier), GFP_KERNEL); + if (!notifier) + return -ENOMEM; + + v4l2_async_notifier_init(notifier); + + fwnode = dev_fwnode(priv->dev); + + /* get this CSI's port id */ + ret = fwnode_property_read_u32(fwnode, "reg", &port); + if (ret < 0) + goto out_free; + + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( + priv->dev->parent, notifier, sizeof(struct v4l2_async_subdev), + port, imx_csi_parse_endpoint); + if (ret < 0) + goto out_cleanup; + + ret = v4l2_async_subdev_notifier_register(&priv->sd, notifier); + if (ret < 0) + goto out_cleanup; + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret < 0) + goto out_unregister; + + priv->sd.subdev_notifier = notifier; + + return 0; + +out_unregister: + v4l2_async_notifier_unregister(notifier); +out_cleanup: + v4l2_async_notifier_cleanup(notifier); +out_free: + kfree(notifier); + + return ret; +} + static int imx_csi_probe(struct platform_device *pdev) { struct ipu_client_platformdata *pdata; @@ -1852,7 +1907,7 @@ static int imx_csi_probe(struct platform_device *pdev) goto free; } - ret = v4l2_async_register_subdev(&priv->sd); + ret = imx_csi_async_register(priv); if (ret) goto free; -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[RESEND PATCH v7 14/17] media: staging/imx: TODO: Remove one assumption about OF graph parsing
The move to subdev notifiers fixes one assumption of OF graph parsing. If a subdevice has non-video related ports, the subdev driver knows not to follow those ports when adding remote devices to its subdev notifier. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/TODO | 29 +++-- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO index 9eb7326f3fc6..aeeb15494a49 100644 --- a/drivers/staging/media/imx/TODO +++ b/drivers/staging/media/imx/TODO @@ -17,29 +17,15 @@ decided whether this feature is useful enough to make it generally available by exporting to v4l2-core. -- The OF graph is walked at probe time to form the list of fwnodes to - be passed to v4l2_async_notifier_register(), starting from the IPU - CSI ports. And after all async subdevices have been bound, - v4l2_fwnode_parse_link() is used to form the media links between - the entities discovered by walking the OF graph. +- After all async subdevices have been bound, v4l2_fwnode_parse_link() + is used to form the media links between the devices discovered in + the OF graph. While this approach allows support for arbitrary OF graphs, there are some assumptions for this to work: - 1. All port parent nodes reachable in the graph from the IPU CSI - ports bind to V4L2 async subdevice drivers. - - If a device has mixed-use ports such as video plus audio, the - endpoints from the audio ports are followed to devices that must - bind to V4L2 subdevice drivers, and not for example, to an ALSA - driver or a non-V4L2 media driver. If the device were bound to - such a driver, imx-media would never get an async completion - notification because the device fwnode was added to the async - list, but the driver does not interface with the V4L2 async - framework. - - 2. Every port reachable in the graph is treated as a media pad, - owned by the V4L2 subdevice that is bound to the port's parent. + 1. If a port owned by a device in the graph has endpoint nodes, the + port is treated as a media pad. This presents problems for devices that don't make this port = pad assumption. Examples are SMIAPP compatible cameras which define only @@ -54,9 +40,8 @@ possible long-term solution is to implement a subdev API that maps a port id to a media pad index. - 3. Every endpoint of a port reachable in the graph is treated as - a media link, between V4L2 subdevices that are bound to the - port parents of the local and remote endpoints. + 2. Every endpoint of a port owned by a device in the graph is treated + as a media link. Which means a port must not contain mixed-use endpoints, they must all refer to media links between V4L2 subdevices. -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[RESEND PATCH v7 12/17] media: staging/imx: Rename root notifier
Rename the imx-media root async notifier from "subdev_notifier" to simply "notifier", so as not to confuse it with true subdev notifiers. No functional changes. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-dev.c | 14 +++--- drivers/staging/media/imx/imx-media.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 718954f969bd..a9faee4b2495 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -29,7 +29,7 @@ static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) { - return container_of(n, struct imx_media_dev, subdev_notifier); + return container_of(n, struct imx_media_dev, notifier); } /* @@ -113,7 +113,7 @@ int imx_media_add_async_subdev(struct imx_media_dev *imxmd, list_add_tail(&imxasd->list, &imxmd->asd_list); - imxmd->subdev_notifier.num_subdevs++; + imxmd->notifier.num_subdevs++; dev_dbg(imxmd->md.dev, "%s: added %s, match type %s\n", __func__, np ? np->name : devname, np ? "FWNODE" : "DEVNAME"); @@ -532,7 +532,7 @@ static int imx_media_probe(struct platform_device *pdev) goto unreg_dev; } - num_subdevs = imxmd->subdev_notifier.num_subdevs; + num_subdevs = imxmd->notifier.num_subdevs; /* no subdevs? just bail */ if (num_subdevs == 0) { @@ -552,10 +552,10 @@ static int imx_media_probe(struct platform_device *pdev) subdevs[i++] = &imxasd->asd; /* prepare the async subdev notifier and register it */ - imxmd->subdev_notifier.subdevs = subdevs; - imxmd->subdev_notifier.ops = &imx_media_subdev_ops; + imxmd->notifier.subdevs = subdevs; + imxmd->notifier.ops = &imx_media_subdev_ops; ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, - &imxmd->subdev_notifier); + &imxmd->notifier); if (ret) { v4l2_err(&imxmd->v4l2_dev, "v4l2_async_notifier_register failed with %d\n", ret); @@ -580,7 +580,7 @@ static int imx_media_remove(struct platform_device *pdev) v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n"); - v4l2_async_notifier_unregister(&imxmd->subdev_notifier); + v4l2_async_notifier_unregister(&imxmd->notifier); imx_media_remove_internal_subdevs(imxmd); v4l2_device_unregister(&imxmd->v4l2_dev); media_device_unregister(&imxmd->md); diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 57bd094cf765..227b79ccc194 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -150,7 +150,7 @@ struct imx_media_dev { /* for async subdev registration */ struct list_head asd_list; - struct v4l2_async_notifier subdev_notifier; + struct v4l2_async_notifier notifier; }; enum codespace_sel { -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[RESEND PATCH v7 13/17] media: staging/imx: Switch to v4l2_async_notifier_add_*_subdev
Switch to v4l2_async_notifier_add_*_subdev() when adding async subdevs to the imx-media root notifier. This removes the need to check for an already added asd, since v4l2_async_notifier_add_*_subdev() does this check. Also no need to allocate a subdevs array when registering the root notifier, or keeping an internal master asd_list, since this is moved to the notifier's asd_list. Signed-off-by: Steve Longerbeam --- Changes since v6: - none Changes since v5: - remove reference to notifier.num_subdevs and call v4l2_async_notifier_init(). Suggested by Sakari Ailus. --- drivers/staging/media/imx/imx-media-dev.c | 121 +- .../staging/media/imx/imx-media-internal-sd.c | 5 +- drivers/staging/media/imx/imx-media.h | 4 +- 3 files changed, 36 insertions(+), 94 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index a9faee4b2495..481840195071 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -33,43 +33,10 @@ static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) } /* - * Find an asd by fwnode or device name. This is called during - * driver load to form the async subdev list and bind them. - */ -static struct v4l2_async_subdev * -find_async_subdev(struct imx_media_dev *imxmd, - struct fwnode_handle *fwnode, - const char *devname) -{ - struct imx_media_async_subdev *imxasd; - struct v4l2_async_subdev *asd; - - list_for_each_entry(imxasd, &imxmd->asd_list, list) { - asd = &imxasd->asd; - switch (asd->match_type) { - case V4L2_ASYNC_MATCH_FWNODE: - if (fwnode && asd->match.fwnode == fwnode) - return asd; - break; - case V4L2_ASYNC_MATCH_DEVNAME: - if (devname && !strcmp(asd->match.device_name, - devname)) - return asd; - break; - default: - break; - } - } - - return NULL; -} - - -/* - * Adds a subdev to the async subdev list. If fwnode is non-NULL, adds - * the async as a V4L2_ASYNC_MATCH_FWNODE match type, otherwise as - * a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name of the - * given platform_device. This is called during driver load when + * Adds a subdev to the root notifier's async subdev list. If fwnode is + * non-NULL, adds the async as a V4L2_ASYNC_MATCH_FWNODE match type, + * otherwise as a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name + * of the given platform_device. This is called during driver load when * forming the async subdev list. */ int imx_media_add_async_subdev(struct imx_media_dev *imxmd, @@ -80,47 +47,34 @@ int imx_media_add_async_subdev(struct imx_media_dev *imxmd, struct imx_media_async_subdev *imxasd; struct v4l2_async_subdev *asd; const char *devname = NULL; - int ret = 0; - - mutex_lock(&imxmd->mutex); + int ret; - if (pdev) + if (fwnode) { + asd = v4l2_async_notifier_add_fwnode_subdev( + &imxmd->notifier, fwnode, sizeof(*imxasd)); + } else { devname = dev_name(&pdev->dev); - - /* return -EEXIST if this asd already added */ - if (find_async_subdev(imxmd, fwnode, devname)) { - dev_dbg(imxmd->md.dev, "%s: already added %s\n", - __func__, np ? np->name : devname); - ret = -EEXIST; - goto out; + asd = v4l2_async_notifier_add_devname_subdev( + &imxmd->notifier, devname, sizeof(*imxasd)); } - imxasd = devm_kzalloc(imxmd->md.dev, sizeof(*imxasd), GFP_KERNEL); - if (!imxasd) { - ret = -ENOMEM; - goto out; - } - asd = &imxasd->asd; - - if (fwnode) { - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = fwnode; - } else { - asd->match_type = V4L2_ASYNC_MATCH_DEVNAME; - asd->match.device_name = devname; - imxasd->pdev = pdev; + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + if (ret == -EEXIST) + dev_dbg(imxmd->md.dev, "%s: already added %s\n", + __func__, np ? np->name : devname); + return ret; } - list_add_tail(&imxasd->list, &imxmd->asd_list); + imxasd = to_imx_media_asd(asd); - imxmd->notifier.num_subdevs++; + if (devname) + imxasd->pdev = pdev; dev_dbg(i
[PATCH v4 04/11] media: imx: Fix field negotiation
IDMAC interlaced scan, a.k.a. interweave, should be enabled in the IDMAC output channels only if the IDMAC output pad field type is 'seq-bt' or 'seq-tb', and field type at the capture interface is 'interlaced*'. V4L2_FIELD_HAS_BOTH() macro should not be used on the input to determine enabling interlaced/interweave scan. That macro includes the 'interlaced' field types, and in those cases the data is already interweaved with top/bottom field lines. The CSI will capture whole frames when the source specifies alternate field mode. So the CSI also enables interweave for alternate input field type and the field type at capture interface is interlaced. Fix the logic for setting field type in try_fmt in CSI entity. The behavior should be: - No restrictions on field type at sink pad. - At the output pads, allow sequential fields in TB order, if the sink pad field type is sequential or alternate. Otherwise passthrough the field type from sink to source pad. Move this logic to new function csi_try_field(). These changes result in the following allowed field transformations from CSI sink -> source pads (all other field types at sink are passed through to source): seq-tb -> seq-tb seq-bt -> seq-tb alternate -> seq-tb In a future patch, the CSI sink -> source will allow: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt This will require supporting interweave with top/bottom line swapping. Until then seq-bt is not allowed at the CSI source pad because there is no way to swap top/bottom lines when interweaving to INTERLACED_BT -- note that despite the name, INTERLACED_BT is top-bottom order in memory. The BT in this case refers to field dominance: the bottom lines are older in time than the top lines. The capture interface device allows selecting IDMAC interweave by choosing INTERLACED_TB if the CSI/PRPENCVF source pad is seq-tb and INTERLACED_BT if the source pad is seq-bt (for future support of seq-bt). Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-ic-prpencvf.c | 21 -- drivers/staging/media/imx/imx-media-capture.c | 14 drivers/staging/media/imx/imx-media-csi.c | 64 ++- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index af7224846bd5..1a03d4c9d7b8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -354,12 +354,13 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; outcc = vdev->cc; ipu_cpmem_zero(channel); @@ -369,6 +370,15 @@ static int prp_setup_channel(struct prp_priv *priv, image.rect.width = image.pix.width; image.rect.height = image.pix.height; + /* +* If the field type at capture interface is interlaced, and +* the output IDMAC pad is sequential, enable interweave at +* the IDMAC output channel. +*/ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && + channel == priv->out_ch; + if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); swap(image.rect.width, image.rect.height); @@ -409,9 +419,7 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field) && - channel == priv->out_ch) + if (interweave) ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, image.pix.pixelformat); @@ -839,8 +847,7 @@ static void prp_try_fmt(struct prp_priv *priv, infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { - if (sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..01ec9443de55 100644 --- a/drivers/staging/media/i
[PATCH v4 05/11] media: imx-csi: Double crop height for alternate fields at sink
If the incoming sink field type is alternate, the reset crop height and crop height bounds must be set to twice the incoming height, because in alternate field mode, upstream will report only the lines for a single field, and the CSI captures the whole frame. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 20 +++- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 764db0d7c6d3..ca6328f53b75 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1138,6 +1138,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1145,6 +1147,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left &= ~0x3; crop->width &= ~0x7; + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; + /* * FIXME: not sure why yet, but on interlaced bt.656, * changing the vertical cropping causes loss of vertical @@ -1154,12 +1160,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1399,6 +1405,8 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; @@ -1526,6 +1534,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 06/11] media: imx: interweave and odd-chroma-row skip are incompatible
If IDMAC interweaving is enabled in a write channel, the channel must write the odd chroma rows for 4:2:0 formats. Skipping writing the odd chroma rows produces corrupted captured 4:2:0 images when interweave is enabled. Reported-by: Krzysztof Hałasa Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++-- drivers/staging/media/imx/imx-media-csi.c | 8 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 1a03d4c9d7b8..cf76b0432371 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -391,12 +391,17 @@ static int prp_setup_channel(struct prp_priv *priv, image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* +* Skip writing U and V components to odd rows in the output +* channels for planar 4:2:0 (but not when enabling IDMAC +* interweaving, they are incompatible). +*/ + if (!interweave && (channel == priv->out_ch || + channel == priv->rot_out_ch)) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index ca6328f53b75..4b075bc949de 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,8 +457,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* +* Skip writing U and V components to odd rows (but not +* when enabling IDMAC interweaving, they are incompatible). +*/ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 07/11] media: imx-csi: Allow skipping odd chroma rows for YVU420
Skip writing U/V components to odd rows for YVU420 in addition to YUV420 and NV12. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 4b075bc949de..6dd53a8e35d2 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -452,6 +452,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 09/11] media: imx-csi: Move crop/compose reset after filling default mbus fields
If caller passes un-initialized field type V4L2_FIELD_ANY to CSI sink pad, the reset CSI crop window would not be correct, because the crop window depends on a valid input field type. To fix move the reset of crop and compose windows to after the call to imx_media_fill_default_mbus_fields(). Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 27 --- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 6dd53a8e35d2..679295da5dde 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1405,19 +1405,6 @@ static void csi_try_fmt(struct csi_priv *priv, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - /* Reset crop and compose rectangles */ - crop->left = 0; - crop->top = 0; - crop->width = sdformat->format.width; - crop->height = sdformat->format.height; - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); - compose->left = 0; - compose->top = 0; - compose->width = crop->width; - compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); if (!*cc) { @@ -1433,6 +1420,20 @@ static void csi_try_fmt(struct csi_priv *priv, imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 03/11] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index a9d2501500a1..d41df8034c5b 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -273,9 +273,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -286,9 +287,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index ad66f007d395..5e3aa4f3a1dd 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index f44a35192313..e888c66b9d9d 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -255,7 +255,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 10/11] media: imx: Allow interweave with top/bottom lines swapped
Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-ic-prpencvf.c | 17 +++- drivers/staging/media/imx/imx-media-csi.c | 46 + 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..1499b0c62d74 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,8 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + u32 interweave_offset; /* interweave line offset to swap + top/bottom lines */ struct completion last_eof_comp; }; @@ -235,6 +237,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (ch == priv->out_ch) + phys += priv->interweave_offset; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -388,6 +393,13 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + priv->interweave_offset = 0; + + if (interweave && image.pix.field == V4L2_FIELD_INTERLACED_BT) { + image.rect.top = 1; + priv->interweave_offset = image.pix.bytesperline; + } + image.phys0 = addr0; image.phys1 = addr1; @@ -425,7 +437,10 @@ static int prp_setup_channel(struct prp_priv *priv, ipu_cpmem_set_rotation(channel, rot_mode); if (interweave) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + ipu_cpmem_interlaced_scan(channel, + priv->interweave_offset ? + -image.pix.bytesperline : + image.pix.bytesperline, image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 679295da5dde..592f7d6edec1 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,8 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + u32 interweave_offset; /* interweave line offset to swap + top/bottom lines */ struct completion last_eof_comp; }; @@ -286,7 +288,8 @@ static void csi_vb2_buf_done(struct csi_priv *priv) if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num); - ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys); + ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, +phys + priv->interweave_offset); } static irqreturn_t csi_idmac_eof_interrupt(int irq, void *dev_id) @@ -396,10 +399,10 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv, static int csi_idmac_setup_channel(struct csi_priv *priv) { struct imx_media_video_dev *vdev = priv->vdev; + bool passthrough, interweave, interweave_swap; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; struct v4l2_mbus_framefmt *outfmt; - bool passthrough, interweave; struct ipu_image image; u32 passthrough_bits; u32 passthrough_cycles; @@ -433,6 +436,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT;
[PATCH v4 08/11] media: imx: vdic: rely on VDIC for correct field order
prepare_vdi_in_buffers() was setting up the dma pointers as if the VDIC is always programmed to receive the fields in bottom-top order, i.e. as if ipu_vdi_set_field_order() only programs BT order in the VDIC. But that's not true, ipu_vdi_set_field_order() is working correctly. So fix prepare_vdi_in_buffers() to give the VDIC the fields in whatever order they were received by the video source, and rely on the VDIC to sort out which is top and which is bottom. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-vdic.c | 12 ++-- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..4a890714193e 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, switch (priv->fieldtype) { case V4L2_FIELD_SEQ_TB: - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; case V4L2_FIELD_SEQ_BT: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; break; + case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; break; - default: - /* assume V4L2_FIELD_INTERLACED_TB */ - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; } ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 02/11] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-csi.c | 132 +++--- drivers/staging/media/imx/imx-media-csi.c | 13 +-- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 97 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index 954eefe144e2..759fcd724ff9 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); if (ret < 0) return ret; /* set default sensor frame width and height */ - width = mbus_fmt->width; - height = mbus_fmt->height; + width = infmt->width; + height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + height *= 2;
Re: [PATCH v4 02/11] gpu: ipu-csi: Swap fields according to input/output field types
Hi Philipp, On 10/05/2018 02:44 AM, Philipp Zabel wrote: Hi Steve, On Thu, 2018-10-04 at 11:53 -0700, Steve Longerbeam wrote: + + /* framelines for NTSC / PAL */ + height = (std & V4L2_STD_525_60) ? 525 : 625; I think this is a bit convoluted. Instead of initializing std, then possibly changing it, and then comparing to the inital value, and then checking it again to determine the new height, why not just: if (width == 720 && height == 480) { std = V4L2_STD_NTSC; height = 525; } else if (width == 720 && height == 576) { std = V4L2_STD_PAL; height = 625; } else { dev_err(csi->ipu->dev, "Unsupported interlaced video mode\n"); ret = -EINVAL; goto out_unlock; } ? Yes that was a bit convoluted, fixed. /* * if cycles is set, we need to handle this over multiple cycles as * generic/bayer data */ - if (is_parallel_bus(&priv->upstream_ep) && incc->cycles) { - if_fmt.width *= incc->cycles; If the input format width passed to ipu_csi_init_interface is not multiplied by the number of cycles per pixel anymore, width in the CSI_SENS_FRM_SIZE register will be set to the unmultiplied value from infmt. This breaks 779680e2e793 ("media: imx: add support for RGB565_2X8 on parallel bus"). Oops, that was a mistake, thanks for catching, fixed. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v4 03/11] gpu: ipu-v3: Add planar support to interlaced scan
On 10/05/2018 02:48 AM, Philipp Zabel wrote: On Thu, 2018-10-04 at 11:53 -0700, Steve Longerbeam wrote: To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index a9d2501500a1..d41df8034c5b 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -273,9 +273,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -286,9 +287,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); [...] Reviewed-by: Philipp Zabel and Acked-by: Philipp Zabel to be merged with the rest of the series via the media tree. I'll take care not to introduce nontrivial conflicts in imx-drm. Ok thanks. Hans, for v5 I will just include the two IPU patches as before. As Philipp stated, he is OK with merging them to the media tree (after his ack of course), along with the rest of the patches in this series. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v4 10/11] media: imx: Allow interweave with top/bottom lines swapped
Hi Philipp, On 10/05/2018 03:43 AM, Philipp Zabel wrote: Hi Steve, On Thu, 2018-10-04 at 11:54 -0700, Steve Longerbeam wrote: Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-ic-prpencvf.c | 17 +++- drivers/staging/media/imx/imx-media-csi.c | 46 + 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..1499b0c62d74 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,8 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + u32 interweave_offset; /* interweave line offset to swap + top/bottom lines */ We have to store this instead of using vdev->fmt.fmt.bytesperline because this potentially is the pre-rotation stride instead? interweave_offset was used by prp_vb2_buf_done() below, but in fact that function is passed the non-rotation IDMAC channel (priv->out_ch) _only_ if rotation is not enabled, so it is actually safe to use vdev->fmt.fmt.bytesperline for the interweave offset in prp_vb2_buf_done(). So I've gotten rid of interweave_offset in both imx-ic-prpencvf.c and imx-media-csi.c, and replaced with a boolean interweave_swap as you suggested. I agree it is much cleaner. diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 679295da5dde..592f7d6edec1 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,8 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + u32 interweave_offset; /* interweave line offset to swap + top/bottom lines */ This doesn't seem necessary. Since there is no rotation here, the offset is just vdev->fmt.fmt.pix.bytesperline if interweave_swap is enabled. Maybe turn this into a bool interweave_swap? Agreed. struct completion last_eof_comp; }; @@ -286,7 +288,8 @@ static void csi_vb2_buf_done(struct csi_priv *priv) if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num); - ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys); + ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, +phys + priv->interweave_offset); } static irqreturn_t csi_idmac_eof_interrupt(int irq, void *dev_id) @@ -396,10 +399,10 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv, static int csi_idmac_setup_channel(struct csi_priv *priv) { struct imx_media_video_dev *vdev = priv->vdev; + bool passthrough, interweave, interweave_swap; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; struct v4l2_mbus_framefmt *outfmt; - bool passthrough, interweave; struct ipu_image image; u32 passthrough_bits; u32 passthrough_cycles; @@ -433,6 +436,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; Although this could just as well be recalculated in csi_vb2_buf_done. In the future yes, when we add support for alternate mode (I assume that's what you are getting at?). Apart from that, Reviewed-by: Philipp Zabel Thanks. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 03/12] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index a9d2501500a1..d41df8034c5b 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -273,9 +273,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -286,9 +287,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 7ecbd4d76d09..4aa20ae72608 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index f44a35192313..e888c66b9d9d 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -255,7 +255,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 07/12] media: imx: interweave and odd-chroma-row skip are incompatible
If IDMAC interweaving is enabled in a write channel, the channel must write the odd chroma rows for 4:2:0 formats. Skipping writing the odd chroma rows produces corrupted captured 4:2:0 images when interweave is enabled. Reported-by: Krzysztof Hałasa Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++-- drivers/staging/media/imx/imx-media-csi.c | 8 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 1a03d4c9d7b8..cf76b0432371 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -391,12 +391,17 @@ static int prp_setup_channel(struct prp_priv *priv, image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* +* Skip writing U and V components to odd rows in the output +* channels for planar 4:2:0 (but not when enabling IDMAC +* interweaving, they are incompatible). +*/ + if (!interweave && (channel == priv->out_ch || + channel == priv->rot_out_ch)) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index d5b0f8a66750..7e648fc9626a 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,8 +457,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* +* Skip writing U and V components to odd rows (but not +* when enabling IDMAC interweaving, they are incompatible). +*/ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 05/12] media: imx-csi: Input connections to CSI should be optional
Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected endpoint as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 176978c7dfe7..8f52428d2c75 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1813,7 +1813,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 11/12] media: imx: Allow interweave with top/bottom lines swapped
Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v4: - Removed interweave_offset and replace with boolean interweave_swap, suggested by Philipp Zabel. --- drivers/staging/media/imx/imx-ic-prpencvf.c | 25 + drivers/staging/media/imx/imx-media-csi.c | 40 ++--- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..33ada6612fee 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,7 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (priv->interweave_swap && ch == priv->out_ch) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv, * the IDMAC output channel. */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && - V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && - channel == priv->out_ch; + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); @@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + if (priv->interweave_swap && channel == priv->out_ch) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + image.phys0 = addr0; image.phys1 = addr1; @@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv, * channels for planar 4:2:0 (but not when enabling IDMAC * interweaving, they are incompatible). */ - if (!interweave && (channel == priv->out_ch || - channel == priv->rot_out_ch)) { + if ((channel == priv->out_ch && !interweave) || + channel == priv->rot_out_ch) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (interweave) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + if (interweave && channel == priv->out_ch) + ipu_cpmem_interlaced_scan(channel, + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 0d494a7db211..73c9f3ae4221 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,7 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv)
[PATCH v5 08/12] media: imx-csi: Allow skipping odd chroma rows for YVU420
Skip writing U/V components to odd rows for YVU420 in addition to YUV420 and NV12. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 7e648fc9626a..c9110dd39a49 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -452,6 +452,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 09/12] media: imx: vdic: rely on VDIC for correct field order
prepare_vdi_in_buffers() was setting up the dma pointers as if the VDIC is always programmed to receive the fields in bottom-top order, i.e. as if ipu_vdi_set_field_order() only programs BT order in the VDIC. But that's not true, ipu_vdi_set_field_order() is working correctly. So fix prepare_vdi_in_buffers() to give the VDIC the fields in whatever order they were received by the video source, and rely on the VDIC to sort out which is top and which is bottom. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-vdic.c | 12 ++-- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..4a890714193e 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, switch (priv->fieldtype) { case V4L2_FIELD_SEQ_TB: - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; case V4L2_FIELD_SEQ_BT: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; break; + case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; break; - default: - /* assume V4L2_FIELD_INTERLACED_TB */ - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; } ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 06/12] media: imx-csi: Double crop height for alternate fields at sink
If the incoming sink field type is alternate, the reset crop height and crop height bounds must be set to twice the incoming height, because in alternate field mode, upstream will report only the lines for a single field, and the CSI captures the whole frame. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 20 +++- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 8f52428d2c75..d5b0f8a66750 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1140,6 +1140,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1147,6 +1149,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left &= ~0x3; crop->width &= ~0x7; + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; + /* * FIXME: not sure why yet, but on interlaced bt.656, * changing the vertical cropping causes loss of vertical @@ -1156,12 +1162,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1401,6 +1407,8 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; @@ -1528,6 +1536,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 10/12] media: imx-csi: Move crop/compose reset after filling default mbus fields
If caller passes un-initialized field type V4L2_FIELD_ANY to CSI sink pad, the reset CSI crop window would not be correct, because the crop window depends on a valid input field type. To fix move the reset of crop and compose windows to after the call to imx_media_fill_default_mbus_fields(). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 27 --- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c9110dd39a49..0d494a7db211 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1407,19 +1407,6 @@ static void csi_try_fmt(struct csi_priv *priv, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - /* Reset crop and compose rectangles */ - crop->left = 0; - crop->top = 0; - crop->width = sdformat->format.width; - crop->height = sdformat->format.height; - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); - compose->left = 0; - compose->top = 0; - compose->width = crop->width; - compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); if (!*cc) { @@ -1435,6 +1422,20 @@ static void csi_try_fmt(struct csi_priv *priv, imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v5 04/12] media: imx: Fix field negotiation
IDMAC interlaced scan, a.k.a. interweave, should be enabled in the IDMAC output channels only if the IDMAC output pad field type is 'seq-bt' or 'seq-tb', and field type at the capture interface is 'interlaced*'. V4L2_FIELD_HAS_BOTH() macro should not be used on the input to determine enabling interlaced/interweave scan. That macro includes the 'interlaced' field types, and in those cases the data is already interweaved with top/bottom field lines. The CSI will capture whole frames when the source specifies alternate field mode. So the CSI also enables interweave for alternate input field type and the field type at capture interface is interlaced. Fix the logic for setting field type in try_fmt in CSI entity. The behavior should be: - No restrictions on field type at sink pad. - At the output pads, allow sequential fields in TB order, if the sink pad field type is sequential or alternate. Otherwise passthrough the field type from sink to source pad. Move this logic to new function csi_try_field(). These changes result in the following allowed field transformations from CSI sink -> source pads (all other field types at sink are passed through to source): seq-tb -> seq-tb seq-bt -> seq-tb alternate -> seq-tb In a future patch, the CSI sink -> source will allow: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt This will require supporting interweave with top/bottom line swapping. Until then seq-bt is not allowed at the CSI source pad because there is no way to swap top/bottom lines when interweaving to INTERLACED_BT -- note that despite the name, INTERLACED_BT is top-bottom order in memory. The BT in this case refers to field dominance: the bottom lines are older in time than the top lines. The capture interface device allows selecting IDMAC interweave by choosing INTERLACED_TB if the CSI/PRPENCVF source pad is seq-tb and INTERLACED_BT if the source pad is seq-bt (for future support of seq-bt). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 21 -- drivers/staging/media/imx/imx-media-capture.c | 14 drivers/staging/media/imx/imx-media-csi.c | 64 ++- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index af7224846bd5..1a03d4c9d7b8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -354,12 +354,13 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; outcc = vdev->cc; ipu_cpmem_zero(channel); @@ -369,6 +370,15 @@ static int prp_setup_channel(struct prp_priv *priv, image.rect.width = image.pix.width; image.rect.height = image.pix.height; + /* +* If the field type at capture interface is interlaced, and +* the output IDMAC pad is sequential, enable interweave at +* the IDMAC output channel. +*/ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && + channel == priv->out_ch; + if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); swap(image.rect.width, image.rect.height); @@ -409,9 +419,7 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field) && - channel == priv->out_ch) + if (interweave) ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, image.pix.pixelformat); @@ -839,8 +847,7 @@ static void prp_try_fmt(struct prp_priv *priv, infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { - if (sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..01ec9443de55 100644 --- a/d
[PATCH v5 02/12] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp Zabel. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 119 +++--- drivers/staging/media/imx/imx-media-csi.c | 17 +--- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..4a15e513fa05 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); if (ret < 0) return ret; /* set default sensor frame width and height */ - width = mbus_fmt->width; - height = mbus_fmt->height; +
Re: [PATCH v5 02/12] gpu: ipu-csi: Swap fields according to input/output field types
Hi Philipp, can you review this patch and give it your ack? Thanks, Steve On 10/16/18 5:00 PM, Steve Longerbeam wrote: The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp Zabel. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 119 +++--- drivers/staging/media/imx/imx-media-csi.c | 17 +--- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..4a15e513fa05 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsigned long flags; u32 width, height, data = 0; + v4l2_std_id std; int ret; - ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt); + ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); if (ret < 0) return re
[PATCH] media: imx: queue subdev events to reachable video devices
From: Steve Longerbeam Forward events from a sub-device to its list of reachable video devices. Note this will queue the event to a video device even if there is no actual _enabled_ media path from the sub-device to the video device. So a future improvement is to skip the video device if there is no enabled path to it from the sub-device. The entity->pipe pointer can't be used for this check because in imx-media a sub-device can be a member to more than one streaming pipeline at a time. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-capture.c | 18 ++ drivers/staging/media/imx/imx-media-dev.c | 24 +++ 2 files changed, 42 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..4dfbe05d203e 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -335,6 +335,21 @@ static int capture_s_parm(struct file *file, void *fh, return 0; } +static int capture_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_querycap= vidioc_querycap, @@ -362,6 +377,9 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon= vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = capture_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 4b344a4a3706..25e916562c66 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -442,6 +442,29 @@ static const struct media_device_ops imx_media_md_ops = { .link_notify = imx_media_link_notify, }; +static void imx_media_notify(struct v4l2_subdev *sd, +unsigned int notification, +void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); + } +} + static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -462,6 +485,7 @@ static int imx_media_probe(struct platform_device *pdev) mutex_init(&imxmd->mutex); imxmd->v4l2_dev.mdev = &imxmd->md; + imxmd->v4l2_dev.notify = imx_media_notify; strscpy(imxmd->v4l2_dev.name, "imx-media", sizeof(imxmd->v4l2_dev.name)); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v5 02/12] gpu: ipu-csi: Swap fields according to input/output field types
On 12/13/18 4:59 AM, Philipp Zabel wrote: Hi Steve, On Tue, 2018-10-16 at 17:00 -0700, Steve Longerbeam wrote: The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam --- Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp Zabel. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 119 +++--- drivers/staging/media/imx/imx-media-csi.c | 17 +--- include/video/imx-ipu-v3.h| 3 +- 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..4a15e513fa05 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,6 +325,15 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ @@ -374,22 +383,75 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt, infmt and outfmt parameters could be const. Agreed, I will convert these pointer args to const. And since we are changing the API to ipu_csi_init_interface() anyway, I went ahead and converted the mbus_cfg, infmt, and outfmt pointer args to const there as well. + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, struct v4l2_mbus_config *mbus_cfg, - struct v4l2_mbus_framefmt *mbus_fmt) + struct v4l2_mbus_framefmt *infmt, + struct v4l2_mbus_framefmt *outfmt) { struct ipu_csi_bus_config cfg; unsi
[PATCH v6] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, -
Re: [PATCH] media: imx: queue subdev events to reachable video devices
Hi Hans, On 1/8/19 5:26 AM, Hans Verkuil wrote: On 12/09/18 20:57, Steve Longerbeam wrote: From: Steve Longerbeam Forward events from a sub-device to its list of reachable video devices. Note this will queue the event to a video device even if there is no actual _enabled_ media path from the sub-device to the video device. So a future improvement is to skip the video device if there is no enabled path to it from the sub-device. The entity->pipe pointer can't be used for this check because in imx-media a sub-device can be a member to more than one streaming pipeline at a time. You explain what you are doing, but I am missing an explanation of *why* this is needed. Ok, I'll add more explanation. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-capture.c | 18 ++ drivers/staging/media/imx/imx-media-dev.c | 24 +++ 2 files changed, 42 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..4dfbe05d203e 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -335,6 +335,21 @@ static int capture_s_parm(struct file *file, void *fh, return 0; } +static int capture_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_querycap= vidioc_querycap, @@ -362,6 +377,9 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon= vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = capture_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; This part of the patch adds event support to the capture device, can't this be split up into a separate patch? It seems to be useful in its own right. Ok, I will split this up. The first patch will add the (un)subscribe_event callbacks to the capture device, and the second patch will forward subdev events. /* diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 4b344a4a3706..25e916562c66 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -442,6 +442,29 @@ static const struct media_device_ops imx_media_md_ops = { .link_notify = imx_media_link_notify, }; +static void imx_media_notify(struct v4l2_subdev *sd, +unsigned int notification, +void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); Which events do you want to forward? Shouldn't any/all subdev events be forwarded? I would also prefer to allow userland to subscribe to any events that a subdevice might generate. In other words, imx-media will allow the user to subscribe to, and receive any events that might be generated by subdevices. E.g. forwarding control events doesn't seem right, but other events may be useful. The imx capture devices inherit controls from the subdevices. If an inherited control is changed via the capture device, will _two_ control events be generated (one from the subdevice and one from the capture device)? Or will only one event get generated to the capture device? Same question goes when changing an inherited control from the subdevice nodes. Are those events also appearing on the v4l-subdevX device? And if so, should they? How do we know what subdevices a future imx-based system might attach to the media graph? That's why I think it makes sense to forward any/all events from subdevices. Steve + } +} + static int imx_media_probe(struct platform_device *pdev) { struct device *dev =
[PATCH v6 08/12] media: imx-csi: Allow skipping odd chroma rows for YVU420
Skip writing U/V components to odd rows for YVU420 in addition to YUV420 and NV12. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index dbc5a92ec073..47568ec43f4a 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -452,6 +452,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v6 02/12] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, -
[PATCH v6 04/12] media: imx: Fix field negotiation
IDMAC interlaced scan, a.k.a. interweave, should be enabled in the IDMAC output channels only if the IDMAC output pad field type is 'seq-bt' or 'seq-tb', and field type at the capture interface is 'interlaced*'. V4L2_FIELD_HAS_BOTH() macro should not be used on the input to determine enabling interlaced/interweave scan. That macro includes the 'interlaced' field types, and in those cases the data is already interweaved with top/bottom field lines. The CSI will capture whole frames when the source specifies alternate field mode. So the CSI also enables interweave for alternate input field type and the field type at capture interface is interlaced. Fix the logic for setting field type in try_fmt in CSI entity. The behavior should be: - No restrictions on field type at sink pad. - At the output pads, allow sequential fields in TB order, if the sink pad field type is sequential or alternate. Otherwise passthrough the field type from sink to source pad. Move this logic to new function csi_try_field(). These changes result in the following allowed field transformations from CSI sink -> source pads (all other field types at sink are passed through to source): seq-tb -> seq-tb seq-bt -> seq-tb alternate -> seq-tb In a future patch, the CSI sink -> source will allow: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt This will require supporting interweave with top/bottom line swapping. Until then seq-bt is not allowed at the CSI source pad because there is no way to swap top/bottom lines when interweaving to INTERLACED_BT -- note that despite the name, INTERLACED_BT is top-bottom order in memory. The BT in this case refers to field dominance: the bottom lines are older in time than the top lines. The capture interface device allows selecting IDMAC interweave by choosing INTERLACED_TB if the CSI/PRPENCVF source pad is seq-tb and INTERLACED_BT if the source pad is seq-bt (for future support of seq-bt). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 21 -- drivers/staging/media/imx/imx-media-capture.c | 14 drivers/staging/media/imx/imx-media-csi.c | 64 ++- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index af7224846bd5..1a03d4c9d7b8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -354,12 +354,13 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; outcc = vdev->cc; ipu_cpmem_zero(channel); @@ -369,6 +370,15 @@ static int prp_setup_channel(struct prp_priv *priv, image.rect.width = image.pix.width; image.rect.height = image.pix.height; + /* +* If the field type at capture interface is interlaced, and +* the output IDMAC pad is sequential, enable interweave at +* the IDMAC output channel. +*/ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && + channel == priv->out_ch; + if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); swap(image.rect.width, image.rect.height); @@ -409,9 +419,7 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field) && - channel == priv->out_ch) + if (interweave) ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, image.pix.pixelformat); @@ -839,8 +847,7 @@ static void prp_try_fmt(struct prp_priv *priv, infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { - if (sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..01ec9443de55 100644 --- a/d
[PATCH v6 10/12] media: imx-csi: Move crop/compose reset after filling default mbus fields
If caller passes un-initialized field type V4L2_FIELD_ANY to CSI sink pad, the reset CSI crop window would not be correct, because the crop window depends on a valid input field type. To fix move the reset of crop and compose windows to after the call to imx_media_fill_default_mbus_fields(). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 27 --- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 47568ec43f4a..7afb7e367d76 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1409,19 +1409,6 @@ static void csi_try_fmt(struct csi_priv *priv, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - /* Reset crop and compose rectangles */ - crop->left = 0; - crop->top = 0; - crop->width = sdformat->format.width; - crop->height = sdformat->format.height; - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); - compose->left = 0; - compose->top = 0; - compose->width = crop->width; - compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); if (!*cc) { @@ -1437,6 +1424,20 @@ static void csi_try_fmt(struct csi_priv *priv, imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v6 07/12] media: imx: interweave and odd-chroma-row skip are incompatible
If IDMAC interweaving is enabled in a write channel, the channel must write the odd chroma rows for 4:2:0 formats. Skipping writing the odd chroma rows produces corrupted captured 4:2:0 images when interweave is enabled. Reported-by: Krzysztof Hałasa Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++-- drivers/staging/media/imx/imx-media-csi.c | 8 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 1a03d4c9d7b8..cf76b0432371 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -391,12 +391,17 @@ static int prp_setup_channel(struct prp_priv *priv, image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* +* Skip writing U and V components to odd rows in the output +* channels for planar 4:2:0 (but not when enabling IDMAC +* interweaving, they are incompatible). +*/ + if (!interweave && (channel == priv->out_ch || + channel == priv->rot_out_ch)) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c40c3262038e..dbc5a92ec073 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,8 +457,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* +* Skip writing U and V components to odd rows (but not +* when enabling IDMAC interweaving, they are incompatible). +*/ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v6 09/12] media: imx: vdic: rely on VDIC for correct field order
prepare_vdi_in_buffers() was setting up the dma pointers as if the VDIC is always programmed to receive the fields in bottom-top order, i.e. as if ipu_vdi_set_field_order() only programs BT order in the VDIC. But that's not true, ipu_vdi_set_field_order() is working correctly. So fix prepare_vdi_in_buffers() to give the VDIC the fields in whatever order they were received by the video source, and rely on the VDIC to sort out which is top and which is bottom. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-vdic.c | 12 ++-- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..4a890714193e 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, switch (priv->fieldtype) { case V4L2_FIELD_SEQ_TB: - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; case V4L2_FIELD_SEQ_BT: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; break; + case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; break; - default: - /* assume V4L2_FIELD_INTERLACED_TB */ - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; } ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v6 11/12] media: imx: Allow interweave with top/bottom lines swapped
Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v4: - Removed interweave_offset and replace with boolean interweave_swap, suggested by Philipp Zabel. --- drivers/staging/media/imx/imx-ic-prpencvf.c | 25 + drivers/staging/media/imx/imx-media-csi.c | 40 ++--- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..33ada6612fee 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,7 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (priv->interweave_swap && ch == priv->out_ch) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv, * the IDMAC output channel. */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && - V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && - channel == priv->out_ch; + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); @@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + if (priv->interweave_swap && channel == priv->out_ch) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + image.phys0 = addr0; image.phys1 = addr1; @@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv, * channels for planar 4:2:0 (but not when enabling IDMAC * interweaving, they are incompatible). */ - if (!interweave && (channel == priv->out_ch || - channel == priv->rot_out_ch)) { + if ((channel == priv->out_ch && !interweave) || + channel == priv->rot_out_ch) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (interweave) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + if (interweave && channel == priv->out_ch) + ipu_cpmem_interlaced_scan(channel, + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 7afb7e367d76..e18f58f56dfb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,7 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv)
[PATCH v6 05/12] media: imx-csi: Input connections to CSI should be optional
Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected endpoint as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e3a4f39dbf73..b276672cae1d 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1815,7 +1815,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v6 03/12] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v6 06/12] media: imx-csi: Double crop height for alternate fields at sink
If the incoming sink field type is alternate, the reset crop height and crop height bounds must be set to twice the incoming height, because in alternate field mode, upstream will report only the lines for a single field, and the CSI captures the whole frame. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 20 +++- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index b276672cae1d..c40c3262038e 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1142,6 +1142,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1149,6 +1151,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left &= ~0x3; crop->width &= ~0x7; + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; + /* * FIXME: not sure why yet, but on interlaced bt.656, * changing the vertical cropping causes loss of vertical @@ -1158,12 +1164,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1403,6 +1409,8 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; @@ -1530,6 +1538,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v6] gpu: ipu-csi: Swap fields according to input/output field types
Please disregard. This patch can't be submitted stand-alone, I will re-submit as part of a v6 of "imx-media: Fixes for interlaced capture" patchset. Steve On 12/14/18 3:46 PM, Steve Longerbeam wrote: The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI
Re: [PATCH v6 05/12] media: imx-csi: Input connections to CSI should be optional
On 1/9/19 3:13 AM, Philipp Zabel wrote: On Tue, 2019-01-08 at 16:15 -0800, Steve Longerbeam wrote: Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected endpoint as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e3a4f39dbf73..b276672cae1d 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1815,7 +1815,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) Is this something that should be applied as a fix, separately from this series? Yes. I'll submit it separately and send a v7 of this series without it. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v7 03/11] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v7 02/11] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_config *mbus_cfg, -
[PATCH v7 05/11] media: imx-csi: Double crop height for alternate fields at sink
If the incoming sink field type is alternate, the reset crop height and crop height bounds must be set to twice the incoming height, because in alternate field mode, upstream will report only the lines for a single field, and the CSI captures the whole frame. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 20 +++- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e3a4f39dbf73..10945cbdbd71 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1142,6 +1142,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1149,6 +1151,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left &= ~0x3; crop->width &= ~0x7; + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; + /* * FIXME: not sure why yet, but on interlaced bt.656, * changing the vertical cropping causes loss of vertical @@ -1158,12 +1164,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1403,6 +1409,8 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; @@ -1530,6 +1538,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v7 10/11] media: imx: Allow interweave with top/bottom lines swapped
Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v4: - Removed interweave_offset and replace with boolean interweave_swap, suggested by Philipp Zabel. --- drivers/staging/media/imx/imx-ic-prpencvf.c | 25 + drivers/staging/media/imx/imx-media-csi.c | 40 ++--- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..33ada6612fee 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,7 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (priv->interweave_swap && ch == priv->out_ch) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv, * the IDMAC output channel. */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && - V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && - channel == priv->out_ch; + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); @@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + if (priv->interweave_swap && channel == priv->out_ch) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + image.phys0 = addr0; image.phys1 = addr1; @@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv, * channels for planar 4:2:0 (but not when enabling IDMAC * interweaving, they are incompatible). */ - if (!interweave && (channel == priv->out_ch || - channel == priv->rot_out_ch)) { + if ((channel == priv->out_ch && !interweave) || + channel == priv->rot_out_ch) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (interweave) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + if (interweave && channel == priv->out_ch) + ipu_cpmem_interlaced_scan(channel, + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 8537ecb7dd17..555aa45e02e3 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,7 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv)
[PATCH v7 08/11] media: imx: vdic: rely on VDIC for correct field order
prepare_vdi_in_buffers() was setting up the dma pointers as if the VDIC is always programmed to receive the fields in bottom-top order, i.e. as if ipu_vdi_set_field_order() only programs BT order in the VDIC. But that's not true, ipu_vdi_set_field_order() is working correctly. So fix prepare_vdi_in_buffers() to give the VDIC the fields in whatever order they were received by the video source, and rely on the VDIC to sort out which is top and which is bottom. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-vdic.c | 12 ++-- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..4a890714193e 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, switch (priv->fieldtype) { case V4L2_FIELD_SEQ_TB: - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; case V4L2_FIELD_SEQ_BT: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; break; + case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; break; - default: - /* assume V4L2_FIELD_INTERLACED_TB */ - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; } ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v7 07/11] media: imx-csi: Allow skipping odd chroma rows for YVU420
Skip writing U/V components to odd rows for YVU420 in addition to YUV420 and NV12. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 604d0bd24389..6f1e11b8a7cb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -452,6 +452,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v7 06/11] media: imx: interweave and odd-chroma-row skip are incompatible
If IDMAC interweaving is enabled in a write channel, the channel must write the odd chroma rows for 4:2:0 formats. Skipping writing the odd chroma rows produces corrupted captured 4:2:0 images when interweave is enabled. Reported-by: Krzysztof Hałasa Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++-- drivers/staging/media/imx/imx-media-csi.c | 8 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 1a03d4c9d7b8..cf76b0432371 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -391,12 +391,17 @@ static int prp_setup_channel(struct prp_priv *priv, image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* +* Skip writing U and V components to odd rows in the output +* channels for planar 4:2:0 (but not when enabling IDMAC +* interweaving, they are incompatible). +*/ + if (!interweave && (channel == priv->out_ch || + channel == priv->rot_out_ch)) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 10945cbdbd71..604d0bd24389 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,8 +457,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* +* Skip writing U and V components to odd rows (but not +* when enabling IDMAC interweaving, they are incompatible). +*/ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v7 04/11] media: imx: Fix field negotiation
IDMAC interlaced scan, a.k.a. interweave, should be enabled in the IDMAC output channels only if the IDMAC output pad field type is 'seq-bt' or 'seq-tb', and field type at the capture interface is 'interlaced*'. V4L2_FIELD_HAS_BOTH() macro should not be used on the input to determine enabling interlaced/interweave scan. That macro includes the 'interlaced' field types, and in those cases the data is already interweaved with top/bottom field lines. The CSI will capture whole frames when the source specifies alternate field mode. So the CSI also enables interweave for alternate input field type and the field type at capture interface is interlaced. Fix the logic for setting field type in try_fmt in CSI entity. The behavior should be: - No restrictions on field type at sink pad. - At the output pads, allow sequential fields in TB order, if the sink pad field type is sequential or alternate. Otherwise passthrough the field type from sink to source pad. Move this logic to new function csi_try_field(). These changes result in the following allowed field transformations from CSI sink -> source pads (all other field types at sink are passed through to source): seq-tb -> seq-tb seq-bt -> seq-tb alternate -> seq-tb In a future patch, the CSI sink -> source will allow: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt This will require supporting interweave with top/bottom line swapping. Until then seq-bt is not allowed at the CSI source pad because there is no way to swap top/bottom lines when interweaving to INTERLACED_BT -- note that despite the name, INTERLACED_BT is top-bottom order in memory. The BT in this case refers to field dominance: the bottom lines are older in time than the top lines. The capture interface device allows selecting IDMAC interweave by choosing INTERLACED_TB if the CSI/PRPENCVF source pad is seq-tb and INTERLACED_BT if the source pad is seq-bt (for future support of seq-bt). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 21 -- drivers/staging/media/imx/imx-media-capture.c | 14 drivers/staging/media/imx/imx-media-csi.c | 64 ++- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index af7224846bd5..1a03d4c9d7b8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -354,12 +354,13 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; outcc = vdev->cc; ipu_cpmem_zero(channel); @@ -369,6 +370,15 @@ static int prp_setup_channel(struct prp_priv *priv, image.rect.width = image.pix.width; image.rect.height = image.pix.height; + /* +* If the field type at capture interface is interlaced, and +* the output IDMAC pad is sequential, enable interweave at +* the IDMAC output channel. +*/ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && + channel == priv->out_ch; + if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); swap(image.rect.width, image.rect.height); @@ -409,9 +419,7 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field) && - channel == priv->out_ch) + if (interweave) ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, image.pix.pixelformat); @@ -839,8 +847,7 @@ static void prp_try_fmt(struct prp_priv *priv, infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { - if (sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..01ec9443de55 100644 --- a/d
[PATCH v7 09/11] media: imx-csi: Move crop/compose reset after filling default mbus fields
If caller passes un-initialized field type V4L2_FIELD_ANY to CSI sink pad, the reset CSI crop window would not be correct, because the crop window depends on a valid input field type. To fix move the reset of crop and compose windows to after the call to imx_media_fill_default_mbus_fields(). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 27 --- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 6f1e11b8a7cb..8537ecb7dd17 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1409,19 +1409,6 @@ static void csi_try_fmt(struct csi_priv *priv, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - /* Reset crop and compose rectangles */ - crop->left = 0; - crop->top = 0; - crop->width = sdformat->format.width; - crop->height = sdformat->format.height; - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); - compose->left = 0; - compose->top = 0; - compose->width = crop->width; - compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); if (!*cc) { @@ -1437,6 +1424,20 @@ static void csi_try_fmt(struct csi_priv *priv, imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v8 04/11] media: imx: Fix field negotiation
IDMAC interlaced scan, a.k.a. interweave, should be enabled in the IDMAC output channels only if the IDMAC output pad field type is 'seq-bt' or 'seq-tb', and field type at the capture interface is 'interlaced*'. V4L2_FIELD_HAS_BOTH() macro should not be used on the input to determine enabling interlaced/interweave scan. That macro includes the 'interlaced' field types, and in those cases the data is already interweaved with top/bottom field lines. The CSI will capture whole frames when the source specifies alternate field mode. So the CSI also enables interweave for alternate input field type and the field type at capture interface is interlaced. Fix the logic for setting field type in try_fmt in CSI entity. The behavior should be: - No restrictions on field type at sink pad. - At the output pads, allow sequential fields in TB order, if the sink pad field type is sequential or alternate. Otherwise passthrough the field type from sink to source pad. Move this logic to new function csi_try_field(). These changes result in the following allowed field transformations from CSI sink -> source pads (all other field types at sink are passed through to source): seq-tb -> seq-tb seq-bt -> seq-tb alternate -> seq-tb In a future patch, the CSI sink -> source will allow: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt This will require supporting interweave with top/bottom line swapping. Until then seq-bt is not allowed at the CSI source pad because there is no way to swap top/bottom lines when interweaving to INTERLACED_BT -- note that despite the name, INTERLACED_BT is top-bottom order in memory. The BT in this case refers to field dominance: the bottom lines are older in time than the top lines. The capture interface device allows selecting IDMAC interweave by choosing INTERLACED_TB if the CSI/PRPENCVF source pad is seq-tb and INTERLACED_BT if the source pad is seq-bt (for future support of seq-bt). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 21 -- drivers/staging/media/imx/imx-media-capture.c | 14 drivers/staging/media/imx/imx-media-csi.c | 64 ++- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index af7224846bd5..1a03d4c9d7b8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -354,12 +354,13 @@ static int prp_setup_channel(struct prp_priv *priv, { struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *outcc; - struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; unsigned int burst_size; struct ipu_image image; + bool interweave; int ret; - infmt = &priv->format_mbus[PRPENCVF_SINK_PAD]; + outfmt = &priv->format_mbus[PRPENCVF_SRC_PAD]; outcc = vdev->cc; ipu_cpmem_zero(channel); @@ -369,6 +370,15 @@ static int prp_setup_channel(struct prp_priv *priv, image.rect.width = image.pix.width; image.rect.height = image.pix.height; + /* +* If the field type at capture interface is interlaced, and +* the output IDMAC pad is sequential, enable interweave at +* the IDMAC output channel. +*/ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && + channel == priv->out_ch; + if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); swap(image.rect.width, image.rect.height); @@ -409,9 +419,7 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field) && - channel == priv->out_ch) + if (interweave) ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, image.pix.pixelformat); @@ -839,8 +847,7 @@ static void prp_try_fmt(struct prp_priv *priv, infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which); if (sdformat->pad == PRPENCVF_SRC_PAD) { - if (sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; + sdformat->format.field = infmt->field; prp_bound_align_output(&sdformat->format, infmt, priv->rot_mode); diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..01ec9443de55 100644 --- a/d
[PATCH v8 03/11] gpu: ipu-v3: Add planar support to interlaced scan
To support interlaced scan with planar formats, cpmem SLUV must be programmed with the correct chroma line stride. For full and partial planar 4:2:2 (YUV422P, NV16), chroma line stride must be doubled. For full and partial planar 4:2:0 (YUV420, YVU420, NV12), chroma line stride must _not_ be doubled, since a single chroma line is shared by two luma lines. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- drivers/gpu/ipu-v3/ipu-cpmem.c | 26 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- include/video/imx-ipu-v3.h | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 163fadb8a33a..d047a6867c59 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -277,9 +277,10 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat) { - u32 ilo, sly; + u32 ilo, sly, sluv; if (stride < 0) { stride = -stride; @@ -290,9 +291,30 @@ void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) sly = (stride * 2) - 1; + switch (pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + sluv = stride / 2 - 1; + break; + case V4L2_PIX_FMT_NV12: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_YUV422P: + sluv = stride - 1; + break; + case V4L2_PIX_FMT_NV16: + sluv = stride * 2 - 1; + break; + default: + sluv = 0; + break; + } + ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); + if (sluv) + ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); }; EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 28f41caba05d..af7224846bd5 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -412,7 +412,8 @@ static int prp_setup_channel(struct prp_priv *priv, if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field) && channel == priv->out_ch) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline); + ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, image.pix.width, image.pix.height, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c2a8d9cd31b7..da4808348845 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -512,7 +512,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) if (image.pix.field == V4L2_FIELD_NONE && V4L2_FIELD_HAS_BOTH(infmt->field)) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index bbc8481f567d..c887f4bee5f8 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -258,7 +258,8 @@ void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride); void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch); void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf); void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off); -void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride); +void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, + u32 pixelformat); void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id); int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch); void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v8 06/11] media: imx: interweave and odd-chroma-row skip are incompatible
If IDMAC interweaving is enabled in a write channel, the channel must write the odd chroma rows for 4:2:0 formats. Skipping writing the odd chroma rows produces corrupted captured 4:2:0 images when interweave is enabled. Reported-by: Krzysztof Hałasa Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++-- drivers/staging/media/imx/imx-media-csi.c | 8 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 1a03d4c9d7b8..cf76b0432371 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -391,12 +391,17 @@ static int prp_setup_channel(struct prp_priv *priv, image.phys0 = addr0; image.phys1 = addr1; - if (channel == priv->out_ch || channel == priv->rot_out_ch) { + /* +* Skip writing U and V components to odd rows in the output +* channels for planar 4:2:0 (but not when enabling IDMAC +* interweaving, they are incompatible). +*/ + if (!interweave && (channel == priv->out_ch || + channel == priv->rot_out_ch)) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: - /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(channel); break; } diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 10945cbdbd71..604d0bd24389 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,8 +457,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* +* Skip writing U and V components to odd rows (but not +* when enabling IDMAC interweaving, they are incompatible). +*/ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v8 05/11] media: imx-csi: Double crop height for alternate fields at sink
If the incoming sink field type is alternate, the reset crop height and crop height bounds must be set to twice the incoming height, because in alternate field mode, upstream will report only the lines for a single field, and the CSI captures the whole frame. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 20 +++- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e3a4f39dbf73..10945cbdbd71 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1142,6 +1142,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1149,6 +1151,10 @@ static void csi_try_crop(struct csi_priv *priv, crop->left &= ~0x3; crop->width &= ~0x7; + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; + /* * FIXME: not sure why yet, but on interlaced bt.656, * changing the vertical cropping causes loss of vertical @@ -1158,12 +1164,12 @@ static void csi_try_crop(struct csi_priv *priv, if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1403,6 +1409,8 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; @@ -1530,6 +1538,8 @@ static int csi_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v8 08/11] media: imx: vdic: rely on VDIC for correct field order
prepare_vdi_in_buffers() was setting up the dma pointers as if the VDIC is always programmed to receive the fields in bottom-top order, i.e. as if ipu_vdi_set_field_order() only programs BT order in the VDIC. But that's not true, ipu_vdi_set_field_order() is working correctly. So fix prepare_vdi_in_buffers() to give the VDIC the fields in whatever order they were received by the video source, and rely on the VDIC to sort out which is top and which is bottom. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-vdic.c | 12 ++-- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 482250d47e7c..4a890714193e 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -219,26 +219,18 @@ static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv, switch (priv->fieldtype) { case V4L2_FIELD_SEQ_TB: - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; case V4L2_FIELD_SEQ_BT: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs; break; + case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is; curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; break; - default: - /* assume V4L2_FIELD_INTERLACED_TB */ - prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0); - curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is; - next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0); - break; } ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v8 09/11] media: imx-csi: Move crop/compose reset after filling default mbus fields
If caller passes un-initialized field type V4L2_FIELD_ANY to CSI sink pad, the reset CSI crop window would not be correct, because the crop window depends on a valid input field type. To fix move the reset of crop and compose windows to after the call to imx_media_fill_default_mbus_fields(). Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 27 --- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 6f1e11b8a7cb..8537ecb7dd17 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1409,19 +1409,6 @@ static void csi_try_fmt(struct csi_priv *priv, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - /* Reset crop and compose rectangles */ - crop->left = 0; - crop->top = 0; - crop->width = sdformat->format.width; - crop->height = sdformat->format.height; - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; - csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); - compose->left = 0; - compose->top = 0; - compose->width = crop->width; - compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, CS_SEL_ANY, true); if (!*cc) { @@ -1437,6 +1424,20 @@ static void csi_try_fmt(struct csi_priv *priv, imx_media_fill_default_mbus_fields( &sdformat->format, infmt, priv->active_output_pad == CSI_SRC_PAD_DIRECT); + + /* Reset crop and compose rectangles */ + crop->left = 0; + crop->top = 0; + crop->width = sdformat->format.width; + crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); + compose->left = 0; + compose->top = 0; + compose->width = crop->width; + compose->height = crop->height; + break; } } -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v8 02/11] gpu: ipu-csi: Swap fields according to input/output field types
The function ipu_csi_init_interface() was inverting the F-bit for NTSC case, in the CCIR_CODE_1/2 registers. The result being that for NTSC bottom-top field order, the CSI would swap fields and capture in top-bottom order. Instead, base field swap on the field order of the input to the CSI, and the field order of the requested output. If the input/output fields are sequential but different, swap fields, otherwise do not swap. This requires passing both the input and output mbus frame formats to ipu_csi_init_interface(). Move this code to a new private function ipu_csi_set_bt_interlaced_codes() that programs the CCIR_CODE_1/2 registers for interlaced BT.656 (and possibly interlaced BT.1120 in the future). When detecting input video standard from the input frame width/height, make sure to double height if input field type is alternate, since in that case input height only includes lines for one field. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Philipp Zabel --- Changes since v5: - Convert to const the infmt, outfmt, and mbus_cfg pointer args to ipu_csi_init_interface(), suggested by Philipp Zabel. - Bring back if_fmt local var and don't copy outfmt to local stack in csi_setup(), suggested by Philipp. Changes since v4: - Cleaned up some convoluted code in ipu_csi_init_interface(), suggested by Philipp. - Fixed a regression in csi_setup(), caught by Philipp. --- drivers/gpu/ipu-v3/ipu-csi.c | 126 +++--- drivers/staging/media/imx/imx-media-csi.c | 7 +- include/video/imx-ipu-v3.h| 5 +- 3 files changed, 89 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index aa0e30a2ba18..d1e575571a8d 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -325,12 +325,21 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, return 0; } +/* translate alternate field mode based on given standard */ +static inline enum v4l2_field +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) +{ + return (field != V4L2_FIELD_ALTERNATE) ? field : + ((std & V4L2_STD_525_60) ? +V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); +} + /* * Fill a CSI bus config struct from mbus_config and mbus_framefmt. */ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -struct v4l2_mbus_config *mbus_cfg, -struct v4l2_mbus_framefmt *mbus_fmt) + const struct v4l2_mbus_config *mbus_cfg, + const struct v4l2_mbus_framefmt *mbus_fmt) { int ret; @@ -374,22 +383,76 @@ static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, return 0; } +static int +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, + const struct v4l2_mbus_framefmt *infmt, + const struct v4l2_mbus_framefmt *outfmt, + v4l2_std_id std) +{ + enum v4l2_field infield, outfield; + bool swap_fields; + + /* get translated field type of input and output */ + infield = ipu_csi_translate_field(infmt->field, std); + outfield = ipu_csi_translate_field(outfmt->field, std); + + /* +* Write the H-V-F codes the CSI will match against the +* incoming data for start/end of active and blanking +* field intervals. If input and output field types are +* sequential but not the same (one is SEQ_BT and the other +* is SEQ_TB), swap the F-bit so that the CSI will capture +* field 1 lines before field 0 lines. +*/ + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && + V4L2_FIELD_IS_SEQUENTIAL(outfield) && + infield != outfield); + + if (!swap_fields) { + /* +* Field0BlankEnd = 110, Field0BlankStart = 010 +* Field0ActiveEnd = 100, Field0ActiveStart = 000 +* Field1BlankEnd = 111, Field1BlankStart = 011 +* Field1ActiveEnd = 101, Field1ActiveStart = 001 +*/ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); + } else { + dev_dbg(csi->ipu->dev, "capture field swap\n"); + + /* same as above but with F-bit inverted */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, + CSI_CCIR_CODE_1); + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); + } + + ipu_csi_write(csi, 0xFF, CSI_CCIR_CODE_3); + + return 0; +} + + int ipu_csi_init_interface(struct ipu_csi *csi, - struct v4l2_mbus_conf
[PATCH v8 10/11] media: imx: Allow interweave with top/bottom lines swapped
Allow sequential->interlaced interweaving but with top/bottom lines swapped to the output buffer. This can be accomplished by adding one line length to IDMAC output channel address, with a negative line length for the interlace offset. This is to allow the seq-bt -> interlaced-bt transformation, where bottom lines are still dominant (older in time) but with top lines first in the interweaved output buffer. With this support, the CSI can now allow seq-bt at its source pads, e.g. the following transformations are allowed in CSI from sink to source: seq-tb -> seq-bt seq-bt -> seq-bt alternate -> seq-bt Suggested-by: Philipp Zabel Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- Changes since v4: - Removed interweave_offset and replace with boolean interweave_swap, suggested by Philipp Zabel. --- drivers/staging/media/imx/imx-ic-prpencvf.c | 25 + drivers/staging/media/imx/imx-media-csi.c | 40 ++--- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index cf76b0432371..33ada6612fee 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -106,6 +106,7 @@ struct prp_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); + if (priv->interweave_swap && ch == priv->out_ch) + phys += vdev->fmt.fmt.pix.bytesperline; + ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); } @@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv, * the IDMAC output channel. */ interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && - V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && - channel == priv->out_ch; + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; if (rot_swap_width_height) { swap(image.pix.width, image.pix.height); @@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv, (image.pix.width * outcc->bpp) >> 3; } + if (priv->interweave_swap && channel == priv->out_ch) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + image.phys0 = addr0; image.phys1 = addr1; @@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv, * channels for planar 4:2:0 (but not when enabling IDMAC * interweaving, they are incompatible). */ - if (!interweave && (channel == priv->out_ch || - channel == priv->rot_out_ch)) { + if ((channel == priv->out_ch && !interweave) || + channel == priv->rot_out_ch) { switch (image.pix.pixelformat) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv, if (rot_mode) ipu_cpmem_set_rotation(channel, rot_mode); - if (interweave) - ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, + if (interweave && channel == priv->out_ch) + ipu_cpmem_interlaced_scan(channel, + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, image.pix.pixelformat); ret = ipu_ic_task_idma_init(priv->ic, channel, diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 8537ecb7dd17..555aa45e02e3 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -114,6 +114,7 @@ struct csi_priv { u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof;/* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv)
[PATCH v8 07/11] media: imx-csi: Allow skipping odd chroma rows for YVU420
Skip writing U/V components to odd rows for YVU420 in addition to YUV420 and NV12. Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel --- drivers/staging/media/imx/imx-media-csi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 604d0bd24389..6f1e11b8a7cb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -452,6 +452,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH] media: imx-csi: Input connections to CSI should be optional
Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected CSI input port as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 4223f8d418ae..30b1717982ae 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1787,7 +1787,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v2] media: imx: queue subdev events to reachable video devices
In order to receive events generated by subdevices on the video capture nodes, those events need to be forwarded to the subdevice's list of reachable video capture devices. Note this will queue the event to a video device even if there is no actual _enabled_ media path from the sub-device to the video device. So a future improvement is to skip the video device if there is no enabled path to it from the sub-device. The entity->pipe pointer can't be used for this check because in imx-media a sub-device can be a member to more than one streaming pipeline at a time. Signed-off-by: Steve Longerbeam --- Changes in v2: - split out the implementation of (un)subscribe_event on the video capture device to a separate patch. --- drivers/staging/media/imx/imx-media-dev.c | 24 +++ 1 file changed, 24 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 4b344a4a3706..25e916562c66 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -442,6 +442,29 @@ static const struct media_device_ops imx_media_md_ops = { .link_notify = imx_media_link_notify, }; +static void imx_media_notify(struct v4l2_subdev *sd, +unsigned int notification, +void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); + } +} + static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -462,6 +485,7 @@ static int imx_media_probe(struct platform_device *pdev) mutex_init(&imxmd->mutex); imxmd->v4l2_dev.mdev = &imxmd->md; + imxmd->v4l2_dev.notify = imx_media_notify; strscpy(imxmd->v4l2_dev.name, "imx-media", sizeof(imxmd->v4l2_dev.name)); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH] media: imx: capture: Allow event subscribe/unsubscribe
Implement the vidioc_(un)subscribe_event operations. Imx will allow subscribing to the imx-specific frame interval error events, events from subdevices (V4L2_EVENT_SOURCE_CHANGE), and control events. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-capture.c | 18 ++ 1 file changed, 18 insertions(+) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index b37e1186eb2f..4dfbe05d203e 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -335,6 +335,21 @@ static int capture_s_parm(struct file *file, void *fh, return 0; } +static int capture_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_querycap= vidioc_querycap, @@ -362,6 +377,9 @@ static const struct v4l2_ioctl_ops capture_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon= vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = capture_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 2/2] media: imx: prpencvf: Disable CSI immediately after last EOF
The CSI must be disabled immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). This can be accomplished by moving upstream stream off to just after receiving the last EOF completion in prp_stop(). For symmetry also move upstream stream on to end of prp_start(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d1 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-ic-prpencvf.c | 26 ++--- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 33ada6612fee..f53cdb608528 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -707,12 +707,23 @@ static int prp_start(struct prp_priv *priv) goto out_free_nfb4eof_irq; } + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) { + v4l2_err(&ic_priv->sd, +"upstream stream on failed: %d\n", ret); + goto out_free_eof_irq; + } + /* start the EOF timeout timer */ mod_timer(&priv->eof_timeout_timer, jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); return 0; +out_free_eof_irq: + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); out_free_nfb4eof_irq: devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); out_unsetup: @@ -744,6 +755,12 @@ static void prp_stop(struct prp_priv *priv) if (ret == 0) v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n"); + /* stop upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_warn(&ic_priv->sd, + "upstream stream off failed: %d\n", ret); + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); @@ -1174,15 +1191,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) goto out; - /* start/stop upstream */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) { - if (enable) - prp_stop(priv); - goto out; - } - update_count: priv->stream_count += enable ? 1 : -1; if (priv->stream_count < 0) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 1/2] media: imx: csi: Disable CSI immediately after last EOF
Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e18f58f56dfb..9218372cb997 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -681,6 +681,8 @@ static void csi_idmac_stop(struct csi_priv *priv) if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); + ipu_csi_disable(priv->csi); + devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -793,11 +795,10 @@ static void csi_stop(struct csi_priv *priv) /* stop the frame interval monitor */ if (priv->fim) imx_media_fim_set_stream(priv->fim, NULL, false); + } else { + ipu_csi_disable(priv->csi); } - - ipu_csi_disable(priv->csi); } - static const struct csi_skip_desc csi_skip[12] = { { 1, 1, 0x00 }, /* Keep all frames */ { 5, 6, 0x10 }, /* Skip every sixth frame */ -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH 1/2] media: imx: csi: Disable CSI immediately after last EOF
Hi Fabio, thanks for the review. On 1/17/19 12:20 PM, Fabio Estevam wrote: Hi Steve, On Thu, Jan 17, 2019 at 6:15 PM Steve Longerbeam wrote: Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Thanks. Since this fixes a lockup, maybe it is worth adding a Fixes tag and Cc stable? Right, forgot. I will do that and resubmit. --- drivers/staging/media/imx/imx-media-csi.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e18f58f56dfb..9218372cb997 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -681,6 +681,8 @@ static void csi_idmac_stop(struct csi_priv *priv) if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); + ipu_csi_disable(priv->csi); + devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -793,11 +795,10 @@ static void csi_stop(struct csi_priv *priv) /* stop the frame interval monitor */ if (priv->fim) imx_media_fim_set_stream(priv->fim, NULL, false); + } else { + ipu_csi_disable(priv->csi); } - - ipu_csi_disable(priv->csi); } - Unneeded line removal. Will fix. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v2 1/2] media: imx: csi: Disable CSI immediately after last EOF
Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v2: - restore an empty line - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-media-csi.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e18f58f56dfb..e0f6f88e2e70 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -681,6 +681,8 @@ static void csi_idmac_stop(struct csi_priv *priv) if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); + ipu_csi_disable(priv->csi); + devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -793,9 +795,9 @@ static void csi_stop(struct csi_priv *priv) /* stop the frame interval monitor */ if (priv->fim) imx_media_fim_set_stream(priv->fim, NULL, false); + } else { + ipu_csi_disable(priv->csi); } - - ipu_csi_disable(priv->csi); } static const struct csi_skip_desc csi_skip[12] = { -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v2 2/2] media: imx: prpencvf: Disable CSI immediately after last EOF
The CSI must be disabled immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). This can be accomplished by moving upstream stream off to just after receiving the last EOF completion in prp_stop(). For symmetry also move upstream stream on to end of prp_start(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d1 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Fixes: f0d9c8924e2c3 ("[media] media: imx: Add IC subdev drivers") Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v2: - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-ic-prpencvf.c | 26 ++--- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 33ada6612fee..f53cdb608528 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -707,12 +707,23 @@ static int prp_start(struct prp_priv *priv) goto out_free_nfb4eof_irq; } + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) { + v4l2_err(&ic_priv->sd, +"upstream stream on failed: %d\n", ret); + goto out_free_eof_irq; + } + /* start the EOF timeout timer */ mod_timer(&priv->eof_timeout_timer, jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); return 0; +out_free_eof_irq: + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); out_free_nfb4eof_irq: devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); out_unsetup: @@ -744,6 +755,12 @@ static void prp_stop(struct prp_priv *priv) if (ret == 0) v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n"); + /* stop upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_warn(&ic_priv->sd, + "upstream stream off failed: %d\n", ret); + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); @@ -1174,15 +1191,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) goto out; - /* start/stop upstream */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) { - if (enable) - prp_stop(priv); - goto out; - } - update_count: priv->stream_count += enable ? 1 : -1; if (priv->stream_count < 0) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH] media: imx-csi: Input connections to CSI should be optional
Hi Tim, On 1/15/19 8:42 AM, Tim Harvey wrote: On Wed, Jan 9, 2019 at 10:34 AM Steve Longerbeam wrote: Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected CSI input port as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 4223f8d418ae..30b1717982ae 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1787,7 +1787,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) -- 2.17.1 Steve, Thanks, this fixes adv7180 the capture regression on the Gateworks Ventana boards as well. This should go to stable to fix 4.20 so please add a 'Cc: sta...@vger.kernel.org' if you re-submit (else we can send it to sta...@vger.kernel.org later). Acked-by: Tim Harvey I will resubmit a non-functional v2 that adds the Cc: stable and your ack. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v2] media: imx-csi: Input connections to CSI should be optional
Some imx platforms do not have fwnode connections to all CSI input ports, and should not be treated as an error. This includes the imx6q SabreAuto, which has no connections to ipu1_csi1 and ipu2_csi0. Return -ENOTCONN in imx_csi_parse_endpoint() so that v4l2-fwnode endpoint parsing will not treat an unconnected CSI input port as an error. Fixes: c893500a16baf ("media: imx: csi: Register a subdev notifier") Signed-off-by: Steve Longerbeam Reviewed-by: Philipp Zabel Acked-by: Tim Harvey Cc: sta...@vger.kernel.org --- Changes in v2: - Added some acks and Cc: stable. No functional changes. --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 555aa45e02e3..e18f58f56dfb 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1861,7 +1861,7 @@ static int imx_csi_parse_endpoint(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { - return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; + return fwnode_device_is_available(asd->match.fwnode) ? 0 : -ENOTCONN; } static int imx_csi_async_register(struct csi_priv *priv) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v2 1/2] media: imx: csi: Disable CSI immediately after last EOF
On 1/18/19 2:24 AM, Philipp Zabel wrote: On Thu, 2019-01-17 at 12:49 -0800, Steve Longerbeam wrote: Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v2: - restore an empty line - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-media-csi.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e18f58f56dfb..e0f6f88e2e70 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -681,6 +681,8 @@ static void csi_idmac_stop(struct csi_priv *priv) if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); + ipu_csi_disable(priv->csi); + Can you add a short comment why this call is here? Since now csi_idmac_stop is kind of a misnomer and symmetry with csi(_idmac)_start is broken, I think this is a bit un-obvious. Yeah. I think a cleaner, more symmetric solution would be to split up csi_idmac_stop. Also note that now the error path of csi_start() will now call ipu_csi_disable() while the CSI is disabled. This happens to work because that just calls ipu_module_disable(), which is not refcounted. Thanks for catching. Splitting up csi_idmac_stop will fix this. Working on that. Steve devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -793,9 +795,9 @@ static void csi_stop(struct csi_priv *priv) /* stop the frame interval monitor */ if (priv->fim) imx_media_fim_set_stream(priv->fim, NULL, false); + } else { + ipu_csi_disable(priv->csi); } - - ipu_csi_disable(priv->csi); Reviewed-by: Philipp Zabel regards Philipp ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v2 1/2] media: imx: csi: Disable CSI immediately after last EOF
On 1/18/19 11:01 AM, Steve Longerbeam wrote: On 1/18/19 2:24 AM, Philipp Zabel wrote: On Thu, 2019-01-17 at 12:49 -0800, Steve Longerbeam wrote: Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v2: - restore an empty line - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-media-csi.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e18f58f56dfb..e0f6f88e2e70 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -681,6 +681,8 @@ static void csi_idmac_stop(struct csi_priv *priv) if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); + ipu_csi_disable(priv->csi); + Can you add a short comment why this call is here? Since now csi_idmac_stop is kind of a misnomer and symmetry with csi(_idmac)_start is broken, I think this is a bit un-obvious. Yeah. I think a cleaner, more symmetric solution would be to split up csi_idmac_stop. Also note that now the error path of csi_start() will now call ipu_csi_disable() while the CSI is disabled. This happens to work because that just calls ipu_module_disable(), which is not refcounted. Thanks for catching. Splitting up csi_idmac_stop will fix this. Working on that. Well turns out Peter Seiderer kindly provided a solution they have been using for some time on custom imx6 hardware. His solution is to disable the SMFC before the IDMA channel, which I have tested on the SabreAuto and also works. This is a much simpler change, so I will post v3 that does this instead. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v3 2/2] media: imx: prpencvf: Stop upstream before disabling IDMA channel
Upstream must be stopped immediately after receiving the last EOF and before disabling the IDMA channel. This can be accomplished by moving upstream stream off to just after receiving the last EOF completion in prp_stop(). For symmetry also move upstream stream on to end of prp_start(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d1 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Stopping the video data stream entering the IDMA channel before disabling the channel itself appears to be a reliable fix for the hard lockup. Fixes: f0d9c8924e2c3 ("[media] media: imx: Add IC subdev drivers") Reported-by: Gaël PORTAY Tested-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v3: - Reword the commit subject and message. No functional changes. Changes in v2: - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-ic-prpencvf.c | 26 ++--- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 33ada6612fee..f53cdb608528 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -707,12 +707,23 @@ static int prp_start(struct prp_priv *priv) goto out_free_nfb4eof_irq; } + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) { + v4l2_err(&ic_priv->sd, +"upstream stream on failed: %d\n", ret); + goto out_free_eof_irq; + } + /* start the EOF timeout timer */ mod_timer(&priv->eof_timeout_timer, jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); return 0; +out_free_eof_irq: + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); out_free_nfb4eof_irq: devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); out_unsetup: @@ -744,6 +755,12 @@ static void prp_stop(struct prp_priv *priv) if (ret == 0) v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n"); + /* stop upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_warn(&ic_priv->sd, + "upstream stream off failed: %d\n", ret); + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); @@ -1174,15 +1191,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) goto out; - /* start/stop upstream */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) { - if (enable) - prp_stop(priv); - goto out; - } - update_count: priv->stream_count += enable ? 1 : -1; if (priv->stream_count < 0) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v3 1/2] media: imx: csi: Disable SMFC before disabling IDMA channel
Disable the SMFC before disabling the IDMA channel, instead of after, in csi_idmac_unsetup(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Stopping the video data stream entering the IDMA channel before disabling the channel itself appears to be a reliable fix for the hard lockup. That can be done either by disabling the SMFC or the CSI before disabling the channel. Disabling the SMFC before the channel is the easiest solution, so do that. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Suggested-by: Peter Seiderer Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v3: - switch from disabling the CSI before the channel to disabling the SMFC before the channel. Changes in v2: - restore an empty line - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e18f58f56dfb..8610027eac97 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -560,8 +560,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) static void csi_idmac_unsetup(struct csi_priv *priv, enum vb2_buffer_state state) { - ipu_idmac_disable_channel(priv->idmac_ch); ipu_smfc_disable(priv->smfc); + ipu_idmac_disable_channel(priv->idmac_ch); csi_idmac_unsetup_vb2_buf(priv, state); } -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 1/4] media: imx: csi: Allow unknown nearest upstream entities
On i.MX6, the nearest upstream entity to the CSI can only be the CSI video muxes or the Synopsys DW MIPI CSI-2 receiver. However the i.MX53 has no CSI video muxes or a MIPI CSI-2 receiver. So allow for the nearest upstream entity to the CSI to be something other than those. Fixes: bf3cfaa712e5c ("media: staging/imx: get CSI bus type from nearest upstream entity") Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/staging/media/imx/imx-media-csi.c | 18 ++ 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 555aa45e02e3..b9af7d3d4974 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -154,9 +154,10 @@ static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep, /* * Parses the fwnode endpoint from the source pad of the entity * connected to this CSI. This will either be the entity directly - * upstream from the CSI-2 receiver, or directly upstream from the - * video mux. The endpoint is needed to determine the bus type and - * bus config coming into the CSI. + * upstream from the CSI-2 receiver, directly upstream from the + * video mux, or directly upstream from the CSI itself. The endpoint + * is needed to determine the bus type and bus config coming into + * the CSI. */ static int csi_get_upstream_endpoint(struct csi_priv *priv, struct v4l2_fwnode_endpoint *ep) @@ -172,7 +173,8 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, if (!priv->src_sd) return -EPIPE; - src = &priv->src_sd->entity; + sd = priv->src_sd; + src = &sd->entity; if (src->function == MEDIA_ENT_F_VID_MUX) { /* @@ -186,6 +188,14 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, src = &sd->entity; } + /* +* If the source is neither the video mux nor the CSI-2 receiver, +* get the source pad directly upstream from CSI itself. +*/ + if (src->function != MEDIA_ENT_F_VID_MUX && + sd->grp_id != IMX_MEDIA_GRP_ID_CSI2) + src = &priv->sd.entity; + /* get source pad of entity directly upstream from src */ pad = imx_media_find_upstream_pad(priv->md, src, 0); if (IS_ERR(pad)) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 3/4] media: imx: Rename functions that add IPU-internal subdevs/links
For the functions that add and remove the internal IPU subdevice descriptors and links between them, rename them to make clear they are the subdevs and links internal to the IPU. Also rename the platform data structure for the internal IPU subdevices. No functional changes. Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/staging/media/imx/imx-ic-common.c| 2 +- drivers/staging/media/imx/imx-media-dev.c| 10 +- .../staging/media/imx/imx-media-internal-sd.c| 16 drivers/staging/media/imx/imx-media-vdic.c | 2 +- drivers/staging/media/imx/imx-media.h| 10 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-common.c b/drivers/staging/media/imx/imx-ic-common.c index cfdd4900a3be..94dc5d88a069 100644 --- a/drivers/staging/media/imx/imx-ic-common.c +++ b/drivers/staging/media/imx/imx-ic-common.c @@ -26,7 +26,7 @@ static struct imx_ic_ops *ic_ops[IC_NUM_OPS] = { static int imx_ic_probe(struct platform_device *pdev) { - struct imx_media_internal_sd_platformdata *pdata; + struct imx_media_ipu_internal_sd_pdata *pdata; struct imx_ic_priv *priv; int ret; diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 4b344a4a3706..edf9c80bbbc8 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -155,7 +155,7 @@ static int imx_media_create_links(struct v4l2_async_notifier *notifier) case IMX_MEDIA_GRP_ID_IC_PRPVF: case IMX_MEDIA_GRP_ID_CSI0: case IMX_MEDIA_GRP_ID_CSI1: - ret = imx_media_create_internal_links(imxmd, sd); + ret = imx_media_create_ipu_internal_links(imxmd, sd); if (ret) return ret; /* @@ -487,10 +487,10 @@ static int imx_media_probe(struct platform_device *pdev) goto notifier_cleanup; } - ret = imx_media_add_internal_subdevs(imxmd); + ret = imx_media_add_ipu_internal_subdevs(imxmd); if (ret) { v4l2_err(&imxmd->v4l2_dev, -"add_internal_subdevs failed with %d\n", ret); +"add_ipu_internal_subdevs failed with %d\n", ret); goto notifier_cleanup; } @@ -513,7 +513,7 @@ static int imx_media_probe(struct platform_device *pdev) return 0; del_int: - imx_media_remove_internal_subdevs(imxmd); + imx_media_remove_ipu_internal_subdevs(imxmd); notifier_cleanup: v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); @@ -530,7 +530,7 @@ static int imx_media_remove(struct platform_device *pdev) v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n"); v4l2_async_notifier_unregister(&imxmd->notifier); - imx_media_remove_internal_subdevs(imxmd); + imx_media_remove_ipu_internal_subdevs(imxmd); v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); media_device_unregister(&imxmd->md); diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c index 0fdc45dbfb76..b8e763dbbecb 100644 --- a/drivers/staging/media/imx/imx-media-internal-sd.c +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -1,7 +1,7 @@ /* * Media driver for Freescale i.MX5/6 SOC * - * Adds the internal subdevices and the media links between them. + * Adds the IPU internal subdevices and the media links between them. * * Copyright (c) 2016 Mentor Graphics Inc. * @@ -192,7 +192,7 @@ static struct v4l2_subdev *find_sink(struct imx_media_dev *imxmd, /* * retrieve IPU id from subdev name, note: can't get this from -* struct imx_media_internal_sd_platformdata because if src is +* struct imx_media_ipu_internal_sd_pdata because if src is * a CSI, it has different struct ipu_client_platformdata which * does not contain IPU id. */ @@ -229,8 +229,8 @@ static int create_ipu_internal_link(struct imx_media_dev *imxmd, return ret; } -int imx_media_create_internal_links(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd) +int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd) { const struct internal_subdev *intsd; const struct internal_pad *intpad; @@ -270,7 +270,7 @@ static int add_internal_subdev(struct imx_media_dev *imxmd, const struct internal_subdev *isd, int ipu_id) { - struct imx_media_internal_
[PATCH 2/4] media: imx: Clear fwnode link struct for each endpoint iteration
In imx_media_create_csi_of_links(), the 'struct v4l2_fwnode_link' must be cleared for each endpoint iteration, otherwise if the remote port has no "reg" property, link.remote_port will not be reset to zero. This was discovered on the i.MX53 SMD board, since the OV5642 connects directly to ipu1_csi0 and has a single source port with no "reg" property. Fixes: 621b08eabcddb ("media: staging/imx: remove static media link arrays") Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/staging/media/imx/imx-media-of.c | 15 +-- 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index a01327f6e045..2da81a5af274 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -143,15 +143,18 @@ int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, struct v4l2_subdev *csi) { struct device_node *csi_np = csi->dev->of_node; - struct fwnode_handle *fwnode, *csi_ep; - struct v4l2_fwnode_link link; struct device_node *ep; - int ret; - - link.local_node = of_fwnode_handle(csi_np); - link.local_port = CSI_SINK_PAD; for_each_child_of_node(csi_np, ep) { + struct fwnode_handle *fwnode, *csi_ep; + struct v4l2_fwnode_link link; + int ret; + + memset(&link, 0, sizeof(link)); + + link.local_node = of_fwnode_handle(csi_np); + link.local_port = CSI_SINK_PAD; + csi_ep = of_fwnode_handle(ep); fwnode = fwnode_graph_get_remote_endpoint(csi_ep); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 4/4] media: imx: Don't register IPU subdevs/links if CSI port missing
The second IPU internal sub-devices were being registered and links to them created even when the second IPU is not present. This is wrong for i.MX6 S/DL and i.MX53 which have only a single IPU. Fixes: e130291212df5 ("[media] media: Add i.MX media core driver") Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/staging/media/imx/imx-media-dev.c | 7 -- .../staging/media/imx/imx-media-internal-sd.c | 22 +- drivers/staging/media/imx/imx-media-of.c | 76 --- drivers/staging/media/imx/imx-media.h | 3 +- 4 files changed, 53 insertions(+), 55 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index edf9c80bbbc8..924166cf957b 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -487,13 +487,6 @@ static int imx_media_probe(struct platform_device *pdev) goto notifier_cleanup; } - ret = imx_media_add_ipu_internal_subdevs(imxmd); - if (ret) { - v4l2_err(&imxmd->v4l2_dev, -"add_ipu_internal_subdevs failed with %d\n", ret); - goto notifier_cleanup; - } - /* no subdevs? just bail */ if (list_empty(&imxmd->notifier.asd_list)) { ret = -ENODEV; diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c index b8e763dbbecb..3811c12d475b 100644 --- a/drivers/staging/media/imx/imx-media-internal-sd.c +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -298,13 +298,14 @@ static int add_internal_subdev(struct imx_media_dev *imxmd, } /* adds the internal subdevs in one ipu */ -static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id) +int imx_media_add_ipu_internal_subdevs(struct imx_media_dev *imxmd, + int ipu_id) { enum isd_enum i; + int ret; for (i = 0; i < num_isd; i++) { const struct internal_subdev *isd = &int_subdev[i]; - int ret; /* * the CSIs are represented in the device-tree, so those @@ -322,25 +323,10 @@ static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id) } if (ret) - return ret; + goto remove; } return 0; -} - -int imx_media_add_ipu_internal_subdevs(struct imx_media_dev *imxmd) -{ - int ret; - - ret = add_ipu_internal_subdevs(imxmd, 0); - if (ret) - goto remove; - - ret = add_ipu_internal_subdevs(imxmd, 1); - if (ret) - goto remove; - - return 0; remove: imx_media_remove_ipu_internal_subdevs(imxmd); diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index 2da81a5af274..32ed1a99813f 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,50 +20,68 @@ #include #include "imx-media.h" -static int of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np) -{ - int ret; - - if (!of_device_is_available(csi_np)) { - dev_dbg(imxmd->md.dev, "%s: %pOFn not enabled\n", __func__, - csi_np); - /* unavailable is not an error */ - return 0; - } - - /* add CSI fwnode to async notifier */ - ret = imx_media_add_async_subdev(imxmd, of_fwnode_handle(csi_np), NULL); - if (ret) { - if (ret == -EEXIST) { - /* already added, everything is fine */ - return 0; - } - - /* other error, can't continue */ - return ret; - } - - return 0; -} - int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, struct device_node *np) { + bool ipu_found[2] = {false, false}; struct device_node *csi_np; int i, ret; + u32 ipu_id; for (i = 0; ; i++) { csi_np = of_parse_phandle(np, "ports", i); if (!csi_np) break; - ret = of_add_csi(imxmd, csi_np); + if (!of_device_is_available(csi_np)) { + /* ignore this CSI if not available */ + of_node_put(csi_np); + continue; + } + + /* add CSI fwnode to async notifier */ + ret = imx_media_add_async_subdev(imxmd, +of_fwnode_handle(csi_np), +NULL); + if (ret) { + if (ret == -EEXIST) { + /* already
[PATCH] media: imx: Validate frame intervals before setting
In the .s_frame_interval() subdev op, don't accept or set a frame interval with a zero numerator or denominator. This fixes a v4l2-compliance failure: fail: v4l2-test-formats.cpp(1146): cap->timeperframe.numerator == 0 || cap->timeperframe.denominator == 0 test VIDIOC_G/S_PARM: FAIL Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-ic-prp.c | 9 +++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 9 +++-- drivers/staging/media/imx/imx-media-csi.c | 5 - drivers/staging/media/imx/imx-media-vdic.c | 5 - 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 98923fc844ce..a2bb5c702d74 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -422,9 +422,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRP_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 33ada6612fee..d35591e9933b 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -1215,9 +1215,14 @@ static int prp_s_frame_interval(struct v4l2_subdev *sd, if (fi->pad >= PRPENCVF_NUM_PADS) return -EINVAL; - /* No limits on frame interval */ mutex_lock(&priv->lock); - priv->frame_interval = fi->interval; + + /* No limits on valid frame intervals */ + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) + fi->interval = priv->frame_interval; + else + priv->frame_interval = fi->interval; + mutex_unlock(&priv->lock); return 0; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 555aa45e02e3..81f78a928048 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -905,7 +905,10 @@ static int csi_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case CSI_SINK_PAD: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = *input_fi; /* Reset output intervals and frame skipping ratio to 1:1 */ priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval; priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval; diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 4a890714193e..62e09a53d171 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -818,7 +818,10 @@ static int vdic_s_frame_interval(struct v4l2_subdev *sd, switch (fi->pad) { case VDIC_SINK_PAD_DIRECT: case VDIC_SINK_PAD_IDMAC: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = priv->frame_interval[fi->pad]; /* Reset output interval */ *output_fi = fi->interval; if (priv->csi_direct) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v3 1/2] media: imx: csi: Disable SMFC before disabling IDMA channel
On 1/21/19 3:49 AM, Philipp Zabel wrote: Hi, On Fri, 2019-01-18 at 17:04 -0800, Steve Longerbeam wrote: Disable the SMFC before disabling the IDMA channel, instead of after, in csi_idmac_unsetup(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Stopping the video data stream entering the IDMA channel before disabling the channel itself appears to be a reliable fix for the hard lockup. That can be done either by disabling the SMFC or the CSI before disabling the channel. Disabling the SMFC before the channel is the easiest solution, so do that. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Suggested-by: Peter Seiderer Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Gaël, could we get a Tested-by: for this as well? Cc: sta...@vger.kernel.org --- Changes in v3: - switch from disabling the CSI before the channel to disabling the SMFC before the channel. Changes in v2: - restore an empty line - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-media-csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index e18f58f56dfb..8610027eac97 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -560,8 +560,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) static void csi_idmac_unsetup(struct csi_priv *priv, enum vb2_buffer_state state) { - ipu_idmac_disable_channel(priv->idmac_ch); ipu_smfc_disable(priv->smfc); + ipu_idmac_disable_channel(priv->idmac_ch); Steve, do you have any theory why this helps? It's a bit weird to disable the SMFC module while the DMA channel is still enabled. It does fix the hang, but I only have a vague theory as to why. That by disabling the DMA channel while its internal FIFO is getting filled is causing the hang, maybe due to a simultaneous update of the channel's internal FIFO write pointer and the channel disable bit. By disabling the SMFC (or the CSI), writes to the channel's internal FIFO stop. I think this warrants a big comment, given that enable order is SMFC_EN before IDMAC channel enable. Also ipu_smfc_disable is refcounted, so if the other CSI is capturing simultaneously, this change has no effect. Sigh, you're right. Let me go back to disabling the CSI before the channel, the CSI enable/disable is not refcounted (it doesn't need to be since it is single use) so it doesn't have this problem. Should we drop this patch or keep it (with a big comment)? By only changing the disable order to "CSI then channel", the hang is reliably fixed from my and Gael's testing, but my concern is that by not disabling the SMFC before the channel, the SMFC could still empty its FIFO to the channel's internal FIFO and still create a hang. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH v3 1/2] media: imx: csi: Disable SMFC before disabling IDMA channel
On 1/21/19 10:43 AM, Steve Longerbeam wrote: On 1/21/19 3:49 AM, Philipp Zabel wrote: Also ipu_smfc_disable is refcounted, so if the other CSI is capturing simultaneously, this change has no effect. Sigh, you're right. Let me go back to disabling the CSI before the channel, the CSI enable/disable is not refcounted (it doesn't need to be since it is single use) so it doesn't have this problem. Should we drop this patch or keep it (with a big comment)? By only changing the disable order to "CSI then channel", the hang is reliably fixed from my and Gael's testing, but my concern is that by not disabling the SMFC before the channel, the SMFC could still empty its FIFO to the channel's internal FIFO and still create a hang. Well, as you said it will have no effect if both CSI's are streaming with the SMFC, in which case the danger would still exist. Perhaps it would be best to just drop this patch. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 2/3] media: imx: csi: Stop upstream before disabling IDMA channel
Move upstream stream off to just after receiving the last EOF completion and disabling the CSI (and thus before disabling the IDMA channel) in csi_stop(). For symmetry also move upstream stream on to beginning of csi_start(). Doing this makes csi_s_stream() more symmetric with prp_s_stream() which will require the same change to fix a hard lockup. Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/staging/media/imx/imx-media-csi.c | 25 --- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 920e38885292..d851ca2497b4 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -753,10 +753,16 @@ static int csi_start(struct csi_priv *priv) output_fi = &priv->frame_interval[priv->active_output_pad]; + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) + return ret; + if (priv->dest == IPU_CSI_DEST_IDMAC) { ret = csi_idmac_start(priv); if (ret) - return ret; + goto stop_upstream; } ret = csi_setup(priv); @@ -784,6 +790,8 @@ static int csi_start(struct csi_priv *priv) idmac_stop: if (priv->dest == IPU_CSI_DEST_IDMAC) csi_idmac_stop(priv); +stop_upstream: + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); return ret; } @@ -799,6 +807,9 @@ static void csi_stop(struct csi_priv *priv) */ ipu_csi_disable(priv->csi); + /* stop upstream */ + v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (priv->dest == IPU_CSI_DEST_IDMAC) { csi_idmac_stop(priv); @@ -966,23 +977,13 @@ static int csi_s_stream(struct v4l2_subdev *sd, int enable) goto update_count; if (enable) { - /* upstream must be started first, before starting CSI */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) - goto out; - dev_dbg(priv->dev, "stream ON\n"); ret = csi_start(priv); - if (ret) { - v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret) goto out; - } } else { dev_dbg(priv->dev, "stream OFF\n"); - /* CSI must be stopped first, then stop upstream */ csi_stop(priv); - v4l2_subdev_call(priv->src_sd, video, s_stream, 0); } update_count: -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 1/3] media: imx: csi: Disable CSI immediately after last EOF
Disable the CSI immediately after receiving the last EOF before stream off (and thus before disabling the IDMA channel). Do this by moving the wait for EOF completion into a new function csi_idmac_wait_last_eof(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d4 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Disabling the CSI before disabling the IDMA channel appears to be a reliable fix for the hard lockup. Fixes: 4a34ec8e470cb ("[media] media: imx: Add CSI subdev driver") Reported-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v4: - Disabling SMFC will have no effect if both CSI's are streaming. So go back to disabling CSI before channel as in v2, but split up csi_idmac_stop such that ipu_csi_disable can still be called within csi_stop. Changes in v3: - switch from disabling the CSI before the channel to disabling the SMFC before the channel. Changes in v2: - restore an empty line - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-media-csi.c | 17 ++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 7abfe0aa1418..920e38885292 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -662,7 +662,7 @@ static int csi_idmac_start(struct csi_priv *priv) return ret; } -static void csi_idmac_stop(struct csi_priv *priv) +static void csi_idmac_wait_last_eof(struct csi_priv *priv) { unsigned long flags; int ret; @@ -679,7 +679,10 @@ static void csi_idmac_stop(struct csi_priv *priv) &priv->last_eof_comp, msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); if (ret == 0) v4l2_warn(&priv->sd, "wait last EOF timeout\n"); +} +static void csi_idmac_stop(struct csi_priv *priv) +{ devm_free_irq(priv->dev, priv->eof_irq, priv); devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); @@ -786,6 +789,16 @@ static int csi_start(struct csi_priv *priv) static void csi_stop(struct csi_priv *priv) { + if (priv->dest == IPU_CSI_DEST_IDMAC) + csi_idmac_wait_last_eof(priv); + + /* +* Disable the CSI asap, after syncing with the last EOF. +* Doing so after the IDMA channel is disabled has shown to +* create hard system-wide hangs. +*/ + ipu_csi_disable(priv->csi); + if (priv->dest == IPU_CSI_DEST_IDMAC) { csi_idmac_stop(priv); @@ -793,8 +806,6 @@ static void csi_stop(struct csi_priv *priv) if (priv->fim) imx_media_fim_set_stream(priv->fim, NULL, false); } - - ipu_csi_disable(priv->csi); } static const struct csi_skip_desc csi_skip[12] = { -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v4 3/3] media: imx: prpencvf: Stop upstream before disabling IDMA channel
Upstream must be stopped immediately after receiving the last EOF and before disabling the IDMA channel. This can be accomplished by moving upstream stream off to just after receiving the last EOF completion in prp_stop(). For symmetry also move upstream stream on to end of prp_start(). This fixes a complete system hard lockup on the SabreAuto when streaming from the ADV7180, by repeatedly sending a stream off immediately followed by stream on: while true; do v4l2-ctl -d1 --stream-mmap --stream-count=3; done Eventually this either causes the system lockup or EOF timeouts at all subsequent stream on, until a system reset. The lockup occurs when disabling the IDMA channel at stream off. Stopping the video data stream entering the IDMA channel before disabling the channel itself appears to be a reliable fix for the hard lockup. Fixes: f0d9c8924e2c3 ("[media] media: imx: Add IC subdev drivers") Reported-by: Gaël PORTAY Tested-by: Gaël PORTAY Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- Changes in v4: - none. Changes in v3: - Reword the commit subject and message. No functional changes. Changes in v2: - Add Fixes: and Cc: stable --- drivers/staging/media/imx/imx-ic-prpencvf.c | 26 ++--- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 053a911d477a..3637693c2bc8 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -706,12 +706,23 @@ static int prp_start(struct prp_priv *priv) goto out_free_nfb4eof_irq; } + /* start upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 1); + ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; + if (ret) { + v4l2_err(&ic_priv->sd, +"upstream stream on failed: %d\n", ret); + goto out_free_eof_irq; + } + /* start the EOF timeout timer */ mod_timer(&priv->eof_timeout_timer, jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); return 0; +out_free_eof_irq: + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); out_free_nfb4eof_irq: devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); out_unsetup: @@ -743,6 +754,12 @@ static void prp_stop(struct prp_priv *priv) if (ret == 0) v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n"); + /* stop upstream */ + ret = v4l2_subdev_call(priv->src_sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_warn(&ic_priv->sd, + "upstream stream off failed: %d\n", ret); + devm_free_irq(ic_priv->dev, priv->eof_irq, priv); devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); @@ -1173,15 +1190,6 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) goto out; - /* start/stop upstream */ - ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); - ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; - if (ret) { - if (enable) - prp_stop(priv); - goto out; - } - update_count: priv->stream_count += enable ? 1 : -1; if (priv->stream_count < 0) -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH 1/4] media: imx: csi: Allow unknown nearest upstream entities
On 1/21/19 8:50 AM, Philipp Zabel wrote: On Sat, 2019-01-19 at 13:45 -0800, Steve Longerbeam wrote: On i.MX6, the nearest upstream entity to the CSI can only be the CSI video muxes or the Synopsys DW MIPI CSI-2 receiver. However the i.MX53 has no CSI video muxes or a MIPI CSI-2 receiver. So allow for the nearest upstream entity to the CSI to be something other than those. Fixes: bf3cfaa712e5c ("media: staging/imx: get CSI bus type from nearest upstream entity") Signed-off-by: Steve Longerbeam Cc: sta...@vger.kernel.org --- drivers/staging/media/imx/imx-media-csi.c | 18 ++ 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 555aa45e02e3..b9af7d3d4974 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -154,9 +154,10 @@ static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep, /* * Parses the fwnode endpoint from the source pad of the entity * connected to this CSI. This will either be the entity directly - * upstream from the CSI-2 receiver, or directly upstream from the - * video mux. The endpoint is needed to determine the bus type and - * bus config coming into the CSI. + * upstream from the CSI-2 receiver, directly upstream from the + * video mux, or directly upstream from the CSI itself. The endpoint + * is needed to determine the bus type and bus config coming into + * the CSI. */ static int csi_get_upstream_endpoint(struct csi_priv *priv, struct v4l2_fwnode_endpoint *ep) @@ -172,7 +173,8 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, if (!priv->src_sd) return -EPIPE; - src = &priv->src_sd->entity; + sd = priv->src_sd; + src = &sd->entity; if (src->function == MEDIA_ENT_F_VID_MUX) { /* @@ -186,6 +188,14 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, src = &sd->entity; } + /* +* If the source is neither the video mux nor the CSI-2 receiver, +* get the source pad directly upstream from CSI itself. +*/ + if (src->function != MEDIA_ENT_F_VID_MUX && Will it work correctly if there's an external MUX connected to the CSI? By external MUX are you referring to some MUX that's external to the SoC (e.g. not the CSI muxes which are external to the IPU but internal to the SoC)? If so then yes it will still work (and of course it works if the MUX in question is the CSI muxes). The function csi_get_upstream_endpoint() is only looking for, and stops at, the first sub-device that is external to the SoC, in order to determine the bus type coming into the SoC of the currently enabled pipeline. And that sub-device could be anything, including another MUX. Steve + sd->grp_id != IMX_MEDIA_GRP_ID_CSI2) + src = &priv->sd.entity; + /* get source pad of entity directly upstream from src */ pad = imx_media_find_upstream_pad(priv->md, src, 0); if (IS_ERR(pad)) regards Philipp ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 2/3] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 67 ++--- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 35ae86ff0585..63362b4fff81 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -199,6 +199,23 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .scale = 1, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* transparent RGB->RGB matrix for graphics combining */ static const struct ic_csc_params ic_csc_rgb2rgb = { .coeff = { @@ -226,12 +243,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -241,10 +277,24 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt601; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt709; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt709; + break; + default: + dev_err(priv->ipu->dev, "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = &ic_csc_ycbcr2rgb_bt601; + params = params_yuv2rgb; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = &ic_csc_rgb2ycbcr_bt601; + params = params_rgb2yuv; else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB) params = &ic_csc_rgb2rgb; else { @@ -391,6 +441,7 @@ EXPORT_SYMBOL_GPL(ipu_ic_task_disable); int ipu_ic_task_graphics_init(struct ipu_ic *ic, enum ipu_color_space in_g_cs, + enum v4l2_ycbcr_encoding encoding, bool galpha_en, u32 galpha, bool colorkey_en, u32 colorkey) { @@ -409,7 +460,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { /* need transparent CSC1 conversion */ ret = init_csc(ic, IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB, 0); + IPUV3_COLORSPACE_RGB, encoding, 0); if (ret) goto unlock; } @@ -417,7 +468,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, ic->g_in_cs = in_g_cs; if (ic->g_in_cs != ic->out_cs) { - ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1); + ret = init_csc(ic, ic->g_in_cs, ic->out_cs, encoding, 1); if (ret) goto unlock; } @@ -451,6 +502,7 @@ int ipu_ic_task_init_rsc(struct ipu_ic *ic, int out_width, int out_height, enum ipu_color_sp
[PATCH 3/3] media: imx: Allow BT.709 encoding for IC routes
The IC now supports BT.709 Y'CbCr encoding, in addition to existing BT.601 encoding, so allow both, for pipelines that route through the IC. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 5f110d90a4ef..3512f09fb226 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -571,7 +571,9 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, tryfmt->quantization = is_rgb ? V4L2_QUANTIZATION_FULL_RANGE : V4L2_QUANTIZATION_LIM_RANGE; - tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + if (tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_601 && + tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_709) + tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; } } EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH 3/3] media: imx: Allow BT.709 encoding for IC routes
Sorry this patch isn't working, it's not possible to set BT.709 encoding, working on a fix for v2. Steve On 2/3/19 11:47 AM, Steve Longerbeam wrote: The IC now supports BT.709 Y'CbCr encoding, in addition to existing BT.601 encoding, so allow both, for pipelines that route through the IC. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-media-utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 5f110d90a4ef..3512f09fb226 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -571,7 +571,9 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, tryfmt->quantization = is_rgb ? V4L2_QUANTIZATION_FULL_RANGE : V4L2_QUANTIZATION_LIM_RANGE; - tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + if (tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_601 && + tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_709) + tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; } } EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields); ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH] media: imx: Set capture compose rectangle in capture_device_set_format
From: Steve Longerbeam The capture compose rectangle was not getting updated when setting the source subdevice's source pad format. This causes the compose window to be zero (or not updated) at stream start unless the capture device format was set explicitly at the capture device node. Fix by moving the calculation of the capture compose rectangle to imx_media_mbus_fmt_to_pix_fmt(), and pass the rectangle to imx_media_capture_device_set_format(). Fixes: 439d8186fb23 ("media: imx: add capture compose rectangle") Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/imx-ic-prpencvf.c | 5 ++-- drivers/staging/media/imx/imx-media-capture.c | 24 +-- drivers/staging/media/imx/imx-media-csi.c | 5 ++-- drivers/staging/media/imx/imx-media-utils.c | 20 drivers/staging/media/imx/imx-media.h | 6 +++-- 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 376b504e8a42..5c8e6ad8c025 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -912,6 +912,7 @@ static int prp_set_fmt(struct v4l2_subdev *sd, const struct imx_media_pixfmt *cc; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect vdev_compose; int ret = 0; if (sdformat->pad >= PRPENCVF_NUM_PADS) @@ -953,11 +954,11 @@ static int prp_set_fmt(struct v4l2_subdev *sd, priv->cc[sdformat->pad] = cc; /* propagate output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, + imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, &priv->format_mbus[PRPENCVF_SRC_PAD], priv->cc[PRPENCVF_SRC_PAD]); mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt); + imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); return 0; out: diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index f92edee63aa6..9703c85b19c4 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -205,7 +205,8 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, static int __capture_try_fmt_vid_cap(struct capture_priv *priv, struct v4l2_subdev_format *fmt_src, -struct v4l2_format *f) +struct v4l2_format *f, +struct v4l2_rect *compose) { const struct imx_media_pixfmt *cc, *cc_src; @@ -245,7 +246,8 @@ static int __capture_try_fmt_vid_cap(struct capture_priv *priv, } } - imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src->format, cc); + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, compose, + &fmt_src->format, cc); return 0; } @@ -263,7 +265,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - return __capture_try_fmt_vid_cap(priv, &fmt_src, f); + return __capture_try_fmt_vid_cap(priv, &fmt_src, f, NULL); } static int capture_s_fmt_vid_cap(struct file *file, void *fh, @@ -271,6 +273,7 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, { struct capture_priv *priv = video_drvdata(file); struct v4l2_subdev_format fmt_src; + struct v4l2_rect compose; int ret; if (vb2_is_busy(&priv->q)) { @@ -284,17 +287,14 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f); + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f, &compose); if (ret) return ret; priv->vdev.fmt.fmt.pix = f->fmt.pix; priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat, CS_SEL_ANY, true); - priv->vdev.compose.left = 0; - priv->vdev.compose.top = 0; - priv->vdev.compose.width = fmt_src.format.width; - priv->vdev.compose.height = fmt_src.format.height; + priv->vdev.compose = compose; return 0; } @@ -655,7 +655,8 @@ static struct video_device capture_videodev = { }; void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, -struct v4l2_pix_format *pix) +const struct v4l2_pix_format *pix, +const struct v
Re: [PATCH 2/3] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
On 2/8/19 8:24 AM, Tim Harvey wrote: On Sun, Feb 3, 2019 at 11:48 AM Steve Longerbeam wrote: Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-ic.c | 67 ++--- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 35ae86ff0585..63362b4fff81 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -199,6 +199,23 @@ static const struct ic_csc_params ic_csc_rgb2ycbcr_bt601 = { .scale = 1, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* transparent RGB->RGB matrix for graphics combining */ static const struct ic_csc_params ic_csc_rgb2rgb = { .coeff = { @@ -226,12 +243,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -241,10 +277,24 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt601; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt709; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt709; + break; + default: + dev_err(priv->ipu->dev, "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + Steve, This will fail for RGB to RGB with 'Unsupported YCbCr encoding. We need to account for the RGB->RGB case. How about something like: Thanks for reporting Tim I rather keep the check for supported encoding, and instead get rid of "Unsupported color space conversion" error, because that is the YUV->YUV case which can be allowed using the identity matrix. Steve static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; - const struct ic_csc_params *params; + const struct ic_csc_params *params = NULL; u32 __iomem *base; const u16 (*c)[3]; const u16 *a; @@ -241,13 +276,18 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); - if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) - params = &ic_csc_ycbcr2rgb_bt601; - else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) - params = &ic_csc_rgb2ycbcr_bt601; + if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB) { + params = (encoding == V4L2_YCBCR_ENC_601) ? + &ic_csc_ycbcr2rgb_bt601 : &ic_csc_ycbcr2rgb_bt709; + } + else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV) { + params = (encoding == V4L2_YCBCR_ENC_601) ? + &ic_cs
[PATCH v2 4/4] media: imx: Allow BT.709 encoding for IC routes
From: Steve Longerbeam The IC now supports BT.709 Y'CbCr encoding, in addition to existing BT.601 encoding, so allow both, for pipelines that route through the IC. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v2: - move ic_route check above default colorimetry checks, and fill default colorimetry for ic_route, otherwise it's not possible to set BT.709 encoding for ic routes. --- drivers/staging/media/imx/imx-media-utils.c | 20 +--- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 5f110d90a4ef..dde0e47550d7 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -544,6 +544,19 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, if (tryfmt->field == V4L2_FIELD_ANY) tryfmt->field = fmt->field; + if (ic_route) { + if (tryfmt->colorspace == V4L2_COLORSPACE_DEFAULT) + tryfmt->colorspace = fmt->colorspace; + + tryfmt->quantization = is_rgb ? + V4L2_QUANTIZATION_FULL_RANGE : + V4L2_QUANTIZATION_LIM_RANGE; + + if (tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_601 && + tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_709) + tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + } + /* fill colorimetry if necessary */ if (tryfmt->colorspace == V4L2_COLORSPACE_DEFAULT) { tryfmt->colorspace = fmt->colorspace; @@ -566,13 +579,6 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, tryfmt->ycbcr_enc); } } - - if (ic_route) { - tryfmt->quantization = is_rgb ? - V4L2_QUANTIZATION_FULL_RANGE : - V4L2_QUANTIZATION_LIM_RANGE; - tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - } } EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v2 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
From: Steve Longerbeam Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 71 +++-- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index e459615a49a1..0d57ca7ba18e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -212,6 +212,23 @@ static const struct ic_csc_params ic_csc_identity = { .scale = 2, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV limited range to RGB full range: * @@ -229,12 +246,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -244,12 +280,30 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt601; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt709; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt709; + break; + default: + if (inf != outf) { + dev_err(priv->ipu->dev, + "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + break; + } + if (inf == outf) params = &ic_csc_identity; else if (inf == IPUV3_COLORSPACE_YUV) - params = &ic_csc_ycbcr2rgb_bt601; + params = &ic_csc_ycbcr2rgb; else - params = &ic_csc_rgb2ycbcr_bt601; + params = &ic_csc_rgb2ycbcr; /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; @@ -390,6 +444,7 @@ EXPORT_SYMBOL_GPL(ipu_ic_task_disable); int ipu_ic_task_graphics_init(struct ipu_ic *ic, enum ipu_color_space in_g_cs, + enum v4l2_ycbcr_encoding encoding, bool galpha_en, u32 galpha, bool colorkey_en, u32 colorkey) { @@ -408,7 +463,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { /* need transparent CSC1 conversion */ ret = init_csc(ic, IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB, 0); + IPUV3_COLORSPACE_RGB, encoding, 0); if (ret) goto unlock; } @@ -416,7 +471,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, ic->g_in_cs = in_g_cs; if (ic->g_in_cs != ic->out_cs) { - ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1); + ret = init_csc(ic, ic->g_in_cs, ic->out_cs, encoding, 1); if (ret) goto un
[PATCH v3 4/4] media: imx: Allow BT.709 encoding for IC routes
The IC now supports BT.709 Y'CbCr encoding, in addition to existing BT.601 encoding, so allow both, for pipelines that route through the IC. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v2: - move ic_route check above default colorimetry checks, and fill default colorimetry for ic_route, otherwise it's not possible to set BT.709 encoding for ic routes. --- drivers/staging/media/imx/imx-media-utils.c | 20 +--- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 5f110d90a4ef..dde0e47550d7 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -544,6 +544,19 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, if (tryfmt->field == V4L2_FIELD_ANY) tryfmt->field = fmt->field; + if (ic_route) { + if (tryfmt->colorspace == V4L2_COLORSPACE_DEFAULT) + tryfmt->colorspace = fmt->colorspace; + + tryfmt->quantization = is_rgb ? + V4L2_QUANTIZATION_FULL_RANGE : + V4L2_QUANTIZATION_LIM_RANGE; + + if (tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_601 && + tryfmt->ycbcr_enc != V4L2_YCBCR_ENC_709) + tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + } + /* fill colorimetry if necessary */ if (tryfmt->colorspace == V4L2_COLORSPACE_DEFAULT) { tryfmt->colorspace = fmt->colorspace; @@ -566,13 +579,6 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, tryfmt->ycbcr_enc); } } - - if (ic_route) { - tryfmt->quantization = is_rgb ? - V4L2_QUANTIZATION_FULL_RANGE : - V4L2_QUANTIZATION_LIM_RANGE; - tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - } } EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields); -- 2.17.1 ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v3 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
Pass v4l2 encoding enum to the ipu_ic task init functions, and add support for the BT.709 encoding and inverse encoding matrices. Reported-by: Tim Harvey Signed-off-by: Steve Longerbeam --- Changes in v2: - only return "Unsupported YCbCr encoding" error if inf != outf, since if inf == outf, the identity matrix can be used. Reported by Tim Harvey. --- drivers/gpu/ipu-v3/ipu-ic.c | 71 +++-- drivers/gpu/ipu-v3/ipu-image-convert.c | 1 + drivers/staging/media/imx/imx-ic-prpencvf.c | 4 +- include/video/imx-ipu-v3.h | 5 +- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index e459615a49a1..0d57ca7ba18e 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -212,6 +212,23 @@ static const struct ic_csc_params ic_csc_identity = { .scale = 2, }; +/* + * BT.709 encoding from RGB full range to YUV limited range: + * + * Y = R * .2126 + G * .7152 + B * .0722; + * U = R * -.1146 + G * -.3854 + B * .5000 + 128.; + * V = R * .5000 + G * -.4542 + B * -.0458 + 128.; + */ +static const struct ic_csc_params ic_csc_rgb2ycbcr_bt709 = { + .coeff = { + { 54, 183, 18 }, + { 483, 413, 128 }, + { 128, 396, 500 }, + }, + .offset = { 0, 512, 512 }, + .scale = 1, +}; + /* * Inverse BT.601 encoding from YUV limited range to RGB full range: * @@ -229,12 +246,31 @@ static const struct ic_csc_params ic_csc_ycbcr2rgb_bt601 = { .scale = 2, }; +/* + * Inverse BT.709 encoding from YUV limited range to RGB full range: + * + * R = (1. * (Y - 16)) + (1.5748 * (Cr - 128)); + * G = (1. * (Y - 16)) - (0.1873 * (Cb - 128)) - (0.4681 * (Cr - 128)); + * B = (1. * (Y - 16)) + (1.8556 * (Cb - 128); + */ +static const struct ic_csc_params ic_csc_ycbcr2rgb_bt709 = { + .coeff = { + { 128, 0, 202 }, + { 128, 488, 452 }, + { 128, 238, 0 }, + }, + .offset = { -435, 136, -507 }, + .scale = 2, +}; + static int init_csc(struct ipu_ic *ic, enum ipu_color_space inf, enum ipu_color_space outf, + enum v4l2_ycbcr_encoding encoding, int csc_index) { struct ipu_ic_priv *priv = ic->priv; + const struct ic_csc_params *params_rgb2yuv, *params_yuv2rgb; const struct ic_csc_params *params; u32 __iomem *base; const u16 (*c)[3]; @@ -244,12 +280,30 @@ static int init_csc(struct ipu_ic *ic, base = (u32 __iomem *) (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); + switch (encoding) { + case V4L2_YCBCR_ENC_601: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt601; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt601; + break; + case V4L2_YCBCR_ENC_709: + params_rgb2yuv = &ic_csc_rgb2ycbcr_bt709; + params_yuv2rgb = &ic_csc_ycbcr2rgb_bt709; + break; + default: + if (inf != outf) { + dev_err(priv->ipu->dev, + "Unsupported YCbCr encoding\n"); + return -EINVAL; + } + break; + } + if (inf == outf) params = &ic_csc_identity; else if (inf == IPUV3_COLORSPACE_YUV) - params = &ic_csc_ycbcr2rgb_bt601; + params = &ic_csc_ycbcr2rgb; else - params = &ic_csc_rgb2ycbcr_bt601; + params = &ic_csc_rgb2ycbcr; /* Cast to unsigned */ c = (const u16 (*)[3])params->coeff; @@ -390,6 +444,7 @@ EXPORT_SYMBOL_GPL(ipu_ic_task_disable); int ipu_ic_task_graphics_init(struct ipu_ic *ic, enum ipu_color_space in_g_cs, + enum v4l2_ycbcr_encoding encoding, bool galpha_en, u32 galpha, bool colorkey_en, u32 colorkey) { @@ -408,7 +463,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { /* need transparent CSC1 conversion */ ret = init_csc(ic, IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB, 0); + IPUV3_COLORSPACE_RGB, encoding, 0); if (ret) goto unlock; } @@ -416,7 +471,7 @@ int ipu_ic_task_graphics_init(struct ipu_ic *ic, ic->g_in_cs = in_g_cs; if (ic->g_in_cs != ic->out_cs) { - ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1); + ret = init_csc(ic, ic->g_in_cs, ic->out_cs, encoding, 1); if (ret) goto unlock; } @@ -450,6
Re: [PATCH v3 3/4] gpu: ipu-v3: ipu-ic: Add support for BT.709 encoding
On 2/8/19 4:20 PM, Tim Harvey wrote: On Fri, Feb 8, 2019 at 11:28 AM Steve Longerbeam wrote: if (inf == outf) params = &ic_csc_identity; else if (inf == IPUV3_COLORSPACE_YUV) - params = &ic_csc_ycbcr2rgb_bt601; + params = &ic_csc_ycbcr2rgb; Steve, compile issue... params = params_yuv2rgb; else - params = &ic_csc_rgb2ycbcr_bt601; + params = &ic_csc_rgb2ycbcr; params = params_rgb2yuv; Wow, did I not even compile test that? Must be my head cold :-/ Sending v4. But, I'm still failing when using the mem2mem element (gst-launch-1.0 v4l2src device=/dev/video4 ! v4l2video8convert output-io-mode=dmabuf-import ! fbdevsink) with 'Unsupported YCbCr encoding' because of inf=IPU_COLORSPACE_YCBCR outf=IPU_COLORSPACE_RGB and a seemingly unset encoding being passed in. It looks like maybe something in the mem2mem driver isn't defaulting encoding. The call path is (v4l2_m2m_streamon -> device_run -> ipu_image_convert_queue -> convert_start -> ipu_ic_task_init_rsc -> init_csc). Looking at v7 of the mem2mem driver, it will set ycbcr_enc at the output side to V4L2_YCBCR_ENC_DEFAULT if colorspace is default. So colorspace will need to be set to something non-default in addition to setting ycbcr_enc, at the output side. I don't know whether gstreamer v4l2videoNconvertelement will do this, but you could hack the driver for now to get around it, and let Philipp know this may need a workaround in mem2mem for v8. Steve ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel