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