diff options
Diffstat (limited to 'drivers/staging/media/imx/imx-media-internal-sd.c')
-rw-r--r-- | drivers/staging/media/imx/imx-media-internal-sd.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c new file mode 100644 index 000000000..da4109b2f --- /dev/null +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Media driver for Freescale i.MX5/6 SOC + * + * Adds the IPU internal subdevices and the media links between them. + * + * Copyright (c) 2016 Mentor Graphics Inc. + */ +#include <linux/platform_device.h> +#include "imx-media.h" + +/* max pads per internal-sd */ +#define MAX_INTERNAL_PADS 8 +/* max links per internal-sd pad */ +#define MAX_INTERNAL_LINKS 8 + +struct internal_subdev; + +struct internal_link { + int remote; + int local_pad; + int remote_pad; +}; + +struct internal_pad { + int num_links; + struct internal_link link[MAX_INTERNAL_LINKS]; +}; + +struct internal_subdev { + u32 grp_id; + struct internal_pad pad[MAX_INTERNAL_PADS]; + + struct v4l2_subdev * (*sync_register)(struct v4l2_device *v4l2_dev, + struct device *ipu_dev, + struct ipu_soc *ipu, + u32 grp_id); + int (*sync_unregister)(struct v4l2_subdev *sd); +}; + +static const struct internal_subdev int_subdev[NUM_IPU_SUBDEVS] = { + [IPU_CSI0] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0, + .pad[CSI_SRC_PAD_DIRECT] = { + .num_links = 2, + .link = { + { + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = IPU_IC_PRP, + .remote_pad = PRP_SINK_PAD, + }, { + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = IPU_VDIC, + .remote_pad = VDIC_SINK_PAD_DIRECT, + }, + }, + }, + }, + + [IPU_CSI1] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1, + .pad[CSI_SRC_PAD_DIRECT] = { + .num_links = 2, + .link = { + { + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = IPU_IC_PRP, + .remote_pad = PRP_SINK_PAD, + }, { + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = IPU_VDIC, + .remote_pad = VDIC_SINK_PAD_DIRECT, + }, + }, + }, + }, + + [IPU_VDIC] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC, + .sync_register = imx_media_vdic_register, + .sync_unregister = imx_media_vdic_unregister, + .pad[VDIC_SRC_PAD_DIRECT] = { + .num_links = 1, + .link = { + { + .local_pad = VDIC_SRC_PAD_DIRECT, + .remote = IPU_IC_PRP, + .remote_pad = PRP_SINK_PAD, + }, + }, + }, + }, + + [IPU_IC_PRP] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP, + .sync_register = imx_media_ic_register, + .sync_unregister = imx_media_ic_unregister, + .pad[PRP_SRC_PAD_PRPENC] = { + .num_links = 1, + .link = { + { + .local_pad = PRP_SRC_PAD_PRPENC, + .remote = IPU_IC_PRPENC, + .remote_pad = PRPENCVF_SINK_PAD, + }, + }, + }, + .pad[PRP_SRC_PAD_PRPVF] = { + .num_links = 1, + .link = { + { + .local_pad = PRP_SRC_PAD_PRPVF, + .remote = IPU_IC_PRPVF, + .remote_pad = PRPENCVF_SINK_PAD, + }, + }, + }, + }, + + [IPU_IC_PRPENC] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC, + .sync_register = imx_media_ic_register, + .sync_unregister = imx_media_ic_unregister, + }, + + [IPU_IC_PRPVF] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF, + .sync_register = imx_media_ic_register, + .sync_unregister = imx_media_ic_unregister, + }, +}; + +static int create_internal_link(struct imx_media_dev *imxmd, + struct v4l2_subdev *src, + struct v4l2_subdev *sink, + const struct internal_link *link) +{ + int ret; + + /* skip if this link already created */ + if (media_entity_find_link(&src->entity.pads[link->local_pad], + &sink->entity.pads[link->remote_pad])) + return 0; + + dev_dbg(imxmd->md.dev, "%s:%d -> %s:%d\n", + src->name, link->local_pad, + sink->name, link->remote_pad); + + ret = media_create_pad_link(&src->entity, link->local_pad, + &sink->entity, link->remote_pad, 0); + if (ret) + v4l2_err(&imxmd->v4l2_dev, "%s failed: %d\n", __func__, ret); + + return ret; +} + +static int create_ipu_internal_links(struct imx_media_dev *imxmd, + const struct internal_subdev *intsd, + struct v4l2_subdev *sd, + int ipu_id) +{ + const struct internal_pad *intpad; + const struct internal_link *link; + struct media_pad *pad; + int i, j, ret; + + /* create the source->sink links */ + for (i = 0; i < sd->entity.num_pads; i++) { + intpad = &intsd->pad[i]; + pad = &sd->entity.pads[i]; + + if (!(pad->flags & MEDIA_PAD_FL_SOURCE)) + continue; + + for (j = 0; j < intpad->num_links; j++) { + struct v4l2_subdev *sink; + + link = &intpad->link[j]; + sink = imxmd->sync_sd[ipu_id][link->remote]; + + ret = create_internal_link(imxmd, sd, sink, link); + if (ret) + return ret; + } + } + + return 0; +} + +int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd, + struct v4l2_subdev *csi) +{ + struct device *ipu_dev = csi->dev->parent; + const struct internal_subdev *intsd; + struct v4l2_subdev *sd; + struct ipu_soc *ipu; + int i, ipu_id, ret; + + ipu = dev_get_drvdata(ipu_dev); + if (!ipu) { + v4l2_err(&imxmd->v4l2_dev, "invalid IPU device!\n"); + return -ENODEV; + } + + ipu_id = ipu_get_num(ipu); + if (ipu_id > 1) { + v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id); + return -ENODEV; + } + + mutex_lock(&imxmd->mutex); + + /* record this IPU */ + if (!imxmd->ipu[ipu_id]) + imxmd->ipu[ipu_id] = ipu; + + /* register the synchronous subdevs */ + for (i = 0; i < NUM_IPU_SUBDEVS; i++) { + intsd = &int_subdev[i]; + + sd = imxmd->sync_sd[ipu_id][i]; + + /* + * skip if this sync subdev already registered or its + * not a sync subdev (one of the CSIs) + */ + if (sd || !intsd->sync_register) + continue; + + mutex_unlock(&imxmd->mutex); + sd = intsd->sync_register(&imxmd->v4l2_dev, ipu_dev, ipu, + intsd->grp_id); + mutex_lock(&imxmd->mutex); + if (IS_ERR(sd)) { + ret = PTR_ERR(sd); + goto err_unwind; + } + + imxmd->sync_sd[ipu_id][i] = sd; + } + + /* + * all the sync subdevs are registered, create the media links + * between them. + */ + for (i = 0; i < NUM_IPU_SUBDEVS; i++) { + intsd = &int_subdev[i]; + + if (intsd->grp_id == csi->grp_id) { + sd = csi; + } else { + sd = imxmd->sync_sd[ipu_id][i]; + if (!sd) + continue; + } + + ret = create_ipu_internal_links(imxmd, intsd, sd, ipu_id); + if (ret) { + mutex_unlock(&imxmd->mutex); + imx_media_unregister_ipu_internal_subdevs(imxmd); + return ret; + } + } + + mutex_unlock(&imxmd->mutex); + return 0; + +err_unwind: + while (--i >= 0) { + intsd = &int_subdev[i]; + sd = imxmd->sync_sd[ipu_id][i]; + if (!sd || !intsd->sync_unregister) + continue; + mutex_unlock(&imxmd->mutex); + intsd->sync_unregister(sd); + mutex_lock(&imxmd->mutex); + } + + mutex_unlock(&imxmd->mutex); + return ret; +} + +void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd) +{ + const struct internal_subdev *intsd; + struct v4l2_subdev *sd; + int i, j; + + mutex_lock(&imxmd->mutex); + + for (i = 0; i < 2; i++) { + for (j = 0; j < NUM_IPU_SUBDEVS; j++) { + intsd = &int_subdev[j]; + sd = imxmd->sync_sd[i][j]; + + if (!sd || !intsd->sync_unregister) + continue; + + mutex_unlock(&imxmd->mutex); + intsd->sync_unregister(sd); + mutex_lock(&imxmd->mutex); + } + } + + mutex_unlock(&imxmd->mutex); +} |