![Mauro Carvalho Chehab](/assets/img/avatar_default.png)
-----BEGIN PGP SIGNATURE----- iQFSBAABCAA8FiEEq68RxlopcLEwq+PEeb4+QwBBGIYFAlz8fAYeHHRvcnZhbGRz QGxpbnV4LWZvdW5kYXRpb24ub3JnAAoJEHm+PkMAQRiG1asH/3ySguxqtqL1MCBa 4/SZ37PHeWKMerfX6ZyJdgEqK3B+PWlmuLiOMNK5h2bPLzeQQQAmHU/mfKmpXqgB dHwUbG9yNnyUtTfsfRqAnCA6vpuw9Yb1oIzTCVQrgJLSWD0j7scBBvmzYqguOkto ThwigLUq3AILr8EfR4rh+GM+5Dn9OTEFAxwil9fPHQo7QoczwZxpURhScT6Co9TB DqLA3fvXbBvLs/CZy/S5vKM9hKzC+p39ApFTURvFPrelUVnythAM0dPDJg3pIn5u g+/+gDxDFa+7ANxvxO2ng1sJPDqJMeY/xmjJYlYyLpA33B7zLNk2vDHhAP06VTtr XCMhQ9s= =cb80 -----END PGP SIGNATURE----- Merge tag 'v5.2-rc4' into media/master There are some conflicts due to SPDX changes. We also have more patches being merged via media tree touching them. So, let's merge back from upstream and address those. Linux 5.2-rc4 * tag 'v5.2-rc4': (767 commits) Linux 5.2-rc4 MAINTAINERS: Karthikeyan Ramasubramanian is MIA i2c: xiic: Add max_read_len quirk lockref: Limit number of cmpxchg loop retries uaccess: add noop untagged_addr definition x86/insn-eval: Fix use-after-free access to LDT entry kbuild: use more portable 'command -v' for cc-cross-prefix s390/unwind: correct stack switching during unwind block, bfq: add weight symlink to the bfq.weight cgroup parameter cgroup: let a symlink too be created with a cftype file drm/nouveau/secboot/gp10[2467]: support newer FW to fix SEC2 failures on some boards drm/nouveau/secboot: enable loading of versioned LS PMU/SEC2 ACR msgqueue FW drm/nouveau/secboot: split out FW version-specific LS function pointers drm/nouveau/secboot: pass max supported FW version to LS load funcs drm/nouveau/core: support versioned firmware loading drm/nouveau/core: pass subdev into nvkm_firmware_get, rather than device block: free sched's request pool in blk_cleanup_queue pktgen: do not sleep with the thread lock held. net: mvpp2: Use strscpy to handle stat strings net: rds: fix memory leak in rds_ib_flush_mr_pool ... Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
396 lines
9.2 KiB
C
396 lines
9.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* vimc-core.c Virtual Media Controller Driver
|
|
*
|
|
* Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
|
|
*/
|
|
|
|
#include <linux/component.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <media/media-device.h>
|
|
#include <media/v4l2-device.h>
|
|
|
|
#include "vimc-common.h"
|
|
|
|
#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
|
|
|
|
#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
|
|
.src_ent = src, \
|
|
.src_pad = srcpad, \
|
|
.sink_ent = sink, \
|
|
.sink_pad = sinkpad, \
|
|
.flags = link_flags, \
|
|
}
|
|
|
|
struct vimc_device {
|
|
/* The platform device */
|
|
struct platform_device pdev;
|
|
|
|
/* The pipeline configuration */
|
|
const struct vimc_pipeline_config *pipe_cfg;
|
|
|
|
/* The Associated media_device parent */
|
|
struct media_device mdev;
|
|
|
|
/* Internal v4l2 parent device*/
|
|
struct v4l2_device v4l2_dev;
|
|
|
|
/* Subdevices */
|
|
struct platform_device **subdevs;
|
|
};
|
|
|
|
/* Structure which describes individual configuration for each entity */
|
|
struct vimc_ent_config {
|
|
const char *name;
|
|
const char *drv;
|
|
};
|
|
|
|
/* Structure which describes links between entities */
|
|
struct vimc_ent_link {
|
|
unsigned int src_ent;
|
|
u16 src_pad;
|
|
unsigned int sink_ent;
|
|
u16 sink_pad;
|
|
u32 flags;
|
|
};
|
|
|
|
/* Structure which describes the whole topology */
|
|
struct vimc_pipeline_config {
|
|
const struct vimc_ent_config *ents;
|
|
size_t num_ents;
|
|
const struct vimc_ent_link *links;
|
|
size_t num_links;
|
|
};
|
|
|
|
/* --------------------------------------------------------------------------
|
|
* Topology Configuration
|
|
*/
|
|
|
|
static const struct vimc_ent_config ent_config[] = {
|
|
{
|
|
.name = "Sensor A",
|
|
.drv = "vimc-sensor",
|
|
},
|
|
{
|
|
.name = "Sensor B",
|
|
.drv = "vimc-sensor",
|
|
},
|
|
{
|
|
.name = "Debayer A",
|
|
.drv = "vimc-debayer",
|
|
},
|
|
{
|
|
.name = "Debayer B",
|
|
.drv = "vimc-debayer",
|
|
},
|
|
{
|
|
.name = "Raw Capture 0",
|
|
.drv = "vimc-capture",
|
|
},
|
|
{
|
|
.name = "Raw Capture 1",
|
|
.drv = "vimc-capture",
|
|
},
|
|
{
|
|
.name = "RGB/YUV Input",
|
|
/* TODO: change this to vimc-input when it is implemented */
|
|
.drv = "vimc-sensor",
|
|
},
|
|
{
|
|
.name = "Scaler",
|
|
.drv = "vimc-scaler",
|
|
},
|
|
{
|
|
.name = "RGB/YUV Capture",
|
|
.drv = "vimc-capture",
|
|
},
|
|
};
|
|
|
|
static const struct vimc_ent_link ent_links[] = {
|
|
/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
|
|
VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
|
|
/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
|
|
VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
|
|
/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
|
|
VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
|
|
/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
|
|
VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
|
|
/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
|
|
VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
|
|
/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
|
|
VIMC_ENT_LINK(3, 1, 7, 0, 0),
|
|
/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
|
|
VIMC_ENT_LINK(6, 0, 7, 0, 0),
|
|
/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
|
|
VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
|
|
};
|
|
|
|
static const struct vimc_pipeline_config pipe_cfg = {
|
|
.ents = ent_config,
|
|
.num_ents = ARRAY_SIZE(ent_config),
|
|
.links = ent_links,
|
|
.num_links = ARRAY_SIZE(ent_links)
|
|
};
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static int vimc_create_links(struct vimc_device *vimc)
|
|
{
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
/* Initialize the links between entities */
|
|
for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
|
|
const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
|
|
/*
|
|
* TODO: Check another way of retrieving ved struct without
|
|
* relying on platform_get_drvdata
|
|
*/
|
|
struct vimc_ent_device *ved_src =
|
|
platform_get_drvdata(vimc->subdevs[link->src_ent]);
|
|
struct vimc_ent_device *ved_sink =
|
|
platform_get_drvdata(vimc->subdevs[link->sink_ent]);
|
|
|
|
ret = media_create_pad_link(ved_src->ent, link->src_pad,
|
|
ved_sink->ent, link->sink_pad,
|
|
link->flags);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vimc_comp_bind(struct device *master)
|
|
{
|
|
struct vimc_device *vimc = container_of(to_platform_device(master),
|
|
struct vimc_device, pdev);
|
|
int ret;
|
|
|
|
dev_dbg(master, "bind");
|
|
|
|
/* Register the v4l2 struct */
|
|
ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
|
|
if (ret) {
|
|
dev_err(vimc->mdev.dev,
|
|
"v4l2 device register failed (err=%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Bind subdevices */
|
|
ret = component_bind_all(master, &vimc->v4l2_dev);
|
|
if (ret)
|
|
goto err_v4l2_unregister;
|
|
|
|
/* Initialize links */
|
|
ret = vimc_create_links(vimc);
|
|
if (ret)
|
|
goto err_comp_unbind_all;
|
|
|
|
/* Register the media device */
|
|
ret = media_device_register(&vimc->mdev);
|
|
if (ret) {
|
|
dev_err(vimc->mdev.dev,
|
|
"media device register failed (err=%d)\n", ret);
|
|
goto err_comp_unbind_all;
|
|
}
|
|
|
|
/* Expose all subdev's nodes*/
|
|
ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev);
|
|
if (ret) {
|
|
dev_err(vimc->mdev.dev,
|
|
"vimc subdev nodes registration failed (err=%d)\n",
|
|
ret);
|
|
goto err_mdev_unregister;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_mdev_unregister:
|
|
media_device_unregister(&vimc->mdev);
|
|
media_device_cleanup(&vimc->mdev);
|
|
err_comp_unbind_all:
|
|
component_unbind_all(master, NULL);
|
|
err_v4l2_unregister:
|
|
v4l2_device_unregister(&vimc->v4l2_dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void vimc_comp_unbind(struct device *master)
|
|
{
|
|
struct vimc_device *vimc = container_of(to_platform_device(master),
|
|
struct vimc_device, pdev);
|
|
|
|
dev_dbg(master, "unbind");
|
|
|
|
media_device_unregister(&vimc->mdev);
|
|
media_device_cleanup(&vimc->mdev);
|
|
component_unbind_all(master, NULL);
|
|
v4l2_device_unregister(&vimc->v4l2_dev);
|
|
}
|
|
|
|
static int vimc_comp_compare(struct device *comp, void *data)
|
|
{
|
|
return comp == data;
|
|
}
|
|
|
|
static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
|
|
{
|
|
struct component_match *match = NULL;
|
|
struct vimc_platform_data pdata;
|
|
int i;
|
|
|
|
for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
|
|
dev_dbg(&vimc->pdev.dev, "new pdev for %s\n",
|
|
vimc->pipe_cfg->ents[i].drv);
|
|
|
|
strscpy(pdata.entity_name, vimc->pipe_cfg->ents[i].name,
|
|
sizeof(pdata.entity_name));
|
|
|
|
vimc->subdevs[i] = platform_device_register_data(&vimc->pdev.dev,
|
|
vimc->pipe_cfg->ents[i].drv,
|
|
PLATFORM_DEVID_AUTO,
|
|
&pdata,
|
|
sizeof(pdata));
|
|
if (IS_ERR(vimc->subdevs[i])) {
|
|
match = ERR_CAST(vimc->subdevs[i]);
|
|
while (--i >= 0)
|
|
platform_device_unregister(vimc->subdevs[i]);
|
|
|
|
return match;
|
|
}
|
|
|
|
component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare,
|
|
&vimc->subdevs[i]->dev);
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
static void vimc_rm_subdevs(struct vimc_device *vimc)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
|
|
platform_device_unregister(vimc->subdevs[i]);
|
|
}
|
|
|
|
static const struct component_master_ops vimc_comp_ops = {
|
|
.bind = vimc_comp_bind,
|
|
.unbind = vimc_comp_unbind,
|
|
};
|
|
|
|
static int vimc_probe(struct platform_device *pdev)
|
|
{
|
|
struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
|
|
struct component_match *match = NULL;
|
|
int ret;
|
|
|
|
dev_dbg(&pdev->dev, "probe");
|
|
|
|
memset(&vimc->mdev, 0, sizeof(vimc->mdev));
|
|
|
|
/* Create platform_device for each entity in the topology*/
|
|
vimc->subdevs = devm_kcalloc(&vimc->pdev.dev, vimc->pipe_cfg->num_ents,
|
|
sizeof(*vimc->subdevs), GFP_KERNEL);
|
|
if (!vimc->subdevs)
|
|
return -ENOMEM;
|
|
|
|
match = vimc_add_subdevs(vimc);
|
|
if (IS_ERR(match))
|
|
return PTR_ERR(match);
|
|
|
|
/* Link the media device within the v4l2_device */
|
|
vimc->v4l2_dev.mdev = &vimc->mdev;
|
|
|
|
/* Initialize media device */
|
|
strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
|
|
sizeof(vimc->mdev.model));
|
|
snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
|
|
"platform:%s", VIMC_PDEV_NAME);
|
|
vimc->mdev.dev = &pdev->dev;
|
|
media_device_init(&vimc->mdev);
|
|
|
|
/* Add self to the component system */
|
|
ret = component_master_add_with_match(&pdev->dev, &vimc_comp_ops,
|
|
match);
|
|
if (ret) {
|
|
media_device_cleanup(&vimc->mdev);
|
|
vimc_rm_subdevs(vimc);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vimc_remove(struct platform_device *pdev)
|
|
{
|
|
struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
|
|
|
|
dev_dbg(&pdev->dev, "remove");
|
|
|
|
component_master_del(&pdev->dev, &vimc_comp_ops);
|
|
vimc_rm_subdevs(vimc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vimc_dev_release(struct device *dev)
|
|
{
|
|
}
|
|
|
|
static struct vimc_device vimc_dev = {
|
|
.pipe_cfg = &pipe_cfg,
|
|
.pdev = {
|
|
.name = VIMC_PDEV_NAME,
|
|
.dev.release = vimc_dev_release,
|
|
}
|
|
};
|
|
|
|
static struct platform_driver vimc_pdrv = {
|
|
.probe = vimc_probe,
|
|
.remove = vimc_remove,
|
|
.driver = {
|
|
.name = VIMC_PDEV_NAME,
|
|
},
|
|
};
|
|
|
|
static int __init vimc_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = platform_device_register(&vimc_dev.pdev);
|
|
if (ret) {
|
|
dev_err(&vimc_dev.pdev.dev,
|
|
"platform device registration failed (err=%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = platform_driver_register(&vimc_pdrv);
|
|
if (ret) {
|
|
dev_err(&vimc_dev.pdev.dev,
|
|
"platform driver registration failed (err=%d)\n", ret);
|
|
platform_driver_unregister(&vimc_pdrv);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit vimc_exit(void)
|
|
{
|
|
platform_driver_unregister(&vimc_pdrv);
|
|
|
|
platform_device_unregister(&vimc_dev.pdev);
|
|
}
|
|
|
|
module_init(vimc_init);
|
|
module_exit(vimc_exit);
|
|
|
|
MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
|
|
MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
|
|
MODULE_LICENSE("GPL");
|