linux/drivers/media/test-drivers/vimc/vimc-core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * vimc-core.c Virtual Media Controller Driver
   4 *
   5 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
   6 */
   7
   8#include <linux/font.h>
   9#include <linux/init.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <media/media-device.h>
  13#include <media/tpg/v4l2-tpg.h>
  14#include <media/v4l2-device.h>
  15
  16#include "vimc-common.h"
  17
  18#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
  19
  20#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
  21        .src_ent = src,                                         \
  22        .src_pad = srcpad,                                      \
  23        .sink_ent = sink,                                       \
  24        .sink_pad = sinkpad,                                    \
  25        .flags = link_flags,                                    \
  26}
  27
  28/* Structure which describes links between entities */
  29struct vimc_ent_link {
  30        unsigned int src_ent;
  31        u16 src_pad;
  32        unsigned int sink_ent;
  33        u16 sink_pad;
  34        u32 flags;
  35};
  36
  37/* Structure which describes the whole topology */
  38struct vimc_pipeline_config {
  39        const struct vimc_ent_config *ents;
  40        size_t num_ents;
  41        const struct vimc_ent_link *links;
  42        size_t num_links;
  43};
  44
  45/* --------------------------------------------------------------------------
  46 * Topology Configuration
  47 */
  48
  49static struct vimc_ent_config ent_config[] = {
  50        {
  51                .name = "Sensor A",
  52                .type = &vimc_sen_type
  53        },
  54        {
  55                .name = "Sensor B",
  56                .type = &vimc_sen_type
  57        },
  58        {
  59                .name = "Debayer A",
  60                .type = &vimc_deb_type
  61        },
  62        {
  63                .name = "Debayer B",
  64                .type = &vimc_deb_type
  65        },
  66        {
  67                .name = "Raw Capture 0",
  68                .type = &vimc_cap_type
  69        },
  70        {
  71                .name = "Raw Capture 1",
  72                .type = &vimc_cap_type
  73        },
  74        {
  75                /* TODO: change this to vimc-input when it is implemented */
  76                .name = "RGB/YUV Input",
  77                .type = &vimc_sen_type
  78        },
  79        {
  80                .name = "Scaler",
  81                .type = &vimc_sca_type
  82        },
  83        {
  84                .name = "RGB/YUV Capture",
  85                .type = &vimc_cap_type
  86        },
  87};
  88
  89static const struct vimc_ent_link ent_links[] = {
  90        /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
  91        VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
  92        /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
  93        VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
  94        /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
  95        VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
  96        /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
  97        VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
  98        /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
  99        VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
 100        /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
 101        VIMC_ENT_LINK(3, 1, 7, 0, 0),
 102        /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
 103        VIMC_ENT_LINK(6, 0, 7, 0, 0),
 104        /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
 105        VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
 106};
 107
 108static struct vimc_pipeline_config pipe_cfg = {
 109        .ents           = ent_config,
 110        .num_ents       = ARRAY_SIZE(ent_config),
 111        .links          = ent_links,
 112        .num_links      = ARRAY_SIZE(ent_links)
 113};
 114
 115/* -------------------------------------------------------------------------- */
 116
 117static void vimc_rm_links(struct vimc_device *vimc)
 118{
 119        unsigned int i;
 120
 121        for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
 122                media_entity_remove_links(vimc->ent_devs[i]->ent);
 123}
 124
 125static int vimc_create_links(struct vimc_device *vimc)
 126{
 127        unsigned int i;
 128        int ret;
 129
 130        /* Initialize the links between entities */
 131        for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
 132                const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
 133
 134                struct vimc_ent_device *ved_src =
 135                        vimc->ent_devs[link->src_ent];
 136                struct vimc_ent_device *ved_sink =
 137                        vimc->ent_devs[link->sink_ent];
 138
 139                ret = media_create_pad_link(ved_src->ent, link->src_pad,
 140                                            ved_sink->ent, link->sink_pad,
 141                                            link->flags);
 142                if (ret)
 143                        goto err_rm_links;
 144        }
 145
 146        return 0;
 147
 148err_rm_links:
 149        vimc_rm_links(vimc);
 150        return ret;
 151}
 152
 153static void vimc_release_subdevs(struct vimc_device *vimc)
 154{
 155        unsigned int i;
 156
 157        for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
 158                if (vimc->ent_devs[i])
 159                        vimc->pipe_cfg->ents[i].type->release(vimc->ent_devs[i]);
 160}
 161
 162static void vimc_unregister_subdevs(struct vimc_device *vimc)
 163{
 164        unsigned int i;
 165
 166        for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
 167                if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].type->unregister)
 168                        vimc->pipe_cfg->ents[i].type->unregister(vimc->ent_devs[i]);
 169}
 170
 171static int vimc_add_subdevs(struct vimc_device *vimc)
 172{
 173        unsigned int i;
 174
 175        for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
 176                dev_dbg(vimc->mdev.dev, "new entity for %s\n",
 177                        vimc->pipe_cfg->ents[i].name);
 178                vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].type->add(vimc,
 179                                        vimc->pipe_cfg->ents[i].name);
 180                if (IS_ERR(vimc->ent_devs[i])) {
 181                        int err = PTR_ERR(vimc->ent_devs[i]);
 182
 183                        dev_err(vimc->mdev.dev, "adding entity %s failed (%d)\n",
 184                                vimc->pipe_cfg->ents[i].name, err);
 185                        vimc->ent_devs[i] = NULL;
 186                        vimc_unregister_subdevs(vimc);
 187                        vimc_release_subdevs(vimc);
 188                        return err;
 189                }
 190        }
 191        return 0;
 192}
 193
 194static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
 195{
 196        struct vimc_device *vimc =
 197                container_of(v4l2_dev, struct vimc_device, v4l2_dev);
 198
 199        vimc_release_subdevs(vimc);
 200        media_device_cleanup(&vimc->mdev);
 201        kfree(vimc->ent_devs);
 202        kfree(vimc);
 203}
 204
 205static int vimc_register_devices(struct vimc_device *vimc)
 206{
 207        int ret;
 208
 209        /* Register the v4l2 struct */
 210        ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
 211        if (ret) {
 212                dev_err(vimc->mdev.dev,
 213                        "v4l2 device register failed (err=%d)\n", ret);
 214                return ret;
 215        }
 216        /* allocate ent_devs */
 217        vimc->ent_devs = kcalloc(vimc->pipe_cfg->num_ents,
 218                                 sizeof(*vimc->ent_devs), GFP_KERNEL);
 219        if (!vimc->ent_devs) {
 220                ret = -ENOMEM;
 221                goto err_v4l2_unregister;
 222        }
 223
 224        /* Invoke entity config hooks to initialize and register subdevs */
 225        ret = vimc_add_subdevs(vimc);
 226        if (ret)
 227                goto err_free_ent_devs;
 228
 229        /* Initialize links */
 230        ret = vimc_create_links(vimc);
 231        if (ret)
 232                goto err_rm_subdevs;
 233
 234        /* Register the media device */
 235        ret = media_device_register(&vimc->mdev);
 236        if (ret) {
 237                dev_err(vimc->mdev.dev,
 238                        "media device register failed (err=%d)\n", ret);
 239                goto err_rm_subdevs;
 240        }
 241
 242        /* Expose all subdev's nodes*/
 243        ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev);
 244        if (ret) {
 245                dev_err(vimc->mdev.dev,
 246                        "vimc subdev nodes registration failed (err=%d)\n",
 247                        ret);
 248                goto err_mdev_unregister;
 249        }
 250
 251        return 0;
 252
 253err_mdev_unregister:
 254        media_device_unregister(&vimc->mdev);
 255err_rm_subdevs:
 256        vimc_unregister_subdevs(vimc);
 257        vimc_release_subdevs(vimc);
 258err_free_ent_devs:
 259        kfree(vimc->ent_devs);
 260err_v4l2_unregister:
 261        v4l2_device_unregister(&vimc->v4l2_dev);
 262
 263        return ret;
 264}
 265
 266static int vimc_probe(struct platform_device *pdev)
 267{
 268        const struct font_desc *font = find_font("VGA8x16");
 269        struct vimc_device *vimc;
 270        int ret;
 271
 272        dev_dbg(&pdev->dev, "probe");
 273
 274        if (!font) {
 275                dev_err(&pdev->dev, "could not find font\n");
 276                return -ENODEV;
 277        }
 278
 279        tpg_set_font(font->data);
 280
 281        vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
 282        if (!vimc)
 283                return -ENOMEM;
 284
 285        vimc->pipe_cfg = &pipe_cfg;
 286
 287        /* Link the media device within the v4l2_device */
 288        vimc->v4l2_dev.mdev = &vimc->mdev;
 289
 290        /* Initialize media device */
 291        strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
 292                sizeof(vimc->mdev.model));
 293        snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
 294                 "platform:%s", VIMC_PDEV_NAME);
 295        vimc->mdev.dev = &pdev->dev;
 296        media_device_init(&vimc->mdev);
 297
 298        ret = vimc_register_devices(vimc);
 299        if (ret) {
 300                media_device_cleanup(&vimc->mdev);
 301                kfree(vimc);
 302                return ret;
 303        }
 304        /*
 305         * the release cb is set only after successful registration.
 306         * if the registration fails, we release directly from probe
 307         */
 308
 309        vimc->v4l2_dev.release = vimc_v4l2_dev_release;
 310        platform_set_drvdata(pdev, vimc);
 311        return 0;
 312}
 313
 314static int vimc_remove(struct platform_device *pdev)
 315{
 316        struct vimc_device *vimc = platform_get_drvdata(pdev);
 317
 318        dev_dbg(&pdev->dev, "remove");
 319
 320        vimc_unregister_subdevs(vimc);
 321        media_device_unregister(&vimc->mdev);
 322        v4l2_device_unregister(&vimc->v4l2_dev);
 323        v4l2_device_put(&vimc->v4l2_dev);
 324
 325        return 0;
 326}
 327
 328static void vimc_dev_release(struct device *dev)
 329{
 330}
 331
 332static struct platform_device vimc_pdev = {
 333        .name = VIMC_PDEV_NAME,
 334        .dev.release = vimc_dev_release,
 335};
 336
 337static struct platform_driver vimc_pdrv = {
 338        .probe          = vimc_probe,
 339        .remove         = vimc_remove,
 340        .driver         = {
 341                .name   = VIMC_PDEV_NAME,
 342        },
 343};
 344
 345static int __init vimc_init(void)
 346{
 347        int ret;
 348
 349        ret = platform_device_register(&vimc_pdev);
 350        if (ret) {
 351                dev_err(&vimc_pdev.dev,
 352                        "platform device registration failed (err=%d)\n", ret);
 353                return ret;
 354        }
 355
 356        ret = platform_driver_register(&vimc_pdrv);
 357        if (ret) {
 358                dev_err(&vimc_pdev.dev,
 359                        "platform driver registration failed (err=%d)\n", ret);
 360                platform_driver_unregister(&vimc_pdrv);
 361                return ret;
 362        }
 363
 364        return 0;
 365}
 366
 367static void __exit vimc_exit(void)
 368{
 369        platform_driver_unregister(&vimc_pdrv);
 370
 371        platform_device_unregister(&vimc_pdev);
 372}
 373
 374module_init(vimc_init);
 375module_exit(vimc_exit);
 376
 377MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
 378MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
 379MODULE_LICENSE("GPL");
 380