linux/drivers/media/platform/vimc/vimc-core.c
<<
>>
Prefs
   1/*
   2 * vimc-core.c Virtual Media Controller Driver
   3 *
   4 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 */
  17
  18#include <linux/component.h>
  19#include <linux/init.h>
  20#include <linux/module.h>
  21#include <linux/platform_device.h>
  22#include <media/media-device.h>
  23#include <media/v4l2-device.h>
  24
  25#include "vimc-common.h"
  26
  27#define VIMC_PDEV_NAME "vimc"
  28#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
  29
  30#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
  31        .src_ent = src,                                         \
  32        .src_pad = srcpad,                                      \
  33        .sink_ent = sink,                                       \
  34        .sink_pad = sinkpad,                                    \
  35        .flags = link_flags,                                    \
  36}
  37
  38struct vimc_device {
  39        /* The platform device */
  40        struct platform_device pdev;
  41
  42        /* The pipeline configuration */
  43        const struct vimc_pipeline_config *pipe_cfg;
  44
  45        /* The Associated media_device parent */
  46        struct media_device mdev;
  47
  48        /* Internal v4l2 parent device*/
  49        struct v4l2_device v4l2_dev;
  50
  51        /* Subdevices */
  52        struct platform_device **subdevs;
  53};
  54
  55/* Structure which describes individual configuration for each entity */
  56struct vimc_ent_config {
  57        const char *name;
  58        const char *drv;
  59};
  60
  61/* Structure which describes links between entities */
  62struct vimc_ent_link {
  63        unsigned int src_ent;
  64        u16 src_pad;
  65        unsigned int sink_ent;
  66        u16 sink_pad;
  67        u32 flags;
  68};
  69
  70/* Structure which describes the whole topology */
  71struct vimc_pipeline_config {
  72        const struct vimc_ent_config *ents;
  73        size_t num_ents;
  74        const struct vimc_ent_link *links;
  75        size_t num_links;
  76};
  77
  78/* --------------------------------------------------------------------------
  79 * Topology Configuration
  80 */
  81
  82static const struct vimc_ent_config ent_config[] = {
  83        {
  84                .name = "Sensor A",
  85                .drv = "vimc-sensor",
  86        },
  87        {
  88                .name = "Sensor B",
  89                .drv = "vimc-sensor",
  90        },
  91        {
  92                .name = "Debayer A",
  93                .drv = "vimc-debayer",
  94        },
  95        {
  96                .name = "Debayer B",
  97                .drv = "vimc-debayer",
  98        },
  99        {
 100                .name = "Raw Capture 0",
 101                .drv = "vimc-capture",
 102        },
 103        {
 104                .name = "Raw Capture 1",
 105                .drv = "vimc-capture",
 106        },
 107        {
 108                .name = "RGB/YUV Input",
 109                /* TODO: change this to vimc-input when it is implemented */
 110                .drv = "vimc-sensor",
 111        },
 112        {
 113                .name = "Scaler",
 114                .drv = "vimc-scaler",
 115        },
 116        {
 117                .name = "RGB/YUV Capture",
 118                .drv = "vimc-capture",
 119        },
 120};
 121
 122static const struct vimc_ent_link ent_links[] = {
 123        /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
 124        VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
 125        /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
 126        VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
 127        /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
 128        VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
 129        /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
 130        VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
 131        /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
 132        VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
 133        /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
 134        VIMC_ENT_LINK(3, 1, 7, 0, 0),
 135        /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
 136        VIMC_ENT_LINK(6, 0, 7, 0, 0),
 137        /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
 138        VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
 139};
 140
 141static const struct vimc_pipeline_config pipe_cfg = {
 142        .ents           = ent_config,
 143        .num_ents       = ARRAY_SIZE(ent_config),
 144        .links          = ent_links,
 145        .num_links      = ARRAY_SIZE(ent_links)
 146};
 147
 148/* -------------------------------------------------------------------------- */
 149
 150static int vimc_create_links(struct vimc_device *vimc)
 151{
 152        unsigned int i;
 153        int ret;
 154
 155        /* Initialize the links between entities */
 156        for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
 157                const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
 158                /*
 159                 * TODO: Check another way of retrieving ved struct without
 160                 * relying on platform_get_drvdata
 161                 */
 162                struct vimc_ent_device *ved_src =
 163                        platform_get_drvdata(vimc->subdevs[link->src_ent]);
 164                struct vimc_ent_device *ved_sink =
 165                        platform_get_drvdata(vimc->subdevs[link->sink_ent]);
 166
 167                ret = media_create_pad_link(ved_src->ent, link->src_pad,
 168                                            ved_sink->ent, link->sink_pad,
 169                                            link->flags);
 170                if (ret)
 171                        return ret;
 172        }
 173
 174        return 0;
 175}
 176
 177static int vimc_comp_bind(struct device *master)
 178{
 179        struct vimc_device *vimc = container_of(to_platform_device(master),
 180                                                struct vimc_device, pdev);
 181        int ret;
 182
 183        dev_dbg(master, "bind");
 184
 185        /* Register the v4l2 struct */
 186        ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
 187        if (ret) {
 188                dev_err(vimc->mdev.dev,
 189                        "v4l2 device register failed (err=%d)\n", ret);
 190                return ret;
 191        }
 192
 193        /* Bind subdevices */
 194        ret = component_bind_all(master, &vimc->v4l2_dev);
 195        if (ret)
 196                goto err_v4l2_unregister;
 197
 198        /* Initialize links */
 199        ret = vimc_create_links(vimc);
 200        if (ret)
 201                goto err_comp_unbind_all;
 202
 203        /* Register the media device */
 204        ret = media_device_register(&vimc->mdev);
 205        if (ret) {
 206                dev_err(vimc->mdev.dev,
 207                        "media device register failed (err=%d)\n", ret);
 208                goto err_comp_unbind_all;
 209        }
 210
 211        /* Expose all subdev's nodes*/
 212        ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev);
 213        if (ret) {
 214                dev_err(vimc->mdev.dev,
 215                        "vimc subdev nodes registration failed (err=%d)\n",
 216                        ret);
 217                goto err_mdev_unregister;
 218        }
 219
 220        return 0;
 221
 222err_mdev_unregister:
 223        media_device_unregister(&vimc->mdev);
 224err_comp_unbind_all:
 225        component_unbind_all(master, NULL);
 226err_v4l2_unregister:
 227        v4l2_device_unregister(&vimc->v4l2_dev);
 228
 229        return ret;
 230}
 231
 232static void vimc_comp_unbind(struct device *master)
 233{
 234        struct vimc_device *vimc = container_of(to_platform_device(master),
 235                                                struct vimc_device, pdev);
 236
 237        dev_dbg(master, "unbind");
 238
 239        media_device_unregister(&vimc->mdev);
 240        component_unbind_all(master, NULL);
 241        v4l2_device_unregister(&vimc->v4l2_dev);
 242}
 243
 244static int vimc_comp_compare(struct device *comp, void *data)
 245{
 246        const struct platform_device *pdev = to_platform_device(comp);
 247        const char *name = data;
 248
 249        return !strcmp(pdev->dev.platform_data, name);
 250}
 251
 252static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
 253{
 254        struct component_match *match = NULL;
 255        struct vimc_platform_data pdata;
 256        int i;
 257
 258        for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
 259                dev_dbg(&vimc->pdev.dev, "new pdev for %s\n",
 260                        vimc->pipe_cfg->ents[i].drv);
 261
 262                strscpy(pdata.entity_name, vimc->pipe_cfg->ents[i].name,
 263                        sizeof(pdata.entity_name));
 264
 265                vimc->subdevs[i] = platform_device_register_data(&vimc->pdev.dev,
 266                                                vimc->pipe_cfg->ents[i].drv,
 267                                                PLATFORM_DEVID_AUTO,
 268                                                &pdata,
 269                                                sizeof(pdata));
 270                if (IS_ERR(vimc->subdevs[i])) {
 271                        match = ERR_CAST(vimc->subdevs[i]);
 272                        while (--i >= 0)
 273                                platform_device_unregister(vimc->subdevs[i]);
 274
 275                        return match;
 276                }
 277
 278                component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare,
 279                                    (void *)vimc->pipe_cfg->ents[i].name);
 280        }
 281
 282        return match;
 283}
 284
 285static void vimc_rm_subdevs(struct vimc_device *vimc)
 286{
 287        unsigned int i;
 288
 289        for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
 290                platform_device_unregister(vimc->subdevs[i]);
 291}
 292
 293static const struct component_master_ops vimc_comp_ops = {
 294        .bind = vimc_comp_bind,
 295        .unbind = vimc_comp_unbind,
 296};
 297
 298static int vimc_probe(struct platform_device *pdev)
 299{
 300        struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
 301        struct component_match *match = NULL;
 302        int ret;
 303
 304        dev_dbg(&pdev->dev, "probe");
 305
 306        /* Create platform_device for each entity in the topology*/
 307        vimc->subdevs = devm_kcalloc(&vimc->pdev.dev, vimc->pipe_cfg->num_ents,
 308                                     sizeof(*vimc->subdevs), GFP_KERNEL);
 309        if (!vimc->subdevs)
 310                return -ENOMEM;
 311
 312        match = vimc_add_subdevs(vimc);
 313        if (IS_ERR(match))
 314                return PTR_ERR(match);
 315
 316        /* Link the media device within the v4l2_device */
 317        vimc->v4l2_dev.mdev = &vimc->mdev;
 318
 319        /* Initialize media device */
 320        strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
 321                sizeof(vimc->mdev.model));
 322        vimc->mdev.dev = &pdev->dev;
 323        media_device_init(&vimc->mdev);
 324
 325        /* Add self to the component system */
 326        ret = component_master_add_with_match(&pdev->dev, &vimc_comp_ops,
 327                                              match);
 328        if (ret) {
 329                media_device_cleanup(&vimc->mdev);
 330                vimc_rm_subdevs(vimc);
 331                kfree(vimc);
 332                return ret;
 333        }
 334
 335        return 0;
 336}
 337
 338static int vimc_remove(struct platform_device *pdev)
 339{
 340        struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
 341
 342        dev_dbg(&pdev->dev, "remove");
 343
 344        component_master_del(&pdev->dev, &vimc_comp_ops);
 345        vimc_rm_subdevs(vimc);
 346
 347        return 0;
 348}
 349
 350static void vimc_dev_release(struct device *dev)
 351{
 352}
 353
 354static struct vimc_device vimc_dev = {
 355        .pipe_cfg = &pipe_cfg,
 356        .pdev = {
 357                .name = VIMC_PDEV_NAME,
 358                .dev.release = vimc_dev_release,
 359        }
 360};
 361
 362static struct platform_driver vimc_pdrv = {
 363        .probe          = vimc_probe,
 364        .remove         = vimc_remove,
 365        .driver         = {
 366                .name   = VIMC_PDEV_NAME,
 367        },
 368};
 369
 370static int __init vimc_init(void)
 371{
 372        int ret;
 373
 374        ret = platform_device_register(&vimc_dev.pdev);
 375        if (ret) {
 376                dev_err(&vimc_dev.pdev.dev,
 377                        "platform device registration failed (err=%d)\n", ret);
 378                return ret;
 379        }
 380
 381        ret = platform_driver_register(&vimc_pdrv);
 382        if (ret) {
 383                dev_err(&vimc_dev.pdev.dev,
 384                        "platform driver registration failed (err=%d)\n", ret);
 385                platform_driver_unregister(&vimc_pdrv);
 386                return ret;
 387        }
 388
 389        return 0;
 390}
 391
 392static void __exit vimc_exit(void)
 393{
 394        platform_driver_unregister(&vimc_pdrv);
 395
 396        platform_device_unregister(&vimc_dev.pdev);
 397}
 398
 399module_init(vimc_init);
 400module_exit(vimc_exit);
 401
 402MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
 403MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
 404MODULE_LICENSE("GPL");
 405