linux/drivers/staging/media/imx/imx-media-internal-sd.c
<<
>>
Prefs
   1/*
   2 * Media driver for Freescale i.MX5/6 SOC
   3 *
   4 * Adds the internal subdevices and the media links between them.
   5 *
   6 * Copyright (c) 2016 Mentor Graphics Inc.
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13#include <linux/platform_device.h>
  14#include "imx-media.h"
  15
  16enum isd_enum {
  17        isd_csi0 = 0,
  18        isd_csi1,
  19        isd_vdic,
  20        isd_ic_prp,
  21        isd_ic_prpenc,
  22        isd_ic_prpvf,
  23        num_isd,
  24};
  25
  26static const struct internal_subdev_id {
  27        enum isd_enum index;
  28        const char *name;
  29        u32 grp_id;
  30} isd_id[num_isd] = {
  31        [isd_csi0] = {
  32                .index = isd_csi0,
  33                .grp_id = IMX_MEDIA_GRP_ID_CSI0,
  34                .name = "imx-ipuv3-csi",
  35        },
  36        [isd_csi1] = {
  37                .index = isd_csi1,
  38                .grp_id = IMX_MEDIA_GRP_ID_CSI1,
  39                .name = "imx-ipuv3-csi",
  40        },
  41        [isd_vdic] = {
  42                .index = isd_vdic,
  43                .grp_id = IMX_MEDIA_GRP_ID_VDIC,
  44                .name = "imx-ipuv3-vdic",
  45        },
  46        [isd_ic_prp] = {
  47                .index = isd_ic_prp,
  48                .grp_id = IMX_MEDIA_GRP_ID_IC_PRP,
  49                .name = "imx-ipuv3-ic",
  50        },
  51        [isd_ic_prpenc] = {
  52                .index = isd_ic_prpenc,
  53                .grp_id = IMX_MEDIA_GRP_ID_IC_PRPENC,
  54                .name = "imx-ipuv3-ic",
  55        },
  56        [isd_ic_prpvf] = {
  57                .index = isd_ic_prpvf,
  58                .grp_id = IMX_MEDIA_GRP_ID_IC_PRPVF,
  59                .name = "imx-ipuv3-ic",
  60        },
  61};
  62
  63struct internal_subdev;
  64
  65struct internal_link {
  66        const struct internal_subdev *remote;
  67        int local_pad;
  68        int remote_pad;
  69};
  70
  71/* max pads per internal-sd */
  72#define MAX_INTERNAL_PADS   8
  73/* max links per internal-sd pad */
  74#define MAX_INTERNAL_LINKS  8
  75
  76struct internal_pad {
  77        struct internal_link link[MAX_INTERNAL_LINKS];
  78};
  79
  80static const struct internal_subdev {
  81        const struct internal_subdev_id *id;
  82        struct internal_pad pad[MAX_INTERNAL_PADS];
  83} int_subdev[num_isd] = {
  84        [isd_csi0] = {
  85                .id = &isd_id[isd_csi0],
  86                .pad[CSI_SRC_PAD_DIRECT] = {
  87                        .link = {
  88                                {
  89                                        .local_pad = CSI_SRC_PAD_DIRECT,
  90                                        .remote = &int_subdev[isd_ic_prp],
  91                                        .remote_pad = PRP_SINK_PAD,
  92                                }, {
  93                                        .local_pad = CSI_SRC_PAD_DIRECT,
  94                                        .remote = &int_subdev[isd_vdic],
  95                                        .remote_pad = VDIC_SINK_PAD_DIRECT,
  96                                },
  97                        },
  98                },
  99        },
 100
 101        [isd_csi1] = {
 102                .id = &isd_id[isd_csi1],
 103                .pad[CSI_SRC_PAD_DIRECT] = {
 104                        .link = {
 105                                {
 106                                        .local_pad = CSI_SRC_PAD_DIRECT,
 107                                        .remote = &int_subdev[isd_ic_prp],
 108                                        .remote_pad = PRP_SINK_PAD,
 109                                }, {
 110                                        .local_pad = CSI_SRC_PAD_DIRECT,
 111                                        .remote = &int_subdev[isd_vdic],
 112                                        .remote_pad = VDIC_SINK_PAD_DIRECT,
 113                                },
 114                        },
 115                },
 116        },
 117
 118        [isd_vdic] = {
 119                .id = &isd_id[isd_vdic],
 120                .pad[VDIC_SRC_PAD_DIRECT] = {
 121                        .link = {
 122                                {
 123                                        .local_pad = VDIC_SRC_PAD_DIRECT,
 124                                        .remote = &int_subdev[isd_ic_prp],
 125                                        .remote_pad = PRP_SINK_PAD,
 126                                },
 127                        },
 128                },
 129        },
 130
 131        [isd_ic_prp] = {
 132                .id = &isd_id[isd_ic_prp],
 133                .pad[PRP_SRC_PAD_PRPENC] = {
 134                        .link = {
 135                                {
 136                                        .local_pad = PRP_SRC_PAD_PRPENC,
 137                                        .remote = &int_subdev[isd_ic_prpenc],
 138                                        .remote_pad = 0,
 139                                },
 140                        },
 141                },
 142                .pad[PRP_SRC_PAD_PRPVF] = {
 143                        .link = {
 144                                {
 145                                        .local_pad = PRP_SRC_PAD_PRPVF,
 146                                        .remote = &int_subdev[isd_ic_prpvf],
 147                                        .remote_pad = 0,
 148                                },
 149                        },
 150                },
 151        },
 152
 153        [isd_ic_prpenc] = {
 154                .id = &isd_id[isd_ic_prpenc],
 155        },
 156
 157        [isd_ic_prpvf] = {
 158                .id = &isd_id[isd_ic_prpvf],
 159        },
 160};
 161
 162/* form a device name given an internal subdev and ipu id */
 163static inline void isd_to_devname(char *devname, int sz,
 164                                  const struct internal_subdev *isd,
 165                                  int ipu_id)
 166{
 167        int pdev_id = ipu_id * num_isd + isd->id->index;
 168
 169        snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id);
 170}
 171
 172static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id)
 173{
 174        enum isd_enum i;
 175
 176        for (i = 0; i < num_isd; i++) {
 177                const struct internal_subdev *isd = &int_subdev[i];
 178
 179                if (isd->id->grp_id == grp_id)
 180                        return isd;
 181        }
 182
 183        return NULL;
 184}
 185
 186static struct v4l2_subdev *find_sink(struct imx_media_dev *imxmd,
 187                                     struct v4l2_subdev *src,
 188                                     const struct internal_link *link)
 189{
 190        char sink_devname[32];
 191        int ipu_id;
 192
 193        /*
 194         * retrieve IPU id from subdev name, note: can't get this from
 195         * struct imx_media_internal_sd_platformdata because if src is
 196         * a CSI, it has different struct ipu_client_platformdata which
 197         * does not contain IPU id.
 198         */
 199        if (sscanf(src->name, "ipu%d", &ipu_id) != 1)
 200                return NULL;
 201
 202        isd_to_devname(sink_devname, sizeof(sink_devname),
 203                       link->remote, ipu_id - 1);
 204
 205        return imx_media_find_subdev_by_devname(imxmd, sink_devname);
 206}
 207
 208static int create_ipu_internal_link(struct imx_media_dev *imxmd,
 209                                    struct v4l2_subdev *src,
 210                                    const struct internal_link *link)
 211{
 212        struct v4l2_subdev *sink;
 213        int ret;
 214
 215        sink = find_sink(imxmd, src, link);
 216        if (!sink)
 217                return -ENODEV;
 218
 219        v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n",
 220                  src->name, link->local_pad,
 221                  sink->name, link->remote_pad);
 222
 223        ret = media_create_pad_link(&src->entity, link->local_pad,
 224                                    &sink->entity, link->remote_pad, 0);
 225        if (ret)
 226                v4l2_err(&imxmd->v4l2_dev,
 227                         "create_pad_link failed: %d\n", ret);
 228
 229        return ret;
 230}
 231
 232int imx_media_create_internal_links(struct imx_media_dev *imxmd,
 233                                    struct v4l2_subdev *sd)
 234{
 235        const struct internal_subdev *intsd;
 236        const struct internal_pad *intpad;
 237        const struct internal_link *link;
 238        struct media_pad *pad;
 239        int i, j, ret;
 240
 241        intsd = find_intsd_by_grp_id(sd->grp_id);
 242        if (!intsd)
 243                return -ENODEV;
 244
 245        /* create the source->sink links */
 246        for (i = 0; i < sd->entity.num_pads; i++) {
 247                intpad = &intsd->pad[i];
 248                pad = &sd->entity.pads[i];
 249
 250                if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
 251                        continue;
 252
 253                for (j = 0; ; j++) {
 254                        link = &intpad->link[j];
 255
 256                        if (!link->remote)
 257                                break;
 258
 259                        ret = create_ipu_internal_link(imxmd, sd, link);
 260                        if (ret)
 261                                return ret;
 262                }
 263        }
 264
 265        return 0;
 266}
 267
 268/* register an internal subdev as a platform device */
 269static int add_internal_subdev(struct imx_media_dev *imxmd,
 270                               const struct internal_subdev *isd,
 271                               int ipu_id)
 272{
 273        struct imx_media_internal_sd_platformdata pdata;
 274        struct platform_device_info pdevinfo = {};
 275        struct platform_device *pdev;
 276
 277        pdata.grp_id = isd->id->grp_id;
 278
 279        /* the id of IPU this subdev will control */
 280        pdata.ipu_id = ipu_id;
 281
 282        /* create subdev name */
 283        imx_media_grp_id_to_sd_name(pdata.sd_name, sizeof(pdata.sd_name),
 284                                    pdata.grp_id, ipu_id);
 285
 286        pdevinfo.name = isd->id->name;
 287        pdevinfo.id = ipu_id * num_isd + isd->id->index;
 288        pdevinfo.parent = imxmd->md.dev;
 289        pdevinfo.data = &pdata;
 290        pdevinfo.size_data = sizeof(pdata);
 291        pdevinfo.dma_mask = DMA_BIT_MASK(32);
 292
 293        pdev = platform_device_register_full(&pdevinfo);
 294        if (IS_ERR(pdev))
 295                return PTR_ERR(pdev);
 296
 297        return imx_media_add_async_subdev(imxmd, NULL, pdev);
 298}
 299
 300/* adds the internal subdevs in one ipu */
 301static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id)
 302{
 303        enum isd_enum i;
 304
 305        for (i = 0; i < num_isd; i++) {
 306                const struct internal_subdev *isd = &int_subdev[i];
 307                int ret;
 308
 309                /*
 310                 * the CSIs are represented in the device-tree, so those
 311                 * devices are already added to the async subdev list by
 312                 * of_parse_subdev().
 313                 */
 314                switch (isd->id->grp_id) {
 315                case IMX_MEDIA_GRP_ID_CSI0:
 316                case IMX_MEDIA_GRP_ID_CSI1:
 317                        ret = 0;
 318                        break;
 319                default:
 320                        ret = add_internal_subdev(imxmd, isd, ipu_id);
 321                        break;
 322                }
 323
 324                if (ret)
 325                        return ret;
 326        }
 327
 328        return 0;
 329}
 330
 331int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd)
 332{
 333        int ret;
 334
 335        ret = add_ipu_internal_subdevs(imxmd, 0);
 336        if (ret)
 337                goto remove;
 338
 339        ret = add_ipu_internal_subdevs(imxmd, 1);
 340        if (ret)
 341                goto remove;
 342
 343        return 0;
 344
 345remove:
 346        imx_media_remove_internal_subdevs(imxmd);
 347        return ret;
 348}
 349
 350void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd)
 351{
 352        struct imx_media_async_subdev *imxasd;
 353        struct v4l2_async_subdev *asd;
 354
 355        list_for_each_entry(asd, &imxmd->notifier.asd_list, asd_list) {
 356                imxasd = to_imx_media_asd(asd);
 357
 358                if (!imxasd->pdev)
 359                        continue;
 360
 361                platform_device_unregister(imxasd->pdev);
 362        }
 363}
 364