diff options
Diffstat (limited to 'drivers/staging/media')
56 files changed, 2367 insertions, 380 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index db5cf67047ad..b3620a8f2d9f 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -31,6 +31,8 @@ source "drivers/staging/media/mt9t031/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" +source "drivers/staging/media/sunxi/Kconfig" + source "drivers/staging/media/tegra-vde/Kconfig" source "drivers/staging/media/zoran/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 503fbe47fa58..42948f805548 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074/ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ +obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index a90b2eb112f9..874d290f9622 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -2304,9 +2304,9 @@ static int bcm2048_vidioc_querycap(struct file *file, void *priv, { struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file)); - strlcpy(capability->driver, BCM2048_DRIVER_NAME, + strscpy(capability->driver, BCM2048_DRIVER_NAME, sizeof(capability->driver)); - strlcpy(capability->card, BCM2048_DRIVER_CARD, + strscpy(capability->card, BCM2048_DRIVER_CARD, sizeof(capability->card)); snprintf(capability->bus_info, 32, "I2C: 0x%X", bdev->client->addr); capability->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index 95942768639c..dcfeac818451 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -695,21 +695,21 @@ static int ipipe_get_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) if (!gamma->bypass_r) { dev_err(dev, - "ipipe_get_gamma_params: table ptr empty for R\n"); + "%s: table ptr empty for R\n", __func__); return -EINVAL; } memcpy(gamma_param->table_r, gamma->table_r, (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); if (!gamma->bypass_g) { - dev_err(dev, "ipipe_get_gamma_params: table ptr empty for G\n"); + dev_err(dev, "%s: table ptr empty for G\n", __func__); return -EINVAL; } memcpy(gamma_param->table_g, gamma->table_g, (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); if (!gamma->bypass_b) { - dev_err(dev, "ipipe_get_gamma_params: table ptr empty for B\n"); + dev_err(dev, "%s: table ptr empty for B\n", __func__); return -EINVAL; } memcpy(gamma_param->table_b, gamma->table_b, @@ -1801,7 +1801,7 @@ vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) v4l2_subdev_init(sd, &ipipe_v4l2_ops); sd->internal_ops = &ipipe_v4l2_internal_ops; - strlcpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name)); + strscpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name)); sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ v4l2_set_subdevdata(sd, ipipe); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c index 11c9edfbdbe3..a53231b08d30 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -1020,7 +1020,7 @@ int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, v4l2_subdev_init(sd, &ipipeif_v4l2_ops); sd->internal_ops = &ipipeif_v4l2_internal_ops; - strlcpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name)); + strscpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name)); sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ v4l2_set_subdevdata(sd, ipipeif); diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 745e33fa6bea..39eb0819ab4e 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -2038,7 +2038,7 @@ int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) isif->video_out.ops = &isif_video_ops; v4l2_subdev_init(sd, &isif_v4l2_ops); sd->internal_ops = &isif_v4l2_internal_ops; - strlcpy(sd->name, "DAVINCI ISIF", sizeof(sd->name)); + strscpy(sd->name, "DAVINCI ISIF", sizeof(sd->name)); sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ v4l2_set_subdevdata(sd, isif); sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c index 2b797474a344..72bbbc34d18c 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -499,7 +499,7 @@ resizer_configure_in_continuous_mode(struct vpfe_resizer_device *resizer) configure_resizer_out_params(resizer, RSZ_A, &cont_config->output1, 1, 0); param->rsz_en[RSZ_B] = DISABLE; - param->oper_mode = RESIZER_MODE_CONTINIOUS; + param->oper_mode = RESIZER_MODE_CONTINUOUS; if (resizer->resizer_b.output == RESIZER_OUTPUT_MEMORY) { struct v4l2_mbus_framefmt *outformat2; @@ -1903,7 +1903,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, v4l2_subdev_init(sd, &resizer_v4l2_ops); sd->internal_ops = &resizer_v4l2_internal_ops; - strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name)); + strscpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name)); sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ v4l2_set_subdevdata(sd, vpfe_rsz); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1927,7 +1927,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, v4l2_subdev_init(sd, &resizer_v4l2_ops); sd->internal_ops = &resizer_v4l2_internal_ops; - strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name)); + strscpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name)); sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ v4l2_set_subdevdata(sd, vpfe_rsz); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1949,7 +1949,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, v4l2_subdev_init(sd, &resizer_v4l2_ops); sd->internal_ops = &resizer_v4l2_internal_ops; - strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name)); + strscpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name)); sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ v4l2_set_subdevdata(sd, vpfe_rsz); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h index 00e64b0d0295..cf560a33d862 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.h +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h @@ -23,7 +23,7 @@ #define _DAVINCI_VPFE_DM365_RESIZER_H enum resizer_oper_mode { - RESIZER_MODE_CONTINIOUS = 0, + RESIZER_MODE_CONTINUOUS = 0, RESIZER_MODE_ONE_SHOT = 1, }; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c index e55c815b9b65..bdf6ee5ad96c 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -639,7 +639,8 @@ static int vpfe_probe(struct platform_device *pdev) goto probe_disable_clock; vpfe_dev->media_dev.dev = vpfe_dev->pdev; - strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media"); + strscpy((char *)&vpfe_dev->media_dev.model, "davinci-media", + sizeof(vpfe_dev->media_dev.model)); ret = media_device_register(&vpfe_dev->media_dev); if (ret) { diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 1269a983455e..5e9769ea8a50 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -618,9 +618,9 @@ static int vpfe_querycap(struct file *file, void *priv, cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; - strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); - strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); - strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + strscpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strscpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); return 0; } @@ -1135,10 +1135,6 @@ static int vpfe_buffer_prepare(struct vb2_buffer *vb) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); - if (vb->state != VB2_BUF_STATE_ACTIVE && - vb->state != VB2_BUF_STATE_PREPARED) - return 0; - /* Initialize buffer */ vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage); if (vb2_plane_vaddr(vb, 0) && @@ -1429,7 +1425,8 @@ static int vpfe_qbuf(struct file *file, void *priv, return -EACCES; } - return vb2_qbuf(&video->buffer_queue, p); + return vb2_qbuf(&video->buffer_queue, + video->video_dev.v4l2_dev->mdev, p); } /* 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. diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 256039ce561e..b37e1186eb2f 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -73,8 +73,8 @@ static int vidioc_querycap(struct file *file, void *fh, { struct capture_priv *priv = video_drvdata(file); - strlcpy(cap->driver, "imx-media-capture", sizeof(cap->driver)); - strlcpy(cap->card, "imx-media-capture", sizeof(cap->card)); + strscpy(cap->driver, "imx-media-capture", sizeof(cap->driver)); + strscpy(cap->card, "imx-media-capture", sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", priv->src_sd->name); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index cd2c291e1e94..4223f8d418ae 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -124,7 +124,7 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev) static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep) { - return ep->bus_type != V4L2_MBUS_CSI2; + return ep->bus_type != V4L2_MBUS_CSI2_DPHY; } static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) @@ -165,6 +165,9 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, struct v4l2_subdev *sd; struct media_pad *pad; + if (!IS_ENABLED(CONFIG_OF)) + return -ENXIO; + if (!priv->src_sd) return -EPIPE; @@ -1050,7 +1053,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *sink_fmt) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep = {}; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; bool is_csi2; int ret; @@ -1164,7 +1167,7 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; int ret = 0; @@ -1403,7 +1406,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct imx_media_video_dev *vdev = priv->vdev; - struct v4l2_fwnode_endpoint upstream_ep; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; const struct imx_media_pixfmt *cc; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; @@ -1542,7 +1545,7 @@ static int csi_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; struct v4l2_mbus_framefmt *infmt; struct v4l2_rect *crop, *compose; int pad, ret; @@ -1780,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; @@ -1849,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; diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index b0be80f05767..4b344a4a3706 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -29,47 +29,14 @@ 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); } /* - * 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,43 @@ 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) { + if (np) + dev_dbg(imxmd->md.dev, "%s: already added %pOFn\n", + __func__, np); + else + dev_dbg(imxmd->md.dev, "%s: already added %s\n", + __func__, devname); + } + return ret; } - list_add_tail(&imxasd->list, &imxmd->asd_list); + imxasd = to_imx_media_asd(asd); - imxmd->subdev_notifier.num_subdevs++; + if (devname) + imxasd->pdev = pdev; - dev_dbg(imxmd->md.dev, "%s: added %s, match type %s\n", - __func__, np ? np->name : devname, np ? "FWNODE" : "DEVNAME"); + if (np) + dev_dbg(imxmd->md.dev, "%s: added %pOFn, match type FWNODE\n", + __func__, np); + else + dev_dbg(imxmd->md.dev, "%s: added %s, match type DEVNAME\n", + __func__, devname); -out: - mutex_unlock(&imxmd->mutex); - return ret; + return 0; } /* @@ -175,7 +138,7 @@ out: } /* - * 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 +147,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 +167,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; } @@ -391,7 +350,7 @@ static int imx_media_inherit_controls(struct imx_media_dev *imxmd, ret = v4l2_ctrl_add_handler(vfd->ctrl_handler, sd->ctrl_handler, - NULL); + NULL, true); if (ret) return ret; } @@ -487,10 +446,8 @@ static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; - struct imx_media_async_subdev *imxasd; - struct v4l2_async_subdev **subdevs; struct imx_media_dev *imxmd; - int num_subdevs, i, ret; + int ret; imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); if (!imxmd) @@ -498,14 +455,14 @@ static int imx_media_probe(struct platform_device *pdev) dev_set_drvdata(dev, imxmd); - strlcpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); + strscpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); imxmd->md.ops = &imx_media_md_ops; imxmd->md.dev = dev; mutex_init(&imxmd->mutex); imxmd->v4l2_dev.mdev = &imxmd->md; - strlcpy(imxmd->v4l2_dev.name, "imx-media", + strscpy(imxmd->v4l2_dev.name, "imx-media", sizeof(imxmd->v4l2_dev.name)); media_device_init(&imxmd->md); @@ -519,47 +476,34 @@ static int imx_media_probe(struct platform_device *pdev) dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); - INIT_LIST_HEAD(&imxmd->asd_list); INIT_LIST_HEAD(&imxmd->vdev_list); + v4l2_async_notifier_init(&imxmd->notifier); + ret = imx_media_add_of_subdevs(imxmd, node); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_of_subdevs failed with %d\n", ret); - goto unreg_dev; + goto notifier_cleanup; } ret = imx_media_add_internal_subdevs(imxmd); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_internal_subdevs failed with %d\n", ret); - goto unreg_dev; + goto notifier_cleanup; } - num_subdevs = imxmd->subdev_notifier.num_subdevs; - /* no subdevs? just bail */ - if (num_subdevs == 0) { + if (list_empty(&imxmd->notifier.asd_list)) { ret = -ENODEV; - goto unreg_dev; + goto notifier_cleanup; } - subdevs = devm_kcalloc(imxmd->md.dev, num_subdevs, sizeof(*subdevs), - GFP_KERNEL); - if (!subdevs) { - ret = -ENOMEM; - goto unreg_dev; - } - - i = 0; - list_for_each_entry(imxasd, &imxmd->asd_list, list) - 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.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); @@ -570,7 +514,8 @@ static int imx_media_probe(struct platform_device *pdev) del_int: imx_media_remove_internal_subdevs(imxmd); -unreg_dev: +notifier_cleanup: + v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); cleanup: media_device_cleanup(&imxmd->md); @@ -584,8 +529,9 @@ 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_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); media_device_unregister(&imxmd->md); media_device_cleanup(&imxmd->md); diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c index 6df189135db8..8cf773eef9da 100644 --- a/drivers/staging/media/imx/imx-media-fim.c +++ b/drivers/staging/media/imx/imx-media-fim.c @@ -463,7 +463,7 @@ int imx_media_fim_add_controls(struct imx_media_fim *fim) { /* add the FIM controls to the calling subdev ctrl handler */ return v4l2_ctrl_add_handler(fim->sd->ctrl_handler, - &fim->ctrl_handler, NULL); + &fim->ctrl_handler, NULL, false); } EXPORT_SYMBOL_GPL(imx_media_fim_add_controls); diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c index daf66c2d69ab..0fdc45dbfb76 100644 --- a/drivers/staging/media/imx/imx-media-internal-sd.c +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -350,8 +350,11 @@ remove: void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd) { struct imx_media_async_subdev *imxasd; + struct v4l2_async_subdev *asd; + + list_for_each_entry(asd, &imxmd->notifier.asd_list, asd_list) { + imxasd = to_imx_media_asd(asd); - list_for_each_entry(imxasd, &imxmd->asd_list, list) { if (!imxasd->pdev) continue; diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index acde372c6795..b2e840f96c50 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,74 +20,19 @@ #include <video/imx-ipu-v3.h> #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)) { - dev_dbg(imxmd->md.dev, "%s: %s not enabled\n", __func__, - sd_np->name); + 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; } - /* 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(imxmd, csi_np); of_node_put(csi_np); if (ret) return ret; diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 8aa13403b09d..0eaa353d5cb3 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -88,7 +88,7 @@ static const struct imx_media_pixfmt rgb_formats[] = { .cs = IPUV3_COLORSPACE_RGB, .bpp = 24, }, { - .fourcc = V4L2_PIX_FMT_RGB32, + .fourcc = V4L2_PIX_FMT_XRGB32, .codes = {MEDIA_BUS_FMT_ARGB8888_1X32}, .cs = IPUV3_COLORSPACE_RGB, .bpp = 32, @@ -212,7 +212,7 @@ static const struct imx_media_pixfmt ipu_yuv_formats[] = { static const struct imx_media_pixfmt ipu_rgb_formats[] = { { - .fourcc = V4L2_PIX_FMT_RGB32, + .fourcc = V4L2_PIX_FMT_XRGB32, .codes = {MEDIA_BUS_FMT_ARGB8888_1X32}, .cs = IPUV3_COLORSPACE_RGB, .bpp = 32, diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 57bd094cf765..bc7feb81937c 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -119,12 +119,11 @@ struct imx_media_internal_sd_platformdata { int ipu_id; }; - struct imx_media_async_subdev { + /* the base asd - must be first in this struct */ struct v4l2_async_subdev asd; /* the platform device of IPU-internal subdevs */ struct platform_device *pdev; - struct list_head list; }; static inline struct imx_media_async_subdev * @@ -149,8 +148,7 @@ struct imx_media_dev { struct ipu_soc *ipu[2]; /* for async subdev registration */ - struct list_head asd_list; - struct v4l2_async_notifier subdev_notifier; + struct v4l2_async_notifier notifier; }; enum codespace_sel { diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index ceeeb3069a02..6a1cee55a49b 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_DPHY) { 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; @@ -597,14 +596,10 @@ static int csi2_probe(struct platform_device *pdev) csi2->sd.dev = &pdev->dev; csi2->sd.owner = THIS_MODULE; csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - strcpy(csi2->sd.name, DEVICE_NAME); + strscpy(csi2->sd.name, DEVICE_NAME, sizeof(csi2->sd.name)); 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; diff --git a/drivers/staging/media/imx074/imx074.c b/drivers/staging/media/imx074/imx074.c index 77f1e0243d6e..1676c166dc83 100644 --- a/drivers/staging/media/imx074/imx074.c +++ b/drivers/staging/media/imx074/imx074.c @@ -223,7 +223,6 @@ static int imx074_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP: return 0; default: @@ -263,7 +262,7 @@ static int imx074_s_power(struct v4l2_subdev *sd, int on) static int imx074_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - cfg->type = V4L2_MBUS_CSI2; + cfg->type = V4L2_MBUS_CSI2_DPHY; cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; diff --git a/drivers/staging/media/mt9t031/mt9t031.c b/drivers/staging/media/mt9t031/mt9t031.c index 4802d30e47de..4ff179302b4f 100644 --- a/drivers/staging/media/mt9t031/mt9t031.c +++ b/drivers/staging/media/mt9t031/mt9t031.c @@ -330,7 +330,6 @@ static int mt9t031_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = MT9T031_COLUMN_SKIP; sel->r.top = MT9T031_ROW_SKIP; sel->r.width = MT9T031_MAX_WIDTH; diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig index dddd27335cb4..841cc0b3ce13 100644 --- a/drivers/staging/media/omap4iss/Kconfig +++ b/drivers/staging/media/omap4iss/Kconfig @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + config VIDEO_OMAP4 tristate "OMAP 4 Camera support" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && I2C diff --git a/drivers/staging/media/omap4iss/Makefile b/drivers/staging/media/omap4iss/Makefile index a716ce936cf6..e64d489a4a76 100644 --- a/drivers/staging/media/omap4iss/Makefile +++ b/drivers/staging/media/omap4iss/Makefile @@ -1,4 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# # Makefile for OMAP4 ISS driver +# omap4-iss-objs += \ iss.o iss_csi2.o iss_csiphy.o iss_ipipeif.o iss_ipipe.o iss_resizer.o iss_video.o diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index b1036baebb03..c8be1db532ab 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP4 ISS V4L2 Driver * * Copyright (C) 2012, Texas Instruments * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> @@ -989,7 +985,7 @@ static int iss_register_entities(struct iss_device *iss) int ret; iss->media_dev.dev = iss->dev; - strlcpy(iss->media_dev.model, "TI OMAP4 ISS", + strscpy(iss->media_dev.model, "TI OMAP4 ISS", sizeof(iss->media_dev.model)); iss->media_dev.hw_revision = iss->revision; iss->media_dev.ops = &iss_media_ops; diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index 760ee27da704..b88f9529683c 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver * * Copyright (C) 2012 Texas Instruments. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef _OMAP4_ISS_H_ diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index f6acc541e8a2..059cf5bd3c36 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP4 ISS V4L2 Driver - CSI PHY module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/delay.h> diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h index 24ab378d469f..3f7fd9cff41d 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.h +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver - CSI2 module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef OMAP4_ISS_CSI2_H diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c index 748607f8918f..96f2ce045138 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.c +++ b/drivers/staging/media/omap4iss/iss_csiphy.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP4 ISS V4L2 Driver - CSI PHY module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/delay.h> diff --git a/drivers/staging/media/omap4iss/iss_csiphy.h b/drivers/staging/media/omap4iss/iss_csiphy.h index a0f2d974daeb..44408e4fcf3b 100644 --- a/drivers/staging/media/omap4iss/iss_csiphy.h +++ b/drivers/staging/media/omap4iss/iss_csiphy.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver - CSI PHY module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef OMAP4_ISS_CSI_PHY_H diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index d86ef8a031f2..26be078b69f3 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/module.h> @@ -507,7 +503,7 @@ static int ipipe_init_entities(struct iss_ipipe_device *ipipe) v4l2_subdev_init(sd, &ipipe_v4l2_ops); sd->internal_ops = &ipipe_v4l2_internal_ops; - strlcpy(sd->name, "OMAP4 ISS ISP IPIPE", sizeof(sd->name)); + strscpy(sd->name, "OMAP4 ISS ISP IPIPE", sizeof(sd->name)); sd->grp_id = BIT(16); /* group ID for iss subdevs */ v4l2_set_subdevdata(sd, ipipe); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/staging/media/omap4iss/iss_ipipe.h b/drivers/staging/media/omap4iss/iss_ipipe.h index d5b441d9cb31..53b42aac1696 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.h +++ b/drivers/staging/media/omap4iss/iss_ipipe.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef OMAP4_ISS_IPIPE_H diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index cb88b2bd0d82..c2978d02e797 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP4 ISS V4L2 Driver - ISP IPIPEIF module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/module.h> @@ -738,7 +734,7 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif) v4l2_subdev_init(sd, &ipipeif_v4l2_ops); sd->internal_ops = &ipipeif_v4l2_internal_ops; - strlcpy(sd->name, "OMAP4 ISS ISP IPIPEIF", sizeof(sd->name)); + strscpy(sd->name, "OMAP4 ISS ISP IPIPEIF", sizeof(sd->name)); sd->grp_id = BIT(16); /* group ID for iss subdevs */ v4l2_set_subdevdata(sd, ipipeif); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.h b/drivers/staging/media/omap4iss/iss_ipipeif.h index bad32b1d6ad8..69792333a62e 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.h +++ b/drivers/staging/media/omap4iss/iss_ipipeif.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver - ISP IPIPEIF module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef OMAP4_ISS_IPIPEIF_H diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h index cb415e898aca..09a7375c89ac 100644 --- a/drivers/staging/media/omap4iss/iss_regs.h +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver - Register defines * * Copyright (C) 2012 Texas Instruments. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef _OMAP4_ISS_REGS_H_ diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 4bbfa20b3c38..3b6875cbca9b 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP4 ISS V4L2 Driver - ISP RESIZER module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/module.h> @@ -781,7 +777,7 @@ static int resizer_init_entities(struct iss_resizer_device *resizer) v4l2_subdev_init(sd, &resizer_v4l2_ops); sd->internal_ops = &resizer_v4l2_internal_ops; - strlcpy(sd->name, "OMAP4 ISS ISP resizer", sizeof(sd->name)); + strscpy(sd->name, "OMAP4 ISS ISP resizer", sizeof(sd->name)); sd->grp_id = BIT(16); /* group ID for iss subdevs */ v4l2_set_subdevdata(sd, resizer); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/staging/media/omap4iss/iss_resizer.h b/drivers/staging/media/omap4iss/iss_resizer.h index 8b7c5fe9ffed..cb937fccc21f 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.h +++ b/drivers/staging/media/omap4iss/iss_resizer.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver - ISP RESIZER module * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef OMAP4_ISS_RESIZER_H diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 16478fe9e3f8..c2c5a9cd8642 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI OMAP4 ISS V4L2 Driver - Generic video node * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> @@ -534,9 +530,9 @@ iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct iss_video *video = video_drvdata(file); - strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver)); - strlcpy(cap->card, video->video.name, sizeof(cap->card)); - strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); + strscpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, video->video.name, sizeof(cap->card)); + strscpy(cap->bus_info, "media", sizeof(cap->bus_info)); if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; @@ -573,7 +569,7 @@ iss_video_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) if (index == 0) { f->pixelformat = info->pixelformat; - strlcpy(f->description, info->description, + strscpy(f->description, info->description, sizeof(f->description)); return 0; } @@ -806,9 +802,10 @@ iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) static int iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) { + struct iss_video *video = video_drvdata(file); struct iss_video_fh *vfh = to_iss_video_fh(fh); - return vb2_qbuf(&vfh->queue, b); + return vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b); } static int @@ -1053,7 +1050,7 @@ iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) if (input->index > 0) return -EINVAL; - strlcpy(input->name, "camera", sizeof(input->name)); + strscpy(input->name, "camera", sizeof(input->name)); input->type = V4L2_INPUT_TYPE_CAMERA; return 0; diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index d7e05d04512c..f22489edb562 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * TI OMAP4 ISS V4L2 Driver - Generic video node * * Copyright (C) 2012 Texas Instruments, Inc. * * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef OMAP4_ISS_VIDEO_H diff --git a/drivers/staging/media/sunxi/Kconfig b/drivers/staging/media/sunxi/Kconfig new file mode 100644 index 000000000000..c78d92240ceb --- /dev/null +++ b/drivers/staging/media/sunxi/Kconfig @@ -0,0 +1,15 @@ +config VIDEO_SUNXI + bool "Allwinner sunXi family Video Devices" + depends on ARCH_SUNXI || COMPILE_TEST + help + If you have an Allwinner SoC based on the sunXi family, say Y. + + Note that this option doesn't include new drivers in the + kernel: saying N will just cause Kconfig to skip all the + questions about Allwinner media devices. + +if VIDEO_SUNXI + +source "drivers/staging/media/sunxi/cedrus/Kconfig" + +endif diff --git a/drivers/staging/media/sunxi/Makefile b/drivers/staging/media/sunxi/Makefile new file mode 100644 index 000000000000..cee2846c3ecf --- /dev/null +++ b/drivers/staging/media/sunxi/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += cedrus/ diff --git a/drivers/staging/media/sunxi/cedrus/Kconfig b/drivers/staging/media/sunxi/cedrus/Kconfig new file mode 100644 index 000000000000..a7a34e89c42d --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/Kconfig @@ -0,0 +1,14 @@ +config VIDEO_SUNXI_CEDRUS + tristate "Allwinner Cedrus VPU driver" + depends on VIDEO_DEV && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on HAS_DMA + depends on OF + select SUNXI_SRAM + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the VPU found in Allwinner SoCs, also known as the Cedar + video engine. + + To compile this driver as a module, choose M here: the module + will be called sunxi-cedrus. diff --git a/drivers/staging/media/sunxi/cedrus/Makefile b/drivers/staging/media/sunxi/cedrus/Makefile new file mode 100644 index 000000000000..e9dc68b7bcb6 --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi-cedrus.o + +sunxi-cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o cedrus_mpeg2.o diff --git a/drivers/staging/media/sunxi/cedrus/TODO b/drivers/staging/media/sunxi/cedrus/TODO new file mode 100644 index 000000000000..ec277ece47af --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/TODO @@ -0,0 +1,7 @@ +Before this stateless decoder driver can leave the staging area: +* The Request API needs to be stabilized; +* The codec-specific controls need to be thoroughly reviewed to ensure they + cover all intended uses cases; +* Userspace support for the Request API needs to be reviewed; +* Another stateless decoder driver should be submitted; +* At least one stateless encoder driver should be submitted. diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c new file mode 100644 index 000000000000..82558455384a --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-mem2mem.h> + +#include "cedrus.h" +#include "cedrus_video.h" +#include "cedrus_dec.h" +#include "cedrus_hw.h" + +static const struct cedrus_control cedrus_controls[] = { + { + .id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS, + .elem_size = sizeof(struct v4l2_ctrl_mpeg2_slice_params), + .codec = CEDRUS_CODEC_MPEG2, + .required = true, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION, + .elem_size = sizeof(struct v4l2_ctrl_mpeg2_quantization), + .codec = CEDRUS_CODEC_MPEG2, + .required = false, + }, +}; + +#define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls) + +void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id) +{ + unsigned int i; + + for (i = 0; ctx->ctrls[i]; i++) + if (ctx->ctrls[i]->id == id) + return ctx->ctrls[i]->p_cur.p; + + return NULL; +} + +static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->hdl; + struct v4l2_ctrl *ctrl; + unsigned int ctrl_size; + unsigned int i; + + v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT); + if (hdl->error) { + v4l2_err(&dev->v4l2_dev, + "Failed to initialize control handler\n"); + return hdl->error; + } + + ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1; + + ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL); + memset(ctx->ctrls, 0, ctrl_size); + + for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { + struct v4l2_ctrl_config cfg = { 0 }; + + cfg.elem_size = cedrus_controls[i].elem_size; + cfg.id = cedrus_controls[i].id; + + ctrl = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + if (hdl->error) { + v4l2_err(&dev->v4l2_dev, + "Failed to create new custom control\n"); + + v4l2_ctrl_handler_free(hdl); + kfree(ctx->ctrls); + return hdl->error; + } + + ctx->ctrls[i] = ctrl; + } + + ctx->fh.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + + return 0; +} + +static int cedrus_request_validate(struct media_request *req) +{ + struct media_request_object *obj; + struct v4l2_ctrl_handler *parent_hdl, *hdl; + struct cedrus_ctx *ctx = NULL; + struct v4l2_ctrl *ctrl_test; + unsigned int count; + unsigned int i; + + count = vb2_request_buffer_cnt(req); + if (!count) { + v4l2_info(&ctx->dev->v4l2_dev, + "No buffer was provided with the request\n"); + return -ENOENT; + } else if (count > 1) { + v4l2_info(&ctx->dev->v4l2_dev, + "More than one buffer was provided with the request\n"); + return -EINVAL; + } + + list_for_each_entry(obj, &req->objects, list) { + struct vb2_buffer *vb; + + if (vb2_request_object_is_buffer(obj)) { + vb = container_of(obj, struct vb2_buffer, req_obj); + ctx = vb2_get_drv_priv(vb->vb2_queue); + + break; + } + } + + if (!ctx) + return -ENOENT; + + parent_hdl = &ctx->hdl; + + hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl); + if (!hdl) { + v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n"); + return -ENOENT; + } + + for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { + if (cedrus_controls[i].codec != ctx->current_codec || + !cedrus_controls[i].required) + continue; + + ctrl_test = v4l2_ctrl_request_hdl_ctrl_find(hdl, + cedrus_controls[i].id); + if (!ctrl_test) { + v4l2_info(&ctx->dev->v4l2_dev, + "Missing required codec control\n"); + return -ENOENT; + } + } + + v4l2_ctrl_request_hdl_put(hdl); + + return vb2_request_validate(req); +} + +static int cedrus_open(struct file *file) +{ + struct cedrus_dev *dev = video_drvdata(file); + struct cedrus_ctx *ctx = NULL; + int ret; + + if (mutex_lock_interruptible(&dev->dev_mutex)) + return -ERESTARTSYS; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + mutex_unlock(&dev->dev_mutex); + return -ENOMEM; + } + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dev = dev; + + ret = cedrus_init_ctrls(dev, ctx); + if (ret) + goto err_free; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + &cedrus_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_ctrls; + } + + v4l2_fh_add(&ctx->fh); + + mutex_unlock(&dev->dev_mutex); + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(&ctx->hdl); +err_free: + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int cedrus_release(struct file *file) +{ + struct cedrus_dev *dev = video_drvdata(file); + struct cedrus_ctx *ctx = container_of(file->private_data, + struct cedrus_ctx, fh); + + mutex_lock(&dev->dev_mutex); + + v4l2_fh_del(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + v4l2_ctrl_handler_free(&ctx->hdl); + kfree(ctx->ctrls); + + v4l2_fh_exit(&ctx->fh); + + kfree(ctx); + + mutex_unlock(&dev->dev_mutex); + + return 0; +} + +static const struct v4l2_file_operations cedrus_fops = { + .owner = THIS_MODULE, + .open = cedrus_open, + .release = cedrus_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device cedrus_video_device = { + .name = CEDRUS_NAME, + .vfl_dir = VFL_DIR_M2M, + .fops = &cedrus_fops, + .ioctl_ops = &cedrus_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_m2m_ops cedrus_m2m_ops = { + .device_run = cedrus_device_run, +}; + +static const struct media_device_ops cedrus_m2m_media_ops = { + .req_validate = cedrus_request_validate, + .req_queue = vb2_m2m_request_queue, +}; + +static int cedrus_probe(struct platform_device *pdev) +{ + struct cedrus_dev *dev; + struct video_device *vfd; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->vfd = cedrus_video_device; + dev->dev = &pdev->dev; + dev->pdev = pdev; + + ret = cedrus_hw_probe(dev); + if (ret) { + dev_err(&pdev->dev, "Failed to probe hardware\n"); + return ret; + } + + dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; + + mutex_init(&dev->dev_mutex); + spin_lock_init(&dev->irq_lock); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register V4L2 device\n"); + return ret; + } + + vfd = &dev->vfd; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + snprintf(vfd->name, sizeof(vfd->name), "%s", cedrus_video_device.name); + video_set_drvdata(vfd, dev); + + dev->m2m_dev = v4l2_m2m_init(&cedrus_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, + "Failed to initialize V4L2 M2M device\n"); + ret = PTR_ERR(dev->m2m_dev); + + goto err_video; + } + + dev->mdev.dev = &pdev->dev; + strscpy(dev->mdev.model, CEDRUS_NAME, sizeof(dev->mdev.model)); + + media_device_init(&dev->mdev); + dev->mdev.ops = &cedrus_m2m_media_ops; + dev->v4l2_dev.mdev = &dev->mdev; + + ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, + MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "Failed to initialize V4L2 M2M media controller\n"); + goto err_m2m; + } + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto err_v4l2; + } + + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + + ret = media_device_register(&dev->mdev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register media device\n"); + goto err_m2m_mc; + } + + platform_set_drvdata(pdev, dev); + + return 0; + +err_m2m_mc: + v4l2_m2m_unregister_media_controller(dev->m2m_dev); +err_m2m: + v4l2_m2m_release(dev->m2m_dev); +err_video: + video_unregister_device(&dev->vfd); +err_v4l2: + v4l2_device_unregister(&dev->v4l2_dev); + + return ret; +} + +static int cedrus_remove(struct platform_device *pdev) +{ + struct cedrus_dev *dev = platform_get_drvdata(pdev); + + if (media_devnode_is_registered(dev->mdev.devnode)) { + media_device_unregister(&dev->mdev); + v4l2_m2m_unregister_media_controller(dev->m2m_dev); + media_device_cleanup(&dev->mdev); + } + + v4l2_m2m_release(dev->m2m_dev); + video_unregister_device(&dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + cedrus_hw_remove(dev); + + return 0; +} + +static const struct cedrus_variant sun4i_a10_cedrus_variant = { + /* No particular capability. */ +}; + +static const struct cedrus_variant sun5i_a13_cedrus_variant = { + /* No particular capability. */ +}; + +static const struct cedrus_variant sun7i_a20_cedrus_variant = { + /* No particular capability. */ +}; + +static const struct cedrus_variant sun8i_a33_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_UNTILED, +}; + +static const struct cedrus_variant sun8i_h3_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_UNTILED, +}; + +static const struct of_device_id cedrus_dt_match[] = { + { + .compatible = "allwinner,sun4i-a10-video-engine", + .data = &sun4i_a10_cedrus_variant, + }, + { + .compatible = "allwinner,sun5i-a13-video-engine", + .data = &sun5i_a13_cedrus_variant, + }, + { + .compatible = "allwinner,sun7i-a20-video-engine", + .data = &sun7i_a20_cedrus_variant, + }, + { + .compatible = "allwinner,sun8i-a33-video-engine", + .data = &sun8i_a33_cedrus_variant, + }, + { + .compatible = "allwinner,sun8i-h3-video-engine", + .data = &sun8i_h3_cedrus_variant, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cedrus_dt_match); + +static struct platform_driver cedrus_driver = { + .probe = cedrus_probe, + .remove = cedrus_remove, + .driver = { + .name = CEDRUS_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cedrus_dt_match), + }, +}; +module_platform_driver(cedrus_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>"); +MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); +MODULE_DESCRIPTION("Cedrus VPU driver"); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h new file mode 100644 index 000000000000..3f61248c57ac --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#ifndef _CEDRUS_H_ +#define _CEDRUS_H_ + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> + +#include <linux/platform_device.h> + +#define CEDRUS_NAME "cedrus" + +#define CEDRUS_CAPABILITY_UNTILED BIT(0) + +enum cedrus_codec { + CEDRUS_CODEC_MPEG2, + + CEDRUS_CODEC_LAST, +}; + +enum cedrus_irq_status { + CEDRUS_IRQ_NONE, + CEDRUS_IRQ_ERROR, + CEDRUS_IRQ_OK, +}; + +struct cedrus_control { + u32 id; + u32 elem_size; + enum cedrus_codec codec; + unsigned char required:1; +}; + +struct cedrus_mpeg2_run { + const struct v4l2_ctrl_mpeg2_slice_params *slice_params; + const struct v4l2_ctrl_mpeg2_quantization *quantization; +}; + +struct cedrus_run { + struct vb2_v4l2_buffer *src; + struct vb2_v4l2_buffer *dst; + + union { + struct cedrus_mpeg2_run mpeg2; + }; +}; + +struct cedrus_buffer { + struct v4l2_m2m_buffer m2m_buf; +}; + +struct cedrus_ctx { + struct v4l2_fh fh; + struct cedrus_dev *dev; + + struct v4l2_pix_format src_fmt; + struct v4l2_pix_format dst_fmt; + enum cedrus_codec current_codec; + + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl **ctrls; + + struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME]; +}; + +struct cedrus_dec_ops { + void (*irq_clear)(struct cedrus_ctx *ctx); + void (*irq_disable)(struct cedrus_ctx *ctx); + enum cedrus_irq_status (*irq_status)(struct cedrus_ctx *ctx); + void (*setup)(struct cedrus_ctx *ctx, struct cedrus_run *run); + int (*start)(struct cedrus_ctx *ctx); + void (*stop)(struct cedrus_ctx *ctx); + void (*trigger)(struct cedrus_ctx *ctx); +}; + +struct cedrus_variant { + unsigned int capabilities; +}; + +struct cedrus_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd; + struct media_device mdev; + struct media_pad pad[2]; + struct platform_device *pdev; + struct device *dev; + struct v4l2_m2m_dev *m2m_dev; + struct cedrus_dec_ops *dec_ops[CEDRUS_CODEC_LAST]; + + /* Device file mutex */ + struct mutex dev_mutex; + /* Interrupt spinlock */ + spinlock_t irq_lock; + + void __iomem *base; + + struct clk *mod_clk; + struct clk *ahb_clk; + struct clk *ram_clk; + + struct reset_control *rstc; + + unsigned int capabilities; +}; + +extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2; + +static inline void cedrus_write(struct cedrus_dev *dev, u32 reg, u32 val) +{ + writel(val, dev->base + reg); +} + +static inline u32 cedrus_read(struct cedrus_dev *dev, u32 reg) +{ + return readl(dev->base + reg); +} + +static inline dma_addr_t cedrus_buf_addr(struct vb2_buffer *buf, + struct v4l2_pix_format *pix_fmt, + unsigned int plane) +{ + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(buf, 0); + + return addr + (pix_fmt ? (dma_addr_t)pix_fmt->bytesperline * + pix_fmt->height * plane : 0); +} + +static inline dma_addr_t cedrus_dst_buf_addr(struct cedrus_ctx *ctx, + unsigned int index, + unsigned int plane) +{ + struct vb2_buffer *buf = ctx->dst_bufs[index]; + + return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; +} + +static inline struct cedrus_buffer * +vb2_v4l2_to_cedrus_buffer(const struct vb2_v4l2_buffer *p) +{ + return container_of(p, struct cedrus_buffer, m2m_buf.vb); +} + +static inline struct cedrus_buffer * +vb2_to_cedrus_buffer(const struct vb2_buffer *p) +{ + return vb2_v4l2_to_cedrus_buffer(to_vb2_v4l2_buffer(p)); +} + +void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id); + +#endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c new file mode 100644 index 000000000000..e40180a33951 --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> + +#include "cedrus.h" +#include "cedrus_dec.h" +#include "cedrus_hw.h" + +void cedrus_device_run(void *priv) +{ + struct cedrus_ctx *ctx = priv; + struct cedrus_dev *dev = ctx->dev; + struct cedrus_run run = { 0 }; + struct media_request *src_req; + unsigned long flags; + + run.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + run.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + /* Apply request(s) controls if needed. */ + src_req = run.src->vb2_buf.req_obj.req; + + if (src_req) + v4l2_ctrl_request_setup(src_req, &ctx->hdl); + + spin_lock_irqsave(&ctx->dev->irq_lock, flags); + + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_MPEG2_SLICE: + run.mpeg2.slice_params = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS); + run.mpeg2.quantization = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION); + break; + + default: + break; + } + + dev->dec_ops[ctx->current_codec]->setup(ctx, &run); + + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); + + /* Complete request(s) controls if needed. */ + + if (src_req) + v4l2_ctrl_request_complete(src_req, &ctx->hdl); + + spin_lock_irqsave(&ctx->dev->irq_lock, flags); + + dev->dec_ops[ctx->current_codec]->trigger(ctx); + + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); +} diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.h b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h new file mode 100644 index 000000000000..4f423d3a1cad --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#ifndef _CEDRUS_DEC_H_ +#define _CEDRUS_DEC_H_ + +extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; + +void cedrus_device_work(struct work_struct *work); +void cedrus_device_run(void *priv); + +int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +#endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c new file mode 100644 index 000000000000..32adbcbe6175 --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#include <linux/platform_device.h> +#include <linux/of_reserved_mem.h> +#include <linux/of_device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/soc/sunxi/sunxi_sram.h> + +#include <media/videobuf2-core.h> +#include <media/v4l2-mem2mem.h> + +#include "cedrus.h" +#include "cedrus_hw.h" +#include "cedrus_regs.h" + +int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec) +{ + u32 reg = 0; + + /* + * FIXME: This is only valid on 32-bits DDR's, we should test + * it on the A13/A33. + */ + reg |= VE_MODE_REC_WR_MODE_2MB; + reg |= VE_MODE_DDR_MODE_BW_128; + + switch (codec) { + case CEDRUS_CODEC_MPEG2: + reg |= VE_MODE_DEC_MPEG; + break; + + default: + return -EINVAL; + } + + cedrus_write(dev, VE_MODE, reg); + + return 0; +} + +void cedrus_engine_disable(struct cedrus_dev *dev) +{ + cedrus_write(dev, VE_MODE, VE_MODE_DISABLED); +} + +void cedrus_dst_format_set(struct cedrus_dev *dev, + struct v4l2_pix_format *fmt) +{ + unsigned int width = fmt->width; + unsigned int height = fmt->height; + u32 chroma_size; + u32 reg; + + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + chroma_size = ALIGN(width, 16) * ALIGN(height, 16) / 2; + + reg = VE_PRIMARY_OUT_FMT_NV12; + cedrus_write(dev, VE_PRIMARY_OUT_FMT, reg); + + reg = VE_CHROMA_BUF_LEN_SDRT(chroma_size / 2); + cedrus_write(dev, VE_CHROMA_BUF_LEN, reg); + + reg = chroma_size / 2; + cedrus_write(dev, VE_PRIMARY_CHROMA_BUF_LEN, reg); + + reg = VE_PRIMARY_FB_LINE_STRIDE_LUMA(ALIGN(width, 16)) | + VE_PRIMARY_FB_LINE_STRIDE_CHROMA(ALIGN(width, 16) / 2); + cedrus_write(dev, VE_PRIMARY_FB_LINE_STRIDE, reg); + + break; + case V4L2_PIX_FMT_SUNXI_TILED_NV12: + default: + reg = VE_PRIMARY_OUT_FMT_TILED_32_NV12; + cedrus_write(dev, VE_PRIMARY_OUT_FMT, reg); + + reg = VE_SECONDARY_OUT_FMT_TILED_32_NV12; + cedrus_write(dev, VE_CHROMA_BUF_LEN, reg); + + break; + } +} + +static irqreturn_t cedrus_bh(int irq, void *data) +{ + struct cedrus_dev *dev = data; + struct cedrus_ctx *ctx; + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + return IRQ_HANDLED; + } + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + + return IRQ_HANDLED; +} + +static irqreturn_t cedrus_irq(int irq, void *data) +{ + struct cedrus_dev *dev = data; + struct cedrus_ctx *ctx; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + enum vb2_buffer_state state; + enum cedrus_irq_status status; + unsigned long flags; + + spin_lock_irqsave(&dev->irq_lock, flags); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + spin_unlock_irqrestore(&dev->irq_lock, flags); + + return IRQ_NONE; + } + + status = dev->dec_ops[ctx->current_codec]->irq_status(ctx); + if (status == CEDRUS_IRQ_NONE) { + spin_unlock_irqrestore(&dev->irq_lock, flags); + return IRQ_NONE; + } + + dev->dec_ops[ctx->current_codec]->irq_disable(ctx); + dev->dec_ops[ctx->current_codec]->irq_clear(ctx); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (!src_buf || !dst_buf) { + v4l2_err(&dev->v4l2_dev, + "Missing source and/or destination buffers\n"); + spin_unlock_irqrestore(&dev->irq_lock, flags); + + return IRQ_HANDLED; + } + + if (status == CEDRUS_IRQ_ERROR) + state = VB2_BUF_STATE_ERROR; + else + state = VB2_BUF_STATE_DONE; + + v4l2_m2m_buf_done(src_buf, state); + v4l2_m2m_buf_done(dst_buf, state); + + spin_unlock_irqrestore(&dev->irq_lock, flags); + + return IRQ_WAKE_THREAD; +} + +int cedrus_hw_probe(struct cedrus_dev *dev) +{ + const struct cedrus_variant *variant; + struct resource *res; + int irq_dec; + int ret; + + variant = of_device_get_match_data(dev->dev); + if (!variant) + return -EINVAL; + + dev->capabilities = variant->capabilities; + + irq_dec = platform_get_irq(dev->pdev, 0); + if (irq_dec <= 0) { + v4l2_err(&dev->v4l2_dev, "Failed to get IRQ\n"); + + return irq_dec; + } + ret = devm_request_threaded_irq(dev->dev, irq_dec, cedrus_irq, + cedrus_bh, 0, dev_name(dev->dev), + dev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to request IRQ\n"); + + return ret; + } + + /* + * The VPU is only able to handle bus addresses so we have to subtract + * the RAM offset to the physcal addresses. + * + * This information will eventually be obtained from device-tree. + */ + +#ifdef PHYS_PFN_OFFSET + dev->dev->dma_pfn_offset = PHYS_PFN_OFFSET; +#endif + + ret = of_reserved_mem_device_init(dev->dev); + if (ret && ret != -ENODEV) { + v4l2_err(&dev->v4l2_dev, "Failed to reserve memory\n"); + + return ret; + } + + ret = sunxi_sram_claim(dev->dev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to claim SRAM\n"); + + goto err_mem; + } + + dev->ahb_clk = devm_clk_get(dev->dev, "ahb"); + if (IS_ERR(dev->ahb_clk)) { + v4l2_err(&dev->v4l2_dev, "Failed to get AHB clock\n"); + + ret = PTR_ERR(dev->ahb_clk); + goto err_sram; + } + + dev->mod_clk = devm_clk_get(dev->dev, "mod"); + if (IS_ERR(dev->mod_clk)) { + v4l2_err(&dev->v4l2_dev, "Failed to get MOD clock\n"); + + ret = PTR_ERR(dev->mod_clk); + goto err_sram; + } + + dev->ram_clk = devm_clk_get(dev->dev, "ram"); + if (IS_ERR(dev->ram_clk)) { + v4l2_err(&dev->v4l2_dev, "Failed to get RAM clock\n"); + + ret = PTR_ERR(dev->ram_clk); + goto err_sram; + } + + dev->rstc = devm_reset_control_get(dev->dev, NULL); + if (IS_ERR(dev->rstc)) { + v4l2_err(&dev->v4l2_dev, "Failed to get reset control\n"); + + ret = PTR_ERR(dev->rstc); + goto err_sram; + } + + res = platform_get_resource(dev->pdev, IORESOURCE_MEM, 0); + dev->base = devm_ioremap_resource(dev->dev, res); + if (!dev->base) { + v4l2_err(&dev->v4l2_dev, "Failed to map registers\n"); + + ret = -ENOMEM; + goto err_sram; + } + + ret = clk_set_rate(dev->mod_clk, CEDRUS_CLOCK_RATE_DEFAULT); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to set clock rate\n"); + + goto err_sram; + } + + ret = clk_prepare_enable(dev->ahb_clk); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to enable AHB clock\n"); + + goto err_sram; + } + + ret = clk_prepare_enable(dev->mod_clk); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to enable MOD clock\n"); + + goto err_ahb_clk; + } + + ret = clk_prepare_enable(dev->ram_clk); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to enable RAM clock\n"); + + goto err_mod_clk; + } + + ret = reset_control_reset(dev->rstc); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to apply reset\n"); + + goto err_ram_clk; + } + + return 0; + +err_ram_clk: + clk_disable_unprepare(dev->ram_clk); +err_mod_clk: + clk_disable_unprepare(dev->mod_clk); +err_ahb_clk: + clk_disable_unprepare(dev->ahb_clk); +err_sram: + sunxi_sram_release(dev->dev); +err_mem: + of_reserved_mem_device_release(dev->dev); + + return ret; +} + +void cedrus_hw_remove(struct cedrus_dev *dev) +{ + reset_control_assert(dev->rstc); + + clk_disable_unprepare(dev->ram_clk); + clk_disable_unprepare(dev->mod_clk); + clk_disable_unprepare(dev->ahb_clk); + + sunxi_sram_release(dev->dev); + + of_reserved_mem_device_release(dev->dev); +} diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h new file mode 100644 index 000000000000..b43c77d54b95 --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#ifndef _CEDRUS_HW_H_ +#define _CEDRUS_HW_H_ + +#define CEDRUS_CLOCK_RATE_DEFAULT 320000000 + +int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec); +void cedrus_engine_disable(struct cedrus_dev *dev); + +void cedrus_dst_format_set(struct cedrus_dev *dev, + struct v4l2_pix_format *fmt); + +int cedrus_hw_probe(struct cedrus_dev *dev); +void cedrus_hw_remove(struct cedrus_dev *dev); + +#endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c new file mode 100644 index 000000000000..9abd39cae38c --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + */ + +#include <media/videobuf2-dma-contig.h> + +#include "cedrus.h" +#include "cedrus_hw.h" +#include "cedrus_regs.h" + +/* Default MPEG-2 quantization coefficients, from the specification. */ + +static const u8 intra_quantization_matrix_default[64] = { + 8, 16, 16, 19, 16, 19, 22, 22, + 22, 22, 22, 22, 26, 24, 26, 27, + 27, 27, 26, 26, 26, 26, 27, 27, + 27, 29, 29, 29, 34, 34, 34, 29, + 29, 29, 27, 27, 29, 29, 32, 32, + 34, 34, 37, 38, 37, 35, 35, 34, + 35, 38, 38, 40, 40, 40, 48, 48, + 46, 46, 56, 56, 58, 69, 69, 83 +}; + +static const u8 non_intra_quantization_matrix_default[64] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16 +}; + +static enum cedrus_irq_status cedrus_mpeg2_irq_status(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + u32 reg; + + reg = cedrus_read(dev, VE_DEC_MPEG_STATUS); + reg &= VE_DEC_MPEG_STATUS_CHECK_MASK; + + if (!reg) + return CEDRUS_IRQ_NONE; + + if (reg & VE_DEC_MPEG_STATUS_CHECK_ERROR || + !(reg & VE_DEC_MPEG_STATUS_SUCCESS)) + return CEDRUS_IRQ_ERROR; + + return CEDRUS_IRQ_OK; +} + +static void cedrus_mpeg2_irq_clear(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + + cedrus_write(dev, VE_DEC_MPEG_STATUS, VE_DEC_MPEG_STATUS_CHECK_MASK); +} + +static void cedrus_mpeg2_irq_disable(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + u32 reg = cedrus_read(dev, VE_DEC_MPEG_CTRL); + + reg &= ~VE_DEC_MPEG_CTRL_IRQ_MASK; + + cedrus_write(dev, VE_DEC_MPEG_CTRL, reg); +} + +static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) +{ + const struct v4l2_ctrl_mpeg2_slice_params *slice_params; + const struct v4l2_mpeg2_sequence *sequence; + const struct v4l2_mpeg2_picture *picture; + const struct v4l2_ctrl_mpeg2_quantization *quantization; + dma_addr_t src_buf_addr, dst_luma_addr, dst_chroma_addr; + dma_addr_t fwd_luma_addr, fwd_chroma_addr; + dma_addr_t bwd_luma_addr, bwd_chroma_addr; + struct cedrus_dev *dev = ctx->dev; + const u8 *matrix; + unsigned int i; + u32 reg; + + slice_params = run->mpeg2.slice_params; + sequence = &slice_params->sequence; + picture = &slice_params->picture; + + quantization = run->mpeg2.quantization; + + /* Activate MPEG engine. */ + cedrus_engine_enable(dev, CEDRUS_CODEC_MPEG2); + + /* Set intra quantization matrix. */ + + if (quantization && quantization->load_intra_quantiser_matrix) + matrix = quantization->intra_quantiser_matrix; + else + matrix = intra_quantization_matrix_default; + + for (i = 0; i < 64; i++) { + reg = VE_DEC_MPEG_IQMINPUT_WEIGHT(i, matrix[i]); + reg |= VE_DEC_MPEG_IQMINPUT_FLAG_INTRA; + + cedrus_write(dev, VE_DEC_MPEG_IQMINPUT, reg); + } + + /* Set non-intra quantization matrix. */ + + if (quantization && quantization->load_non_intra_quantiser_matrix) + matrix = quantization->non_intra_quantiser_matrix; + else + matrix = non_intra_quantization_matrix_default; + + for (i = 0; i < 64; i++) { + reg = VE_DEC_MPEG_IQMINPUT_WEIGHT(i, matrix[i]); + reg |= VE_DEC_MPEG_IQMINPUT_FLAG_NON_INTRA; + + cedrus_write(dev, VE_DEC_MPEG_IQMINPUT, reg); + } + + /* Set MPEG picture header. */ + + reg = VE_DEC_MPEG_MP12HDR_SLICE_TYPE(picture->picture_coding_type); + reg |= VE_DEC_MPEG_MP12HDR_F_CODE(0, 0, picture->f_code[0][0]); + reg |= VE_DEC_MPEG_MP12HDR_F_CODE(0, 1, picture->f_code[0][1]); + reg |= VE_DEC_MPEG_MP12HDR_F_CODE(1, 0, picture->f_code[1][0]); + reg |= VE_DEC_MPEG_MP12HDR_F_CODE(1, 1, picture->f_code[1][1]); + reg |= VE_DEC_MPEG_MP12HDR_INTRA_DC_PRECISION(picture->intra_dc_precision); + reg |= VE_DEC_MPEG_MP12HDR_INTRA_PICTURE_STRUCTURE(picture->picture_structure); + reg |= VE_DEC_MPEG_MP12HDR_TOP_FIELD_FIRST(picture->top_field_first); + reg |= VE_DEC_MPEG_MP12HDR_FRAME_PRED_FRAME_DCT(picture->frame_pred_frame_dct); + reg |= VE_DEC_MPEG_MP12HDR_CONCEALMENT_MOTION_VECTORS(picture->concealment_motion_vectors); + reg |= VE_DEC_MPEG_MP12HDR_Q_SCALE_TYPE(picture->q_scale_type); + reg |= VE_DEC_MPEG_MP12HDR_INTRA_VLC_FORMAT(picture->intra_vlc_format); + reg |= VE_DEC_MPEG_MP12HDR_ALTERNATE_SCAN(picture->alternate_scan); + reg |= VE_DEC_MPEG_MP12HDR_FULL_PEL_FORWARD_VECTOR(0); + reg |= VE_DEC_MPEG_MP12HDR_FULL_PEL_BACKWARD_VECTOR(0); + + cedrus_write(dev, VE_DEC_MPEG_MP12HDR, reg); + + /* Set frame dimensions. */ + + reg = VE_DEC_MPEG_PICCODEDSIZE_WIDTH(sequence->horizontal_size); + reg |= VE_DEC_MPEG_PICCODEDSIZE_HEIGHT(sequence->vertical_size); + + cedrus_write(dev, VE_DEC_MPEG_PICCODEDSIZE, reg); + + reg = VE_DEC_MPEG_PICBOUNDSIZE_WIDTH(ctx->src_fmt.width); + reg |= VE_DEC_MPEG_PICBOUNDSIZE_HEIGHT(ctx->src_fmt.height); + + cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); + + /* Forward and backward prediction reference buffers. */ + + fwd_luma_addr = cedrus_dst_buf_addr(ctx, + slice_params->forward_ref_index, + 0); + fwd_chroma_addr = cedrus_dst_buf_addr(ctx, + slice_params->forward_ref_index, + 1); + + cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); + cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); + + bwd_luma_addr = cedrus_dst_buf_addr(ctx, + slice_params->backward_ref_index, + 0); + bwd_chroma_addr = cedrus_dst_buf_addr(ctx, + slice_params->backward_ref_index, + 1); + + cedrus_write(dev, VE_DEC_MPEG_BWD_REF_LUMA_ADDR, bwd_luma_addr); + cedrus_write(dev, VE_DEC_MPEG_BWD_REF_CHROMA_ADDR, bwd_chroma_addr); + + /* Destination luma and chroma buffers. */ + + dst_luma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 0); + dst_chroma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 1); + + cedrus_write(dev, VE_DEC_MPEG_REC_LUMA, dst_luma_addr); + cedrus_write(dev, VE_DEC_MPEG_REC_CHROMA, dst_chroma_addr); + + /* Source offset and length in bits. */ + + cedrus_write(dev, VE_DEC_MPEG_VLD_OFFSET, + slice_params->data_bit_offset); + + reg = slice_params->bit_size - slice_params->data_bit_offset; + cedrus_write(dev, VE_DEC_MPEG_VLD_LEN, reg); + + /* Source beginning and end addresses. */ + + src_buf_addr = vb2_dma_contig_plane_dma_addr(&run->src->vb2_buf, 0); + + reg = VE_DEC_MPEG_VLD_ADDR_BASE(src_buf_addr); + reg |= VE_DEC_MPEG_VLD_ADDR_VALID_PIC_DATA; + reg |= VE_DEC_MPEG_VLD_ADDR_LAST_PIC_DATA; + reg |= VE_DEC_MPEG_VLD_ADDR_FIRST_PIC_DATA; + + cedrus_write(dev, VE_DEC_MPEG_VLD_ADDR, reg); + + reg = src_buf_addr + DIV_ROUND_UP(slice_params->bit_size, 8); + cedrus_write(dev, VE_DEC_MPEG_VLD_END_ADDR, reg); + + /* Macroblock address: start at the beginning. */ + reg = VE_DEC_MPEG_MBADDR_Y(0) | VE_DEC_MPEG_MBADDR_X(0); + cedrus_write(dev, VE_DEC_MPEG_MBADDR, reg); + + /* Clear previous errors. */ + cedrus_write(dev, VE_DEC_MPEG_ERROR, 0); + + /* Clear correct macroblocks register. */ + cedrus_write(dev, VE_DEC_MPEG_CRTMBADDR, 0); + + /* Enable appropriate interruptions and components. */ + + reg = VE_DEC_MPEG_CTRL_IRQ_MASK | VE_DEC_MPEG_CTRL_MC_NO_WRITEBACK | + VE_DEC_MPEG_CTRL_MC_CACHE_EN; + + cedrus_write(dev, VE_DEC_MPEG_CTRL, reg); +} + +static void cedrus_mpeg2_trigger(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + u32 reg; + + /* Trigger MPEG engine. */ + reg = VE_DEC_MPEG_TRIGGER_HW_MPEG_VLD | VE_DEC_MPEG_TRIGGER_MPEG2 | + VE_DEC_MPEG_TRIGGER_MB_BOUNDARY; + + cedrus_write(dev, VE_DEC_MPEG_TRIGGER, reg); +} + +struct cedrus_dec_ops cedrus_dec_ops_mpeg2 = { + .irq_clear = cedrus_mpeg2_irq_clear, + .irq_disable = cedrus_mpeg2_irq_disable, + .irq_status = cedrus_mpeg2_irq_status, + .setup = cedrus_mpeg2_setup, + .trigger = cedrus_mpeg2_trigger, +}; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h new file mode 100644 index 000000000000..de2d6b6f64bf --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cedrus VPU driver + * + * Copyright (c) 2013-2016 Jens Kuske <jenskuske@gmail.com> + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _CEDRUS_REGS_H_ +#define _CEDRUS_REGS_H_ + +/* + * Common acronyms and contractions used in register descriptions: + * * VLD : Variable-Length Decoder + * * IQ: Inverse Quantization + * * IDCT: Inverse Discrete Cosine Transform + * * MC: Motion Compensation + * * STCD: Start Code Detect + * * SDRT: Scale Down and Rotate + */ + +#define VE_ENGINE_DEC_MPEG 0x100 +#define VE_ENGINE_DEC_H264 0x200 + +#define VE_MODE 0x00 + +#define VE_MODE_REC_WR_MODE_2MB (0x01 << 20) +#define VE_MODE_REC_WR_MODE_1MB (0x00 << 20) +#define VE_MODE_DDR_MODE_BW_128 (0x03 << 16) +#define VE_MODE_DDR_MODE_BW_256 (0x02 << 16) +#define VE_MODE_DISABLED (0x07 << 0) +#define VE_MODE_DEC_H265 (0x04 << 0) +#define VE_MODE_DEC_H264 (0x01 << 0) +#define VE_MODE_DEC_MPEG (0x00 << 0) + +#define VE_PRIMARY_CHROMA_BUF_LEN 0xc4 +#define VE_PRIMARY_FB_LINE_STRIDE 0xc8 + +#define VE_PRIMARY_FB_LINE_STRIDE_CHROMA(s) (((s) << 16) & GENMASK(31, 16)) +#define VE_PRIMARY_FB_LINE_STRIDE_LUMA(s) (((s) << 0) & GENMASK(15, 0)) + +#define VE_CHROMA_BUF_LEN 0xe8 + +#define VE_SECONDARY_OUT_FMT_TILED_32_NV12 (0x00 << 30) +#define VE_SECONDARY_OUT_FMT_EXT (0x01 << 30) +#define VE_SECONDARY_OUT_FMT_YU12 (0x02 << 30) +#define VE_SECONDARY_OUT_FMT_YV12 (0x03 << 30) +#define VE_CHROMA_BUF_LEN_SDRT(l) ((l) & GENMASK(27, 0)) + +#define VE_PRIMARY_OUT_FMT 0xec + +#define VE_PRIMARY_OUT_FMT_TILED_32_NV12 (0x00 << 4) +#define VE_PRIMARY_OUT_FMT_TILED_128_NV12 (0x01 << 4) +#define VE_PRIMARY_OUT_FMT_YU12 (0x02 << 4) +#define VE_PRIMARY_OUT_FMT_YV12 (0x03 << 4) +#define VE_PRIMARY_OUT_FMT_NV12 (0x04 << 4) +#define VE_PRIMARY_OUT_FMT_NV21 (0x05 << 4) +#define VE_SECONDARY_OUT_FMT_EXT_TILED_32_NV12 (0x00 << 0) +#define VE_SECONDARY_OUT_FMT_EXT_TILED_128_NV12 (0x01 << 0) +#define VE_SECONDARY_OUT_FMT_EXT_YU12 (0x02 << 0) +#define VE_SECONDARY_OUT_FMT_EXT_YV12 (0x03 << 0) +#define VE_SECONDARY_OUT_FMT_EXT_NV12 (0x04 << 0) +#define VE_SECONDARY_OUT_FMT_EXT_NV21 (0x05 << 0) + +#define VE_VERSION 0xf0 + +#define VE_VERSION_SHIFT 16 + +#define VE_DEC_MPEG_MP12HDR (VE_ENGINE_DEC_MPEG + 0x00) + +#define VE_DEC_MPEG_MP12HDR_SLICE_TYPE(t) (((t) << 28) & GENMASK(30, 28)) +#define VE_DEC_MPEG_MP12HDR_F_CODE_SHIFT(x, y) (24 - 4 * (y) - 8 * (x)) +#define VE_DEC_MPEG_MP12HDR_F_CODE(__x, __y, __v) \ + (((__v) & GENMASK(3, 0)) << VE_DEC_MPEG_MP12HDR_F_CODE_SHIFT(__x, __y)) + +#define VE_DEC_MPEG_MP12HDR_INTRA_DC_PRECISION(p) \ + (((p) << 10) & GENMASK(11, 10)) +#define VE_DEC_MPEG_MP12HDR_INTRA_PICTURE_STRUCTURE(s) \ + (((s) << 8) & GENMASK(9, 8)) +#define VE_DEC_MPEG_MP12HDR_TOP_FIELD_FIRST(v) \ + ((v) ? BIT(7) : 0) +#define VE_DEC_MPEG_MP12HDR_FRAME_PRED_FRAME_DCT(v) \ + ((v) ? BIT(6) : 0) +#define VE_DEC_MPEG_MP12HDR_CONCEALMENT_MOTION_VECTORS(v) \ + ((v) ? BIT(5) : 0) +#define VE_DEC_MPEG_MP12HDR_Q_SCALE_TYPE(v) \ + ((v) ? BIT(4) : 0) +#define VE_DEC_MPEG_MP12HDR_INTRA_VLC_FORMAT(v) \ + ((v) ? BIT(3) : 0) +#define VE_DEC_MPEG_MP12HDR_ALTERNATE_SCAN(v) \ + ((v) ? BIT(2) : 0) +#define VE_DEC_MPEG_MP12HDR_FULL_PEL_FORWARD_VECTOR(v) \ + ((v) ? BIT(1) : 0) +#define VE_DEC_MPEG_MP12HDR_FULL_PEL_BACKWARD_VECTOR(v) \ + ((v) ? BIT(0) : 0) + +#define VE_DEC_MPEG_PICCODEDSIZE (VE_ENGINE_DEC_MPEG + 0x08) + +#define VE_DEC_MPEG_PICCODEDSIZE_WIDTH(w) \ + ((DIV_ROUND_UP((w), 16) << 8) & GENMASK(15, 8)) +#define VE_DEC_MPEG_PICCODEDSIZE_HEIGHT(h) \ + ((DIV_ROUND_UP((h), 16) << 0) & GENMASK(7, 0)) + +#define VE_DEC_MPEG_PICBOUNDSIZE (VE_ENGINE_DEC_MPEG + 0x0c) + +#define VE_DEC_MPEG_PICBOUNDSIZE_WIDTH(w) (((w) << 16) & GENMASK(27, 16)) +#define VE_DEC_MPEG_PICBOUNDSIZE_HEIGHT(h) (((h) << 0) & GENMASK(11, 0)) + +#define VE_DEC_MPEG_MBADDR (VE_ENGINE_DEC_MPEG + 0x10) + +#define VE_DEC_MPEG_MBADDR_X(w) (((w) << 8) & GENMASK(15, 8)) +#define VE_DEC_MPEG_MBADDR_Y(h) (((h) << 0) & GENMASK(0, 7)) + +#define VE_DEC_MPEG_CTRL (VE_ENGINE_DEC_MPEG + 0x14) + +#define VE_DEC_MPEG_CTRL_MC_CACHE_EN BIT(31) +#define VE_DEC_MPEG_CTRL_SW_VLD BIT(27) +#define VE_DEC_MPEG_CTRL_SW_IQ_IS BIT(17) +#define VE_DEC_MPEG_CTRL_QP_AC_DC_OUT_EN BIT(14) +#define VE_DEC_MPEG_CTRL_ROTATE_SCALE_OUT_EN BIT(8) +#define VE_DEC_MPEG_CTRL_MC_NO_WRITEBACK BIT(7) +#define VE_DEC_MPEG_CTRL_ROTATE_IRQ_EN BIT(6) +#define VE_DEC_MPEG_CTRL_VLD_DATA_REQ_IRQ_EN BIT(5) +#define VE_DEC_MPEG_CTRL_ERROR_IRQ_EN BIT(4) +#define VE_DEC_MPEG_CTRL_FINISH_IRQ_EN BIT(3) +#define VE_DEC_MPEG_CTRL_IRQ_MASK \ + (VE_DEC_MPEG_CTRL_FINISH_IRQ_EN | VE_DEC_MPEG_CTRL_ERROR_IRQ_EN | \ + VE_DEC_MPEG_CTRL_VLD_DATA_REQ_IRQ_EN) + +#define VE_DEC_MPEG_TRIGGER (VE_ENGINE_DEC_MPEG + 0x18) + +#define VE_DEC_MPEG_TRIGGER_MB_BOUNDARY BIT(31) + +#define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_420 (0x00 << 27) +#define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_411 (0x01 << 27) +#define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422 (0x02 << 27) +#define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_444 (0x03 << 27) +#define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422T (0x04 << 27) + +#define VE_DEC_MPEG_TRIGGER_MPEG1 (0x01 << 24) +#define VE_DEC_MPEG_TRIGGER_MPEG2 (0x02 << 24) +#define VE_DEC_MPEG_TRIGGER_JPEG (0x03 << 24) +#define VE_DEC_MPEG_TRIGGER_MPEG4 (0x04 << 24) +#define VE_DEC_MPEG_TRIGGER_VP62 (0x05 << 24) + +#define VE_DEC_MPEG_TRIGGER_VP62_AC_GET_BITS BIT(7) + +#define VE_DEC_MPEG_TRIGGER_STCD_VC1 (0x02 << 4) +#define VE_DEC_MPEG_TRIGGER_STCD_MPEG2 (0x01 << 4) +#define VE_DEC_MPEG_TRIGGER_STCD_AVC (0x00 << 4) + +#define VE_DEC_MPEG_TRIGGER_HW_MPEG_VLD (0x0f << 0) +#define VE_DEC_MPEG_TRIGGER_HW_JPEG_VLD (0x0e << 0) +#define VE_DEC_MPEG_TRIGGER_HW_MB (0x0d << 0) +#define VE_DEC_MPEG_TRIGGER_HW_ROTATE (0x0c << 0) +#define VE_DEC_MPEG_TRIGGER_HW_VP6_VLD (0x0b << 0) +#define VE_DEC_MPEG_TRIGGER_HW_MAF (0x0a << 0) +#define VE_DEC_MPEG_TRIGGER_HW_STCD_END (0x09 << 0) +#define VE_DEC_MPEG_TRIGGER_HW_STCD_BEGIN (0x08 << 0) +#define VE_DEC_MPEG_TRIGGER_SW_MC (0x07 << 0) +#define VE_DEC_MPEG_TRIGGER_SW_IQ (0x06 << 0) +#define VE_DEC_MPEG_TRIGGER_SW_IDCT (0x05 << 0) +#define VE_DEC_MPEG_TRIGGER_SW_SCALE (0x04 << 0) +#define VE_DEC_MPEG_TRIGGER_SW_VP6 (0x03 << 0) +#define VE_DEC_MPEG_TRIGGER_SW_VP62_AC_GET_BITS (0x02 << 0) + +#define VE_DEC_MPEG_STATUS (VE_ENGINE_DEC_MPEG + 0x1c) + +#define VE_DEC_MPEG_STATUS_START_DETECT_BUSY BIT(27) +#define VE_DEC_MPEG_STATUS_VP6_BIT BIT(26) +#define VE_DEC_MPEG_STATUS_VP6_BIT_BUSY BIT(25) +#define VE_DEC_MPEG_STATUS_MAF_BUSY BIT(23) +#define VE_DEC_MPEG_STATUS_VP6_MVP_BUSY BIT(22) +#define VE_DEC_MPEG_STATUS_JPEG_BIT_END BIT(21) +#define VE_DEC_MPEG_STATUS_JPEG_RESTART_ERROR BIT(20) +#define VE_DEC_MPEG_STATUS_JPEG_MARKER BIT(19) +#define VE_DEC_MPEG_STATUS_ROTATE_BUSY BIT(18) +#define VE_DEC_MPEG_STATUS_DEBLOCKING_BUSY BIT(17) +#define VE_DEC_MPEG_STATUS_SCALE_DOWN_BUSY BIT(16) +#define VE_DEC_MPEG_STATUS_IQIS_BUF_EMPTY BIT(15) +#define VE_DEC_MPEG_STATUS_IDCT_BUF_EMPTY BIT(14) +#define VE_DEC_MPEG_STATUS_VE_BUSY BIT(13) +#define VE_DEC_MPEG_STATUS_MC_BUSY BIT(12) +#define VE_DEC_MPEG_STATUS_IDCT_BUSY BIT(11) +#define VE_DEC_MPEG_STATUS_IQIS_BUSY BIT(10) +#define VE_DEC_MPEG_STATUS_DCAC_BUSY BIT(9) +#define VE_DEC_MPEG_STATUS_VLD_BUSY BIT(8) +#define VE_DEC_MPEG_STATUS_ROTATE_SUCCESS BIT(3) +#define VE_DEC_MPEG_STATUS_VLD_DATA_REQ BIT(2) +#define VE_DEC_MPEG_STATUS_ERROR BIT(1) +#define VE_DEC_MPEG_STATUS_SUCCESS BIT(0) +#define VE_DEC_MPEG_STATUS_CHECK_MASK \ + (VE_DEC_MPEG_STATUS_SUCCESS | VE_DEC_MPEG_STATUS_ERROR | \ + VE_DEC_MPEG_STATUS_VLD_DATA_REQ) +#define VE_DEC_MPEG_STATUS_CHECK_ERROR \ + (VE_DEC_MPEG_STATUS_ERROR | VE_DEC_MPEG_STATUS_VLD_DATA_REQ) + +#define VE_DEC_MPEG_VLD_ADDR (VE_ENGINE_DEC_MPEG + 0x28) + +#define VE_DEC_MPEG_VLD_ADDR_FIRST_PIC_DATA BIT(30) +#define VE_DEC_MPEG_VLD_ADDR_LAST_PIC_DATA BIT(29) +#define VE_DEC_MPEG_VLD_ADDR_VALID_PIC_DATA BIT(28) +#define VE_DEC_MPEG_VLD_ADDR_BASE(a) \ + ({ \ + u32 _tmp = (a); \ + u32 _lo = _tmp & GENMASK(27, 4); \ + u32 _hi = (_tmp >> 28) & GENMASK(3, 0); \ + (_lo | _hi); \ + }) + +#define VE_DEC_MPEG_VLD_OFFSET (VE_ENGINE_DEC_MPEG + 0x2c) +#define VE_DEC_MPEG_VLD_LEN (VE_ENGINE_DEC_MPEG + 0x30) +#define VE_DEC_MPEG_VLD_END_ADDR (VE_ENGINE_DEC_MPEG + 0x34) + +#define VE_DEC_MPEG_REC_LUMA (VE_ENGINE_DEC_MPEG + 0x48) +#define VE_DEC_MPEG_REC_CHROMA (VE_ENGINE_DEC_MPEG + 0x4c) +#define VE_DEC_MPEG_FWD_REF_LUMA_ADDR (VE_ENGINE_DEC_MPEG + 0x50) +#define VE_DEC_MPEG_FWD_REF_CHROMA_ADDR (VE_ENGINE_DEC_MPEG + 0x54) +#define VE_DEC_MPEG_BWD_REF_LUMA_ADDR (VE_ENGINE_DEC_MPEG + 0x58) +#define VE_DEC_MPEG_BWD_REF_CHROMA_ADDR (VE_ENGINE_DEC_MPEG + 0x5c) + +#define VE_DEC_MPEG_IQMINPUT (VE_ENGINE_DEC_MPEG + 0x80) + +#define VE_DEC_MPEG_IQMINPUT_FLAG_INTRA (0x01 << 14) +#define VE_DEC_MPEG_IQMINPUT_FLAG_NON_INTRA (0x00 << 14) +#define VE_DEC_MPEG_IQMINPUT_WEIGHT(i, v) \ + (((v) & GENMASK(7, 0)) | (((i) << 8) & GENMASK(13, 8))) + +#define VE_DEC_MPEG_ERROR (VE_ENGINE_DEC_MPEG + 0xc4) +#define VE_DEC_MPEG_CRTMBADDR (VE_ENGINE_DEC_MPEG + 0xc8) +#define VE_DEC_MPEG_ROT_LUMA (VE_ENGINE_DEC_MPEG + 0xcc) +#define VE_DEC_MPEG_ROT_CHROMA (VE_ENGINE_DEC_MPEG + 0xd0) + +#endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c new file mode 100644 index 000000000000..5c5fce678b93 --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#include <media/videobuf2-dma-contig.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> + +#include "cedrus.h" +#include "cedrus_video.h" +#include "cedrus_dec.h" +#include "cedrus_hw.h" + +#define CEDRUS_DECODE_SRC BIT(0) +#define CEDRUS_DECODE_DST BIT(1) + +#define CEDRUS_MIN_WIDTH 16U +#define CEDRUS_MIN_HEIGHT 16U +#define CEDRUS_MAX_WIDTH 3840U +#define CEDRUS_MAX_HEIGHT 2160U + +static struct cedrus_format cedrus_formats[] = { + { + .pixelformat = V4L2_PIX_FMT_MPEG2_SLICE, + .directions = CEDRUS_DECODE_SRC, + }, + { + .pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12, + .directions = CEDRUS_DECODE_DST, + }, + { + .pixelformat = V4L2_PIX_FMT_NV12, + .directions = CEDRUS_DECODE_DST, + .capabilities = CEDRUS_CAPABILITY_UNTILED, + }, +}; + +#define CEDRUS_FORMATS_COUNT ARRAY_SIZE(cedrus_formats) + +static inline struct cedrus_ctx *cedrus_file2ctx(struct file *file) +{ + return container_of(file->private_data, struct cedrus_ctx, fh); +} + +static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions, + unsigned int capabilities) +{ + struct cedrus_format *fmt; + unsigned int i; + + for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) { + fmt = &cedrus_formats[i]; + + if (fmt->capabilities && (fmt->capabilities & capabilities) != + fmt->capabilities) + continue; + + if (fmt->pixelformat == pixelformat && + (fmt->directions & directions) != 0) + break; + } + + if (i == CEDRUS_FORMATS_COUNT) + return NULL; + + return &cedrus_formats[i]; +} + +static bool cedrus_check_format(u32 pixelformat, u32 directions, + unsigned int capabilities) +{ + return cedrus_find_format(pixelformat, directions, capabilities); +} + +static void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) +{ + unsigned int width = pix_fmt->width; + unsigned int height = pix_fmt->height; + unsigned int sizeimage = pix_fmt->sizeimage; + unsigned int bytesperline = pix_fmt->bytesperline; + + pix_fmt->field = V4L2_FIELD_NONE; + + /* Limit to hardware min/max. */ + width = clamp(width, CEDRUS_MIN_WIDTH, CEDRUS_MAX_WIDTH); + height = clamp(height, CEDRUS_MIN_HEIGHT, CEDRUS_MAX_HEIGHT); + + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_MPEG2_SLICE: + /* Zero bytes per line for encoded source. */ + bytesperline = 0; + + break; + + case V4L2_PIX_FMT_SUNXI_TILED_NV12: + /* 32-aligned stride. */ + bytesperline = ALIGN(width, 32); + + /* 32-aligned height. */ + height = ALIGN(height, 32); + + /* Luma plane size. */ + sizeimage = bytesperline * height; + + /* Chroma plane size. */ + sizeimage += bytesperline * height / 2; + + break; + + case V4L2_PIX_FMT_NV12: + /* 16-aligned stride. */ + bytesperline = ALIGN(width, 16); + + /* 16-aligned height. */ + height = ALIGN(height, 16); + + /* Luma plane size. */ + sizeimage = bytesperline * height; + + /* Chroma plane size. */ + sizeimage += bytesperline * height / 2; + + break; + } + + pix_fmt->width = width; + pix_fmt->height = height; + + pix_fmt->bytesperline = bytesperline; + pix_fmt->sizeimage = sizeimage; +} + +static int cedrus_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, CEDRUS_NAME, sizeof(cap->driver)); + strscpy(cap->card, CEDRUS_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", CEDRUS_NAME); + + return 0; +} + +static int cedrus_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, + u32 direction) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct cedrus_dev *dev = ctx->dev; + unsigned int capabilities = dev->capabilities; + struct cedrus_format *fmt; + unsigned int i, index; + + /* Index among formats that match the requested direction. */ + index = 0; + + for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) { + fmt = &cedrus_formats[i]; + + if (fmt->capabilities && (fmt->capabilities & capabilities) != + fmt->capabilities) + continue; + + if (!(cedrus_formats[i].directions & direction)) + continue; + + if (index == f->index) + break; + + index++; + } + + /* Matched format. */ + if (i < CEDRUS_FORMATS_COUNT) { + f->pixelformat = cedrus_formats[i].pixelformat; + + return 0; + } + + return -EINVAL; +} + +static int cedrus_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return cedrus_enum_fmt(file, f, CEDRUS_DECODE_DST); +} + +static int cedrus_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return cedrus_enum_fmt(file, f, CEDRUS_DECODE_SRC); +} + +static int cedrus_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + + /* Fall back to dummy default by lack of hardware configuration. */ + if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12; + cedrus_prepare_format(&f->fmt.pix); + + return 0; + } + + f->fmt.pix = ctx->dst_fmt; + + return 0; +} + +static int cedrus_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + + /* Fall back to dummy default by lack of hardware configuration. */ + if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE; + f->fmt.pix.sizeimage = SZ_1K; + cedrus_prepare_format(&f->fmt.pix); + + return 0; + } + + f->fmt.pix = ctx->src_fmt; + + return 0; +} + +static int cedrus_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct cedrus_dev *dev = ctx->dev; + struct v4l2_pix_format *pix_fmt = &f->fmt.pix; + + if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_DST, + dev->capabilities)) + return -EINVAL; + + cedrus_prepare_format(pix_fmt); + + return 0; +} + +static int cedrus_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct cedrus_dev *dev = ctx->dev; + struct v4l2_pix_format *pix_fmt = &f->fmt.pix; + + if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_SRC, + dev->capabilities)) + return -EINVAL; + + /* Source image size has to be provided by userspace. */ + if (pix_fmt->sizeimage == 0) + return -EINVAL; + + cedrus_prepare_format(pix_fmt); + + return 0; +} + +static int cedrus_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct cedrus_dev *dev = ctx->dev; + int ret; + + ret = cedrus_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + ctx->dst_fmt = f->fmt.pix; + + cedrus_dst_format_set(dev, &ctx->dst_fmt); + + return 0; +} + +static int cedrus_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + int ret; + + ret = cedrus_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + ctx->src_fmt = f->fmt.pix; + + /* Propagate colorspace information to capture. */ + ctx->dst_fmt.colorspace = f->fmt.pix.colorspace; + ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func; + ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->dst_fmt.quantization = f->fmt.pix.quantization; + + return 0; +} + +const struct v4l2_ioctl_ops cedrus_ioctl_ops = { + .vidioc_querycap = cedrus_querycap, + + .vidioc_enum_fmt_vid_cap = cedrus_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cedrus_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cedrus_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cedrus_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = cedrus_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = cedrus_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = cedrus_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = cedrus_s_fmt_vid_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + struct cedrus_dev *dev = ctx->dev; + struct v4l2_pix_format *pix_fmt; + u32 directions; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + directions = CEDRUS_DECODE_SRC; + pix_fmt = &ctx->src_fmt; + } else { + directions = CEDRUS_DECODE_DST; + pix_fmt = &ctx->dst_fmt; + } + + if (!cedrus_check_format(pix_fmt->pixelformat, directions, + dev->capabilities)) + return -EINVAL; + + if (*nplanes) { + if (sizes[0] < pix_fmt->sizeimage) + return -EINVAL; + } else { + sizes[0] = pix_fmt->sizeimage; + *nplanes = 1; + } + + return 0; +} + +static void cedrus_queue_cleanup(struct vb2_queue *vq, u32 state) +{ + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf; + unsigned long flags; + + for (;;) { + spin_lock_irqsave(&ctx->dev->irq_lock, flags); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + spin_unlock_irqrestore(&ctx->dev->irq_lock, flags); + + if (!vbuf) + return; + + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, + &ctx->hdl); + v4l2_m2m_buf_done(vbuf, state); + } +} + +static int cedrus_buf_init(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + + if (!V4L2_TYPE_IS_OUTPUT(vq->type)) + ctx->dst_bufs[vb->index] = vb; + + return 0; +} + +static void cedrus_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + + if (!V4L2_TYPE_IS_OUTPUT(vq->type)) + ctx->dst_bufs[vb->index] = NULL; +} + +static int cedrus_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + + if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage); + + return 0; +} + +static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + struct cedrus_dev *dev = ctx->dev; + int ret = 0; + + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_MPEG2_SLICE: + ctx->current_codec = CEDRUS_CODEC_MPEG2; + break; + + default: + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(vq->type) && + dev->dec_ops[ctx->current_codec]->start) + ret = dev->dec_ops[ctx->current_codec]->start(ctx); + + if (ret) + cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void cedrus_stop_streaming(struct vb2_queue *vq) +{ + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + struct cedrus_dev *dev = ctx->dev; + + if (V4L2_TYPE_IS_OUTPUT(vq->type) && + dev->dec_ops[ctx->current_codec]->stop) + dev->dec_ops[ctx->current_codec]->stop(ctx); + + cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR); +} + +static void cedrus_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void cedrus_buf_request_complete(struct vb2_buffer *vb) +{ + struct cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); +} + +static struct vb2_ops cedrus_qops = { + .queue_setup = cedrus_queue_setup, + .buf_prepare = cedrus_buf_prepare, + .buf_init = cedrus_buf_init, + .buf_cleanup = cedrus_buf_cleanup, + .buf_queue = cedrus_buf_queue, + .buf_request_complete = cedrus_buf_request_complete, + .start_streaming = cedrus_start_streaming, + .stop_streaming = cedrus_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct cedrus_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct cedrus_buffer); + src_vq->min_buffers_needed = 1; + src_vq->ops = &cedrus_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = ctx->dev->dev; + src_vq->supports_requests = true; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct cedrus_buffer); + dst_vq->min_buffers_needed = 1; + dst_vq->ops = &cedrus_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = ctx->dev->dev; + + return vb2_queue_init(dst_vq); +} diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.h b/drivers/staging/media/sunxi/cedrus/cedrus_video.h new file mode 100644 index 000000000000..0e4f7a8cccf2 --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cedrus VPU driver + * + * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com> + * Copyright (C) 2018 Bootlin + * + * Based on the vim2m driver, that is: + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + */ + +#ifndef _CEDRUS_VIDEO_H_ +#define _CEDRUS_VIDEO_H_ + +struct cedrus_format { + u32 pixelformat; + u32 directions; + unsigned int capabilities; +}; + +extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; + +int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +#endif diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c index a6b9ebd20263..94dadbba7cd5 100644 --- a/drivers/staging/media/zoran/zoran_card.c +++ b/drivers/staging/media/zoran/zoran_card.c @@ -706,7 +706,7 @@ zoran_register_i2c (struct zoran *zr) { zr->i2c_algo = zoran_i2c_bit_data_template; zr->i2c_algo.data = zr; - strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), + strscpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); zr->i2c_adapter.algo_data = &zr->i2c_algo; @@ -1048,7 +1048,7 @@ static int zr36057_init (struct zoran *zr) *zr->video_dev = zoran_template; zr->video_dev->v4l2_dev = &zr->v4l2_dev; zr->video_dev->lock = &zr->lock; - strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); + strscpy(zr->video_dev->name, ZR_DEVNAME(zr), sizeof(zr->video_dev->name)); /* It's not a mem2mem device, but you can both capture and output from one and the same device. This should really be split up into two device nodes, but that's a job for another day. */ @@ -1145,7 +1145,7 @@ static struct videocodec_master *zoran_setup_videocodec(struct zoran *zr, m->type = 0; m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; - strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); + strscpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); m->data = zr; switch (type) diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index d7842224fff6..27c76e2eeb41 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -692,7 +692,7 @@ static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num, case BUZ_STATE_DONE: dprintk(2, KERN_WARNING - "%s: %s - queing frame in BUZ_STATE_DONE state!?\n", + "%s: %s - queuing frame in BUZ_STATE_DONE state!?\n", ZR_DEVNAME(zr), __func__); /* fall through */ case BUZ_STATE_USER: @@ -1510,8 +1510,8 @@ static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - strlcpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)); - strlcpy(cap->driver, "zoran", sizeof(cap->driver)); + strscpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)); + strscpy(cap->driver, "zoran", sizeof(cap->driver)); snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(zr->pci_dev)); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | |