linux/drivers/staging/media/imx/imx-media-internal-sd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Media driver for Freescale i.MX5/6 SOC
   4 *
   5 * Adds the IPU internal subdevices and the media links between them.
   6 *
   7 * Copyright (c) 2016 Mentor Graphics Inc.
   8 */
   9#include <linux/platform_device.h>
  10#include "imx-media.h"
  11
  12/* max pads per internal-sd */
  13#define MAX_INTERNAL_PADS   8
  14/* max links per internal-sd pad */
  15#define MAX_INTERNAL_LINKS  8
  16
  17struct internal_subdev;
  18
  19struct internal_link {
  20        int remote;
  21        int local_pad;
  22        int remote_pad;
  23};
  24
  25struct internal_pad {
  26        int num_links;
  27        struct internal_link link[MAX_INTERNAL_LINKS];
  28};
  29
  30struct internal_subdev {
  31        u32 grp_id;
  32        struct internal_pad pad[MAX_INTERNAL_PADS];
  33
  34        struct v4l2_subdev * (*sync_register)(struct v4l2_device *v4l2_dev,
  35                                              struct device *ipu_dev,
  36                                              struct ipu_soc *ipu,
  37                                              u32 grp_id);
  38        int (*sync_unregister)(struct v4l2_subdev *sd);
  39};
  40
  41static const struct internal_subdev int_subdev[NUM_IPU_SUBDEVS] = {
  42        [IPU_CSI0] = {
  43                .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0,
  44                .pad[CSI_SRC_PAD_DIRECT] = {
  45                        .num_links = 2,
  46                        .link = {
  47                                {
  48                                        .local_pad = CSI_SRC_PAD_DIRECT,
  49                                        .remote = IPU_IC_PRP,
  50                                        .remote_pad = PRP_SINK_PAD,
  51                                }, {
  52                                        .local_pad = CSI_SRC_PAD_DIRECT,
  53                                        .remote = IPU_VDIC,
  54                                        .remote_pad = VDIC_SINK_PAD_DIRECT,
  55                                },
  56                        },
  57                },
  58        },
  59
  60        [IPU_CSI1] = {
  61                .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1,
  62                .pad[CSI_SRC_PAD_DIRECT] = {
  63                        .num_links = 2,
  64                        .link = {
  65                                {
  66                                        .local_pad = CSI_SRC_PAD_DIRECT,
  67                                        .remote = IPU_IC_PRP,
  68                                        .remote_pad = PRP_SINK_PAD,
  69                                }, {
  70                                        .local_pad = CSI_SRC_PAD_DIRECT,
  71                                        .remote = IPU_VDIC,
  72                                        .remote_pad = VDIC_SINK_PAD_DIRECT,
  73                                },
  74                        },
  75                },
  76        },
  77
  78        [IPU_VDIC] = {
  79                .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC,
  80                .sync_register = imx_media_vdic_register,
  81                .sync_unregister = imx_media_vdic_unregister,
  82                .pad[VDIC_SRC_PAD_DIRECT] = {
  83                        .num_links = 1,
  84                        .link = {
  85                                {
  86                                        .local_pad = VDIC_SRC_PAD_DIRECT,
  87                                        .remote = IPU_IC_PRP,
  88                                        .remote_pad = PRP_SINK_PAD,
  89                                },
  90                        },
  91                },
  92        },
  93
  94        [IPU_IC_PRP] = {
  95                .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP,
  96                .sync_register = imx_media_ic_register,
  97                .sync_unregister = imx_media_ic_unregister,
  98                .pad[PRP_SRC_PAD_PRPENC] = {
  99                        .num_links = 1,
 100                        .link = {
 101                                {
 102                                        .local_pad = PRP_SRC_PAD_PRPENC,
 103                                        .remote = IPU_IC_PRPENC,
 104                                        .remote_pad = PRPENCVF_SINK_PAD,
 105                                },
 106                        },
 107                },
 108                .pad[PRP_SRC_PAD_PRPVF] = {
 109                        .num_links = 1,
 110                        .link = {
 111                                {
 112                                        .local_pad = PRP_SRC_PAD_PRPVF,
 113                                        .remote = IPU_IC_PRPVF,
 114                                        .remote_pad = PRPENCVF_SINK_PAD,
 115                                },
 116                        },
 117                },
 118        },
 119
 120        [IPU_IC_PRPENC] = {
 121                .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC,
 122                .sync_register = imx_media_ic_register,
 123                .sync_unregister = imx_media_ic_unregister,
 124        },
 125
 126        [IPU_IC_PRPVF] = {
 127                .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF,
 128                .sync_register = imx_media_ic_register,
 129                .sync_unregister = imx_media_ic_unregister,
 130        },
 131};
 132
 133static int create_internal_link(struct imx_media_dev *imxmd,
 134                                struct v4l2_subdev *src,
 135                                struct v4l2_subdev *sink,
 136                                const struct internal_link *link)
 137{
 138        int ret;
 139
 140        /* skip if this link already created */
 141        if (media_entity_find_link(&src->entity.pads[link->local_pad],
 142                                   &sink->entity.pads[link->remote_pad]))
 143                return 0;
 144
 145        dev_dbg(imxmd->md.dev, "%s:%d -> %s:%d\n",
 146                src->name, link->local_pad,
 147                sink->name, link->remote_pad);
 148
 149        ret = media_create_pad_link(&src->entity, link->local_pad,
 150                                    &sink->entity, link->remote_pad, 0);
 151        if (ret)
 152                v4l2_err(&imxmd->v4l2_dev, "%s failed: %d\n", __func__, ret);
 153
 154        return ret;
 155}
 156
 157static int create_ipu_internal_links(struct imx_media_dev *imxmd,
 158                                     const struct internal_subdev *intsd,
 159                                     struct v4l2_subdev *sd,
 160                                     int ipu_id)
 161{
 162        const struct internal_pad *intpad;
 163        const struct internal_link *link;
 164        struct media_pad *pad;
 165        int i, j, ret;
 166
 167        /* create the source->sink links */
 168        for (i = 0; i < sd->entity.num_pads; i++) {
 169                intpad = &intsd->pad[i];
 170                pad = &sd->entity.pads[i];
 171
 172                if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
 173                        continue;
 174
 175                for (j = 0; j < intpad->num_links; j++) {
 176                        struct v4l2_subdev *sink;
 177
 178                        link = &intpad->link[j];
 179                        sink = imxmd->sync_sd[ipu_id][link->remote];
 180
 181                        ret = create_internal_link(imxmd, sd, sink, link);
 182                        if (ret)
 183                                return ret;
 184                }
 185        }
 186
 187        return 0;
 188}
 189
 190int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd,
 191                                            struct v4l2_subdev *csi)
 192{
 193        struct device *ipu_dev = csi->dev->parent;
 194        const struct internal_subdev *intsd;
 195        struct v4l2_subdev *sd;
 196        struct ipu_soc *ipu;
 197        int i, ipu_id, ret;
 198
 199        ipu = dev_get_drvdata(ipu_dev);
 200        if (!ipu) {
 201                v4l2_err(&imxmd->v4l2_dev, "invalid IPU device!\n");
 202                return -ENODEV;
 203        }
 204
 205        ipu_id = ipu_get_num(ipu);
 206        if (ipu_id > 1) {
 207                v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id);
 208                return -ENODEV;
 209        }
 210
 211        mutex_lock(&imxmd->mutex);
 212
 213        /* record this IPU */
 214        if (!imxmd->ipu[ipu_id])
 215                imxmd->ipu[ipu_id] = ipu;
 216
 217        /* register the synchronous subdevs */
 218        for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
 219                intsd = &int_subdev[i];
 220
 221                sd = imxmd->sync_sd[ipu_id][i];
 222
 223                /*
 224                 * skip if this sync subdev already registered or its
 225                 * not a sync subdev (one of the CSIs)
 226                 */
 227                if (sd || !intsd->sync_register)
 228                        continue;
 229
 230                mutex_unlock(&imxmd->mutex);
 231                sd = intsd->sync_register(&imxmd->v4l2_dev, ipu_dev, ipu,
 232                                          intsd->grp_id);
 233                mutex_lock(&imxmd->mutex);
 234                if (IS_ERR(sd)) {
 235                        ret = PTR_ERR(sd);
 236                        goto err_unwind;
 237                }
 238
 239                imxmd->sync_sd[ipu_id][i] = sd;
 240        }
 241
 242        /*
 243         * all the sync subdevs are registered, create the media links
 244         * between them.
 245         */
 246        for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
 247                intsd = &int_subdev[i];
 248
 249                if (intsd->grp_id == csi->grp_id) {
 250                        sd = csi;
 251                } else {
 252                        sd = imxmd->sync_sd[ipu_id][i];
 253                        if (!sd)
 254                                continue;
 255                }
 256
 257                ret = create_ipu_internal_links(imxmd, intsd, sd, ipu_id);
 258                if (ret) {
 259                        mutex_unlock(&imxmd->mutex);
 260                        imx_media_unregister_ipu_internal_subdevs(imxmd);
 261                        return ret;
 262                }
 263        }
 264
 265        mutex_unlock(&imxmd->mutex);
 266        return 0;
 267
 268err_unwind:
 269        while (--i >= 0) {
 270                intsd = &int_subdev[i];
 271                sd = imxmd->sync_sd[ipu_id][i];
 272                if (!sd || !intsd->sync_unregister)
 273                        continue;
 274                mutex_unlock(&imxmd->mutex);
 275                intsd->sync_unregister(sd);
 276                mutex_lock(&imxmd->mutex);
 277        }
 278
 279        mutex_unlock(&imxmd->mutex);
 280        return ret;
 281}
 282
 283void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd)
 284{
 285        const struct internal_subdev *intsd;
 286        struct v4l2_subdev *sd;
 287        int i, j;
 288
 289        mutex_lock(&imxmd->mutex);
 290
 291        for (i = 0; i < 2; i++) {
 292                for (j = 0; j < NUM_IPU_SUBDEVS; j++) {
 293                        intsd = &int_subdev[j];
 294                        sd = imxmd->sync_sd[i][j];
 295
 296                        if (!sd || !intsd->sync_unregister)
 297                                continue;
 298
 299                        mutex_unlock(&imxmd->mutex);
 300                        intsd->sync_unregister(sd);
 301                        mutex_lock(&imxmd->mutex);
 302                }
 303        }
 304
 305        mutex_unlock(&imxmd->mutex);
 306}
 307