From 36909b552d0ea960732bc1450186f3e738bc41fc Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 5 Sep 2019 15:05:32 -0300 Subject: media: i2c: mt9m001: make array init_regs static, makes object smaller Don't populate the array init_regs on the stack but instead make it static. Makes the object code smaller by 57 bytes. Before: text data bss dec hex filename 15935 3600 128 19663 4ccf drivers/media/i2c/mt9m001.o After: text data bss dec hex filename 15782 3696 128 19606 4c96 drivers/media/i2c/mt9m001.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9m001.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 5613072908ac..210ea76adb53 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -167,7 +167,7 @@ static int multi_reg_write(struct i2c_client *client, static int mt9m001_init(struct i2c_client *client) { - const struct mt9m001_reg init_regs[] = { + static const struct mt9m001_reg init_regs[] = { /* * Issue a soft reset. This returns all registers to their * default values. -- cgit v1.2.3 From 713f871b30a66dc4daff4d17b760c9916aaaf2e1 Mon Sep 17 00:00:00 2001 From: zhengbin Date: Sun, 18 Aug 2019 22:51:30 -0300 Subject: media: mc-device.c: fix memleak in media_device_register_entity In media_device_register_entity, if media_graph_walk_init fails, need to free the previously memory. Reported-by: Hulk Robot Signed-off-by: zhengbin Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/mc/mc-device.c | 65 ++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 32 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c index e19df5165e78..da8088351135 100644 --- a/drivers/media/mc/mc-device.c +++ b/drivers/media/mc/mc-device.c @@ -575,6 +575,38 @@ static void media_device_release(struct media_devnode *devnode) dev_dbg(devnode->parent, "Media device released\n"); } +static void __media_device_unregister_entity(struct media_entity *entity) +{ + struct media_device *mdev = entity->graph_obj.mdev; + struct media_link *link, *tmp; + struct media_interface *intf; + unsigned int i; + + ida_free(&mdev->entity_internal_idx, entity->internal_idx); + + /* Remove all interface links pointing to this entity */ + list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) { + list_for_each_entry_safe(link, tmp, &intf->links, list) { + if (link->entity == entity) + __media_remove_intf_link(link); + } + } + + /* Remove all data links that belong to this entity */ + __media_entity_remove_links(entity); + + /* Remove all pads that belong to this entity */ + for (i = 0; i < entity->num_pads; i++) + media_gobj_destroy(&entity->pads[i].graph_obj); + + /* Remove the entity */ + media_gobj_destroy(&entity->graph_obj); + + /* invoke entity_notify callbacks to handle entity removal?? */ + + entity->graph_obj.mdev = NULL; +} + /** * media_device_register_entity - Register an entity with a media device * @mdev: The media device @@ -632,6 +664,7 @@ int __must_check media_device_register_entity(struct media_device *mdev, */ ret = media_graph_walk_init(&new, mdev); if (ret) { + __media_device_unregister_entity(entity); mutex_unlock(&mdev->graph_mutex); return ret; } @@ -644,38 +677,6 @@ int __must_check media_device_register_entity(struct media_device *mdev, } EXPORT_SYMBOL_GPL(media_device_register_entity); -static void __media_device_unregister_entity(struct media_entity *entity) -{ - struct media_device *mdev = entity->graph_obj.mdev; - struct media_link *link, *tmp; - struct media_interface *intf; - unsigned int i; - - ida_free(&mdev->entity_internal_idx, entity->internal_idx); - - /* Remove all interface links pointing to this entity */ - list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) { - list_for_each_entry_safe(link, tmp, &intf->links, list) { - if (link->entity == entity) - __media_remove_intf_link(link); - } - } - - /* Remove all data links that belong to this entity */ - __media_entity_remove_links(entity); - - /* Remove all pads that belong to this entity */ - for (i = 0; i < entity->num_pads; i++) - media_gobj_destroy(&entity->pads[i].graph_obj); - - /* Remove the entity */ - media_gobj_destroy(&entity->graph_obj); - - /* invoke entity_notify callbacks to handle entity removal?? */ - - entity->graph_obj.mdev = NULL; -} - void media_device_unregister_entity(struct media_entity *entity) { struct media_device *mdev = entity->graph_obj.mdev; -- cgit v1.2.3 From f13d5f3619599d257b4bcb941245085f5d118af8 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Tue, 17 Sep 2019 13:35:08 -0300 Subject: media: vimc: Collapse component structure into a single monolithic driver vimc uses Component API to split the driver into functional components. The real hardware resembles a monolith structure than component and component structure added a level of complexity making it hard to maintain without adding any real benefit. The sensor is one vimc component that would makes sense to be a separate module to closely align with the real hardware. It would be easier to collapse vimc into single monolithic driver first and then split the sensor off as a separate module. Collapse it into a single monolithic driver removing the Component API. This patch removes the component API and makes minimal changes to the code base preserving the functional division of the code structure. Preserving the functional structure allows us to split the sensor off as a separate module in the future. Major design elements in this change are: - Use existing struct vimc_ent_config and struct vimc_pipeline_config to drive the initialization of the functional components. - Make vimc_device and vimc_ent_config global by moving them to vimc-common.h - Add two new hooks add and rm to initialize and register, unregister and free subdevs. - All component API is now gone and bind and unbind hooks are modified to do "add" and "rm" with minimal changes to just add and rm subdevs. - vimc-core's bind and unbind are now register and unregister. - Add a new field to vimc_device structure for saving the pointers to struct vimc_ent_device(s) subdevs create in their "add" hooks. These get used to create links and removing the subdevs. vimc-core allocates this array which sized to number of entries in the topology defined in the vimc_pipeline_config structure. - vimc-core invokes "add" hooks from its vimc_register_devices(). The "add" hooks remain the same and register subdevs. They don't create platform devices of their own and use vimc's pdev.dev as their reference device. Each "add" hook returns pointer to its struct vimc_ent_device. This is saved in the vimc_device ent_devs array. - vimc-core invokes "rm" hooks from its unregister to unregister subdevs and cleanup. - vimc-core invokes "add" and "rm" hooks with pointer to struct vimc_device and the corresponding vimc_ent_device saved in the ent_devs. The following configure and stream test works on all devices. media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]' media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]' media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]' media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]' v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440 v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81 v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video1 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video2 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video3 Signed-off-by: Shuah Khan Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/Makefile | 7 +- drivers/media/platform/vimc/vimc-capture.c | 81 ++--------- drivers/media/platform/vimc/vimc-common.h | 54 +++++++ drivers/media/platform/vimc/vimc-core.c | 216 +++++++++++----------------- drivers/media/platform/vimc/vimc-debayer.c | 73 ++-------- drivers/media/platform/vimc/vimc-scaler.c | 75 ++-------- drivers/media/platform/vimc/vimc-sensor.c | 73 ++-------- drivers/media/platform/vimc/vimc-streamer.c | 1 - 8 files changed, 195 insertions(+), 385 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile index 96d06f030c31..a53b2b532e9f 100644 --- a/drivers/media/platform/vimc/Makefile +++ b/drivers/media/platform/vimc/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -vimc-y := vimc-core.o vimc-common.o vimc-streamer.o +vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \ + vimc-debayer.o vimc-scaler.o vimc-sensor.o + +obj-$(CONFIG_VIDEO_VIMC) += vimc.o -obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc-capture.o vimc-debayer.o \ - vimc-scaler.o vimc-sensor.o diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 1d56b91830ba..602f80323031 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -5,10 +5,6 @@ * Copyright (C) 2015-2017 Helen Koike */ -#include -#include -#include -#include #include #include #include @@ -16,8 +12,6 @@ #include "vimc-common.h" #include "vimc-streamer.h" -#define VIMC_CAP_DRV_NAME "vimc-capture" - struct vimc_cap_device { struct vimc_ent_device ved; struct video_device vdev; @@ -340,13 +334,11 @@ static void vimc_cap_release(struct video_device *vdev) kfree(vcap); } -static void vimc_cap_comp_unbind(struct device *comp, struct device *master, - void *master_data) +void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) { - struct vimc_ent_device *ved = dev_get_drvdata(comp); - struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, - ved); + struct vimc_cap_device *vcap; + vcap = container_of(ved, struct vimc_cap_device, ved); vb2_queue_release(&vcap->queue); media_entity_cleanup(ved->ent); video_unregister_device(&vcap->vdev); @@ -391,11 +383,10 @@ static void *vimc_cap_process_frame(struct vimc_ent_device *ved, return NULL; } -static int vimc_cap_comp_bind(struct device *comp, struct device *master, - void *master_data) +struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, + const char *vcfg_name) { - struct v4l2_device *v4l2_dev = master_data; - struct vimc_platform_data *pdata = comp->platform_data; + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; const struct vimc_pix_map *vpix; struct vimc_cap_device *vcap; struct video_device *vdev; @@ -405,7 +396,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master, /* Allocate the vimc_cap_device struct */ vcap = kzalloc(sizeof(*vcap), GFP_KERNEL); if (!vcap) - return -ENOMEM; + return NULL; /* Allocate the pads */ vcap->ved.pads = @@ -416,7 +407,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master, } /* Initialize the media entity */ - vcap->vdev.entity.name = pdata->entity_name; + vcap->vdev.entity.name = vcfg_name; vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; ret = media_entity_pads_init(&vcap->vdev.entity, 1, vcap->ved.pads); @@ -440,8 +431,8 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master, ret = vb2_queue_init(q); if (ret) { - dev_err(comp, "%s: vb2 queue init failed (err=%d)\n", - pdata->entity_name, ret); + dev_err(&vimc->pdev.dev, "%s: vb2 queue init failed (err=%d)\n", + vcfg_name, ret); goto err_clean_m_ent; } @@ -460,8 +451,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master, vcap->ved.ent = &vcap->vdev.entity; vcap->ved.process_frame = vimc_cap_process_frame; vcap->ved.vdev_get_format = vimc_cap_get_format; - dev_set_drvdata(comp, &vcap->ved); - vcap->dev = comp; + vcap->dev = &vimc->pdev.dev; /* Initialize the video_device struct */ vdev = &vcap->vdev; @@ -474,18 +464,18 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master, vdev->queue = q; vdev->v4l2_dev = v4l2_dev; vdev->vfl_dir = VFL_DIR_RX; - strscpy(vdev->name, pdata->entity_name, sizeof(vdev->name)); + strscpy(vdev->name, vcfg_name, sizeof(vdev->name)); video_set_drvdata(vdev, &vcap->ved); /* Register the video_device with the v4l2 and the media framework */ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); if (ret) { - dev_err(comp, "%s: video register failed (err=%d)\n", + dev_err(&vimc->pdev.dev, "%s: video register failed (err=%d)\n", vcap->vdev.name, ret); goto err_release_queue; } - return 0; + return &vcap->ved; err_release_queue: vb2_queue_release(q); @@ -496,46 +486,5 @@ err_clean_pads: err_free_vcap: kfree(vcap); - return ret; -} - -static const struct component_ops vimc_cap_comp_ops = { - .bind = vimc_cap_comp_bind, - .unbind = vimc_cap_comp_unbind, -}; - -static int vimc_cap_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &vimc_cap_comp_ops); -} - -static int vimc_cap_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &vimc_cap_comp_ops); - - return 0; + return NULL; } - -static const struct platform_device_id vimc_cap_driver_ids[] = { - { - .name = VIMC_CAP_DRV_NAME, - }, - { } -}; - -static struct platform_driver vimc_cap_pdrv = { - .probe = vimc_cap_probe, - .remove = vimc_cap_remove, - .id_table = vimc_cap_driver_ids, - .driver = { - .name = VIMC_CAP_DRV_NAME, - }, -}; - -module_platform_driver(vimc_cap_pdrv); - -MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Capture"); -MODULE_AUTHOR("Helen Mae Koike Fornazier "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 9c2e0e216c6b..d7aaf31175bc 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -8,6 +8,7 @@ #ifndef _VIMC_COMMON_H_ #define _VIMC_COMMON_H_ +#include #include #include #include @@ -111,6 +112,59 @@ struct vimc_ent_device { struct v4l2_pix_format *fmt); }; +/** + * struct vimc_device - main device for vimc driver + * + * @pdev pointer to the platform device + * @pipe_cfg pointer to the vimc pipeline configuration structure + * @ent_devs array of vimc_ent_device pointers + * @mdev the associated media_device parent + * @v4l2_dev Internal v4l2 parent device + */ +struct vimc_device { + struct platform_device pdev; + const struct vimc_pipeline_config *pipe_cfg; + struct vimc_ent_device **ent_devs; + struct media_device mdev; + struct v4l2_device v4l2_dev; +}; + +/** + * struct vimc_ent_config Structure which describes individual + * configuration for each entity + * + * @name entity name + * @ved pointer to vimc_ent_device (a node in the + * topology) + * @add subdev add hook - initializes and registers + * subdev called from vimc-core + * @rm subdev rm hook - unregisters and frees + * subdev called from vimc-core + */ +struct vimc_ent_config { + const char *name; + struct vimc_ent_device *(*add)(struct vimc_device *vimc, + const char *vcfg_name); + void (*rm)(struct vimc_device *vimc, struct vimc_ent_device *ved); +}; + +/* prototypes for vimc_ent_config add and rm hooks */ +struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); + +struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); + +struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); + +struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, + const char *vcfg_name); +void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); + /** * vimc_pads_init - initialize pads * diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index 571c55aa0e16..6e3e5c91ae39 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -5,7 +5,6 @@ * Copyright (C) 2015-2017 Helen Koike */ -#include #include #include #include @@ -24,29 +23,6 @@ .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; @@ -68,43 +44,52 @@ struct vimc_pipeline_config { * Topology Configuration */ -static const struct vimc_ent_config ent_config[] = { +static struct vimc_ent_config ent_config[] = { { .name = "Sensor A", - .drv = "vimc-sensor", + .add = vimc_sen_add, + .rm = vimc_sen_rm, }, { .name = "Sensor B", - .drv = "vimc-sensor", + .add = vimc_sen_add, + .rm = vimc_sen_rm, }, { .name = "Debayer A", - .drv = "vimc-debayer", + .add = vimc_deb_add, + .rm = vimc_deb_rm, }, { .name = "Debayer B", - .drv = "vimc-debayer", + .add = vimc_deb_add, + .rm = vimc_deb_rm, }, { .name = "Raw Capture 0", - .drv = "vimc-capture", + .add = vimc_cap_add, + .rm = vimc_cap_rm, }, { .name = "Raw Capture 1", - .drv = "vimc-capture", + .add = vimc_cap_add, + .rm = vimc_cap_rm, }, { - .name = "RGB/YUV Input", /* TODO: change this to vimc-input when it is implemented */ - .drv = "vimc-sensor", + .name = "RGB/YUV Input", + .add = vimc_sen_add, + .rm = vimc_sen_rm, }, { .name = "Scaler", - .drv = "vimc-scaler", + .add = vimc_sca_add, + .rm = vimc_sca_rm, }, { .name = "RGB/YUV Capture", - .drv = "vimc-capture", + .add = vimc_cap_add, + .rm = vimc_cap_rm, }, }; @@ -127,7 +112,7 @@ static const struct vimc_ent_link ent_links[] = { VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), }; -static const struct vimc_pipeline_config pipe_cfg = { +static struct vimc_pipeline_config pipe_cfg = { .ents = ent_config, .num_ents = ARRAY_SIZE(ent_config), .links = ent_links, @@ -136,6 +121,14 @@ static const struct vimc_pipeline_config pipe_cfg = { /* -------------------------------------------------------------------------- */ +static void vimc_rm_links(struct vimc_device *vimc) +{ + unsigned int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + media_entity_remove_links(vimc->ent_devs[i]->ent); +} + static int vimc_create_links(struct vimc_device *vimc) { unsigned int i; @@ -144,32 +137,58 @@ static int vimc_create_links(struct vimc_device *vimc) /* 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]); + vimc->ent_devs[link->src_ent]; struct vimc_ent_device *ved_sink = - platform_get_drvdata(vimc->subdevs[link->sink_ent]); + vimc->ent_devs[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; + goto err_rm_links; } return 0; + +err_rm_links: + vimc_rm_links(vimc); + return ret; } -static int vimc_comp_bind(struct device *master) +static int vimc_add_subdevs(struct vimc_device *vimc) { - struct vimc_device *vimc = container_of(to_platform_device(master), - struct vimc_device, pdev); - int ret; + unsigned int i; + struct vimc_ent_device *ved; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { + dev_dbg(&vimc->pdev.dev, "new entity for %s\n", + vimc->pipe_cfg->ents[i].name); + ved = vimc->pipe_cfg->ents[i].add(vimc, + vimc->pipe_cfg->ents[i].name); + if (!ved) { + dev_err(&vimc->pdev.dev, "add new entity for %s\n", + vimc->pipe_cfg->ents[i].name); + return -EINVAL; + } + vimc->ent_devs[i] = ved; + } + return 0; +} + +static void vimc_rm_subdevs(struct vimc_device *vimc) +{ + unsigned int i; - dev_dbg(master, "bind"); + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + if (vimc->ent_devs[i]) + vimc->pipe_cfg->ents[i].rm(vimc, vimc->ent_devs[i]); +} + +static int vimc_register_devices(struct vimc_device *vimc) +{ + int ret; /* Register the v4l2 struct */ ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev); @@ -179,22 +198,30 @@ static int vimc_comp_bind(struct device *master) return ret; } - /* Bind subdevices */ - ret = component_bind_all(master, &vimc->v4l2_dev); - if (ret) + /* allocate ent_devs */ + vimc->ent_devs = kmalloc_array(vimc->pipe_cfg->num_ents, + sizeof(*vimc->ent_devs), + GFP_KERNEL); + if (!vimc->ent_devs) goto err_v4l2_unregister; + /* Invoke entity config hooks to initialize and register subdevs */ + ret = vimc_add_subdevs(vimc); + if (ret) + /* remove sundevs that got added */ + goto err_rm_subdevs; + /* Initialize links */ ret = vimc_create_links(vimc); if (ret) - goto err_comp_unbind_all; + goto err_rm_subdevs; /* 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; + goto err_rm_subdevs; } /* Expose all subdev's nodes*/ @@ -211,98 +238,32 @@ static int vimc_comp_bind(struct device *master) err_mdev_unregister: media_device_unregister(&vimc->mdev); media_device_cleanup(&vimc->mdev); -err_comp_unbind_all: - component_unbind_all(master, NULL); +err_rm_subdevs: + vimc_rm_subdevs(vimc); + kfree(vimc->ent_devs); err_v4l2_unregister: v4l2_device_unregister(&vimc->v4l2_dev); return ret; } -static void vimc_comp_unbind(struct device *master) +static void vimc_unregister(struct vimc_device *vimc) { - 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); + kfree(vimc->ent_devs); } -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; @@ -314,12 +275,9 @@ static int vimc_probe(struct platform_device *pdev) 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); + ret = vimc_register_devices(vimc); if (ret) { media_device_cleanup(&vimc->mdev); - vimc_rm_subdevs(vimc); return ret; } @@ -332,8 +290,8 @@ static int vimc_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "remove"); - component_master_del(&pdev->dev, &vimc_comp_ops); vimc_rm_subdevs(vimc); + vimc_unregister(vimc); return 0; } diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index b72b8385067b..2c291447807e 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -5,9 +5,7 @@ * Copyright (C) 2015-2017 Helen Koike */ -#include -#include -#include +#include #include #include #include @@ -15,8 +13,6 @@ #include "vimc-common.h" -#define VIMC_DEB_DRV_NAME "vimc-debayer" - static unsigned int deb_mean_win_size = 3; module_param(deb_mean_win_size, uint, 0000); MODULE_PARM_DESC(deb_mean_win_size, " the window size to calculate the mean.\n" @@ -491,44 +487,40 @@ static const struct v4l2_subdev_internal_ops vimc_deb_int_ops = { .release = vimc_deb_release, }; -static void vimc_deb_comp_unbind(struct device *comp, struct device *master, - void *master_data) +void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) { - struct vimc_ent_device *ved = dev_get_drvdata(comp); - struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device, - ved); + struct vimc_deb_device *vdeb; + vdeb = container_of(ved, struct vimc_deb_device, ved); vimc_ent_sd_unregister(ved, &vdeb->sd); } -static int vimc_deb_comp_bind(struct device *comp, struct device *master, - void *master_data) +struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, + const char *vcfg_name) { - struct v4l2_device *v4l2_dev = master_data; - struct vimc_platform_data *pdata = comp->platform_data; + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; struct vimc_deb_device *vdeb; int ret; /* Allocate the vdeb struct */ vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL); if (!vdeb) - return -ENOMEM; + return NULL; /* Initialize ved and sd */ ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, - pdata->entity_name, + vcfg_name, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, (const unsigned long[2]) {MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE}, &vimc_deb_int_ops, &vimc_deb_ops); if (ret) { kfree(vdeb); - return ret; + return NULL; } vdeb->ved.process_frame = vimc_deb_process_frame; - dev_set_drvdata(comp, &vdeb->ved); - vdeb->dev = comp; + vdeb->dev = &vimc->pdev.dev; /* Initialize the frame format */ vdeb->sink_fmt = sink_fmt_default; @@ -541,46 +533,5 @@ static int vimc_deb_comp_bind(struct device *comp, struct device *master, vdeb->src_code = MEDIA_BUS_FMT_RGB888_1X24; vdeb->set_rgb_src = vimc_deb_set_rgb_mbus_fmt_rgb888_1x24; - return 0; + return &vdeb->ved; } - -static const struct component_ops vimc_deb_comp_ops = { - .bind = vimc_deb_comp_bind, - .unbind = vimc_deb_comp_unbind, -}; - -static int vimc_deb_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &vimc_deb_comp_ops); -} - -static int vimc_deb_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &vimc_deb_comp_ops); - - return 0; -} - -static const struct platform_device_id vimc_deb_driver_ids[] = { - { - .name = VIMC_DEB_DRV_NAME, - }, - { } -}; - -static struct platform_driver vimc_deb_pdrv = { - .probe = vimc_deb_probe, - .remove = vimc_deb_remove, - .id_table = vimc_deb_driver_ids, - .driver = { - .name = VIMC_DEB_DRV_NAME, - }, -}; - -module_platform_driver(vimc_deb_pdrv); - -MODULE_DEVICE_TABLE(platform, vimc_deb_driver_ids); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Debayer"); -MODULE_AUTHOR("Helen Mae Koike Fornazier "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index 49ab8d9dd9c9..f72200de2535 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -5,18 +5,13 @@ * Copyright (C) 2015-2017 Helen Koike */ -#include -#include -#include -#include +#include #include #include #include #include "vimc-common.h" -#define VIMC_SCA_DRV_NAME "vimc-scaler" - static unsigned int sca_mult = 3; module_param(sca_mult, uint, 0000); MODULE_PARM_DESC(sca_mult, " the image size multiplier"); @@ -350,89 +345,43 @@ static const struct v4l2_subdev_internal_ops vimc_sca_int_ops = { .release = vimc_sca_release, }; -static void vimc_sca_comp_unbind(struct device *comp, struct device *master, - void *master_data) +void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) { - struct vimc_ent_device *ved = dev_get_drvdata(comp); - struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, - ved); + struct vimc_sca_device *vsca; + vsca = container_of(ved, struct vimc_sca_device, ved); vimc_ent_sd_unregister(ved, &vsca->sd); } - -static int vimc_sca_comp_bind(struct device *comp, struct device *master, - void *master_data) +struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, + const char *vcfg_name) { - struct v4l2_device *v4l2_dev = master_data; - struct vimc_platform_data *pdata = comp->platform_data; + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; struct vimc_sca_device *vsca; int ret; /* Allocate the vsca struct */ vsca = kzalloc(sizeof(*vsca), GFP_KERNEL); if (!vsca) - return -ENOMEM; + return NULL; /* Initialize ved and sd */ ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, - pdata->entity_name, + vcfg_name, MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, (const unsigned long[2]) {MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE}, &vimc_sca_int_ops, &vimc_sca_ops); if (ret) { kfree(vsca); - return ret; + return NULL; } vsca->ved.process_frame = vimc_sca_process_frame; - dev_set_drvdata(comp, &vsca->ved); - vsca->dev = comp; + vsca->dev = &vimc->pdev.dev; /* Initialize the frame format */ vsca->sink_fmt = sink_fmt_default; - return 0; -} - -static const struct component_ops vimc_sca_comp_ops = { - .bind = vimc_sca_comp_bind, - .unbind = vimc_sca_comp_unbind, -}; - -static int vimc_sca_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &vimc_sca_comp_ops); + return &vsca->ved; } - -static int vimc_sca_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &vimc_sca_comp_ops); - - return 0; -} - -static const struct platform_device_id vimc_sca_driver_ids[] = { - { - .name = VIMC_SCA_DRV_NAME, - }, - { } -}; - -static struct platform_driver vimc_sca_pdrv = { - .probe = vimc_sca_probe, - .remove = vimc_sca_remove, - .id_table = vimc_sca_driver_ids, - .driver = { - .name = VIMC_SCA_DRV_NAME, - }, -}; - -module_platform_driver(vimc_sca_pdrv); - -MODULE_DEVICE_TABLE(platform, vimc_sca_driver_ids); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Scaler"); -MODULE_AUTHOR("Helen Mae Koike Fornazier "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 6c53b9fc1617..1f15637ca8bb 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -5,10 +5,6 @@ * Copyright (C) 2015-2017 Helen Koike */ -#include -#include -#include -#include #include #include #include @@ -18,8 +14,6 @@ #include "vimc-common.h" -#define VIMC_SEN_DRV_NAME "vimc-sensor" - struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; @@ -304,13 +298,11 @@ static const struct v4l2_subdev_internal_ops vimc_sen_int_ops = { .release = vimc_sen_release, }; -static void vimc_sen_comp_unbind(struct device *comp, struct device *master, - void *master_data) +void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) { - struct vimc_ent_device *ved = dev_get_drvdata(comp); - struct vimc_sen_device *vsen = - container_of(ved, struct vimc_sen_device, ved); + struct vimc_sen_device *vsen; + vsen = container_of(ved, struct vimc_sen_device, ved); vimc_ent_sd_unregister(ved, &vsen->sd); } @@ -331,18 +323,17 @@ static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { .qmenu = tpg_pattern_strings, }; -static int vimc_sen_comp_bind(struct device *comp, struct device *master, - void *master_data) +struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, + const char *vcfg_name) { - struct v4l2_device *v4l2_dev = master_data; - struct vimc_platform_data *pdata = comp->platform_data; + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; struct vimc_sen_device *vsen; int ret; /* Allocate the vsen struct */ vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); if (!vsen) - return -ENOMEM; + return NULL; v4l2_ctrl_handler_init(&vsen->hdl, 4); @@ -368,7 +359,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master, /* Initialize ved and sd */ ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, - pdata->entity_name, + vcfg_name, MEDIA_ENT_F_CAM_SENSOR, 1, (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE}, &vimc_sen_int_ops, &vimc_sen_ops); @@ -376,8 +367,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master, goto err_free_hdl; vsen->ved.process_frame = vimc_sen_process_frame; - dev_set_drvdata(comp, &vsen->ved); - vsen->dev = comp; + vsen->dev = &vimc->pdev.dev; /* Initialize the frame format */ vsen->mbus_format = fmt_default; @@ -389,7 +379,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master, if (ret) goto err_unregister_ent_sd; - return 0; + return &vsen->ved; err_unregister_ent_sd: vimc_ent_sd_unregister(&vsen->ved, &vsen->sd); @@ -398,46 +388,5 @@ err_free_hdl: err_free_vsen: kfree(vsen); - return ret; + return NULL; } - -static const struct component_ops vimc_sen_comp_ops = { - .bind = vimc_sen_comp_bind, - .unbind = vimc_sen_comp_unbind, -}; - -static int vimc_sen_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &vimc_sen_comp_ops); -} - -static int vimc_sen_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &vimc_sen_comp_ops); - - return 0; -} - -static const struct platform_device_id vimc_sen_driver_ids[] = { - { - .name = VIMC_SEN_DRV_NAME, - }, - { } -}; - -static struct platform_driver vimc_sen_pdrv = { - .probe = vimc_sen_probe, - .remove = vimc_sen_remove, - .id_table = vimc_sen_driver_ids, - .driver = { - .name = VIMC_SEN_DRV_NAME, - }, -}; - -module_platform_driver(vimc_sen_pdrv); - -MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Sensor"); -MODULE_AUTHOR("Helen Mae Koike Fornazier "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index 048d770e498b..faa2879c25df 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -7,7 +7,6 @@ */ #include -#include #include #include -- cgit v1.2.3 From d7fb5c361c2a2666d20e044206e1756bc8e87df2 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Tue, 17 Sep 2019 13:35:09 -0300 Subject: media: vimc: Fix gpf in rmmod path when stream is active If vimc module is removed while streaming is in progress, sensor subdev unregister runs into general protection fault when it tries to unregister media entities. This is a common subdev problem related to releasing pads from v4l2_device_unregister_subdev() before calling unregister. Unregister references pads during unregistering subdev. The sd release handler is the right place for releasing all sd resources including pads. The release handlers currently release all resources except the pads. Fix v4l2_device_unregister_subdev() not release pads and release pads from the sd_int_op release handlers. kernel: [ 4136.715839] general protection fault: 0000 [#1] SMP PTI kernel: [ 4136.715847] CPU: 2 PID: 1972 Comm: bash Not tainted 5.3.0-rc2+ #4 kernel: [ 4136.715850] Hardware name: Dell Inc. OptiPlex 790/0HY9JP, BIOS A18 09/24/2013 kernel: [ 4136.715858] RIP: 0010:media_gobj_destroy.part.16+0x1f/0x60 kernel: [ 4136.715863] Code: ff 66 2e 0f 1f 84 00 00 00 00 00 66 66 66 66 90 55 48 89 fe 48 89 e5 53 48 89 fb 48 c7 c7 00 7f cf b0 e8 24 fa ff ff 48 8b 03 <48> 83 80 a0 00 00 00 01 48 8b 43 18 48 8b 53 10 48 89 42 08 48 89 kernel: [ 4136.715866] RSP: 0018:ffff9b2248fe3cb0 EFLAGS: 00010246 kernel: [ 4136.715870] RAX: bcf2bfbfa0d63c2f RBX: ffff88c3eb37e9c0 RCX: 00000000802a0018 kernel: [ 4136.715873] RDX: ffff88c3e4f6a078 RSI: ffff88c3eb37e9c0 RDI: ffffffffb0cf7f00 kernel: [ 4136.715876] RBP: ffff9b2248fe3cb8 R08: 0000000001000002 R09: ffffffffb0492b00 kernel: [ 4136.715879] R10: ffff9b2248fe3c28 R11: 0000000000000001 R12: 0000000000000038 kernel: [ 4136.715881] R13: ffffffffc09a1628 R14: ffff88c3e4f6a028 R15: fffffffffffffff2 kernel: [ 4136.715885] FS: 00007f8389647740(0000) GS:ffff88c465500000(0000) knlGS:0000000000000000 kernel: [ 4136.715888] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 kernel: [ 4136.715891] CR2: 000055d008f80fd8 CR3: 00000001996ec005 CR4: 00000000000606e0 kernel: [ 4136.715894] Call Trace: kernel: [ 4136.715903] media_gobj_destroy+0x14/0x20 kernel: [ 4136.715908] __media_device_unregister_entity+0xb3/0xe0 kernel: [ 4136.715915] media_device_unregister_entity+0x30/0x40 kernel: [ 4136.715920] v4l2_device_unregister_subdev+0xa8/0xe0 kernel: [ 4136.715928] vimc_ent_sd_unregister+0x1e/0x30 [vimc] kernel: [ 4136.715933] vimc_sen_rm+0x16/0x20 [vimc] kernel: [ 4136.715938] vimc_remove+0x3e/0xa0 [vimc] kernel: [ 4136.715945] platform_drv_remove+0x25/0x50 kernel: [ 4136.715951] device_release_driver_internal+0xe0/0x1b0 kernel: [ 4136.715956] device_driver_detach+0x14/0x20 kernel: [ 4136.715960] unbind_store+0xd1/0x130 kernel: [ 4136.715965] drv_attr_store+0x27/0x40 kernel: [ 4136.715971] sysfs_kf_write+0x48/0x60 kernel: [ 4136.715976] kernfs_fop_write+0x128/0x1b0 kernel: [ 4136.715982] __vfs_write+0x1b/0x40 kernel: [ 4136.715987] vfs_write+0xc3/0x1d0 kernel: [ 4136.715993] ksys_write+0xaa/0xe0 kernel: [ 4136.715999] __x64_sys_write+0x1a/0x20 kernel: [ 4136.716005] do_syscall_64+0x5a/0x130 kernel: [ 4136.716010] entry_SYSCALL_64_after_hwframe+0x4 Signed-off-by: Shuah Khan Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 3 +-- drivers/media/platform/vimc/vimc-debayer.c | 1 + drivers/media/platform/vimc/vimc-scaler.c | 1 + drivers/media/platform/vimc/vimc-sensor.c | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 7e1ae0b12f1e..a3120f4f7a90 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -375,7 +375,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, { int ret; - /* Allocate the pads */ + /* Allocate the pads. Should be released from the sd_int_op release */ ved->pads = vimc_pads_init(num_pads, pads_flag); if (IS_ERR(ved->pads)) return PTR_ERR(ved->pads); @@ -424,7 +424,6 @@ EXPORT_SYMBOL_GPL(vimc_ent_sd_register); void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) { media_entity_cleanup(ved->ent); - vimc_pads_cleanup(ved->pads); v4l2_device_unregister_subdev(sd); } EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 2c291447807e..4125159e8f31 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -480,6 +480,7 @@ static void vimc_deb_release(struct v4l2_subdev *sd) struct vimc_deb_device *vdeb = container_of(sd, struct vimc_deb_device, sd); + vimc_pads_cleanup(vdeb->ved.pads); kfree(vdeb); } diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index f72200de2535..1a593d81ea7c 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -338,6 +338,7 @@ static void vimc_sca_release(struct v4l2_subdev *sd) struct vimc_sca_device *vsca = container_of(sd, struct vimc_sca_device, sd); + vimc_pads_cleanup(vsca->ved.pads); kfree(vsca); } diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 1f15637ca8bb..46dc6a535abe 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -291,6 +291,7 @@ static void vimc_sen_release(struct v4l2_subdev *sd) v4l2_ctrl_handler_free(&vsen->hdl); tpg_free(&vsen->tpg); + vimc_pads_cleanup(vsen->ved.pads); kfree(vsen); } -- cgit v1.2.3 From 3a9e69f1404f174ea8e0432d308aff20d04a9eeb Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Tue, 17 Sep 2019 13:35:10 -0300 Subject: media: vimc: move duplicated IS_SRC and IS_SINK to common header Move duplicated IS_SRC and IS_SINK dfines to common header. Rename them to VIMC_IS_SRC and VIM_IS_SINK. Signed-off-by: Shuah Khan Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.h | 4 ++++ drivers/media/platform/vimc/vimc-debayer.c | 11 ++++------- drivers/media/platform/vimc/vimc-scaler.c | 8 +++----- 3 files changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index d7aaf31175bc..698db7c07645 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -27,6 +27,10 @@ #define VIMC_FRAME_INDEX(lin, col, width, bpp) ((lin * width + col) * bpp) +/* Source and sink pad checks */ +#define VIMC_IS_SRC(pad) (pad) +#define VIMC_IS_SINK(pad) (!(pad)) + /** * struct vimc_colorimetry_clamp - Adjust colorimetry parameters * diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 4125159e8f31..feac47d79449 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -20,9 +20,6 @@ MODULE_PARM_DESC(deb_mean_win_size, " the window size to calculate the mean.\n" "stays in the center of the window, otherwise the next odd number " "is considered"); -#define IS_SINK(pad) (!pad) -#define IS_SRC(pad) (pad) - enum vimc_deb_rgb_colors { VIMC_DEB_RED = 0, VIMC_DEB_GREEN = 1, @@ -155,7 +152,7 @@ static int vimc_deb_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { /* We only support one format for source pads */ - if (IS_SRC(code->pad)) { + if (VIMC_IS_SRC(code->pad)) { struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); if (code->index) @@ -181,7 +178,7 @@ static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd, if (fse->index) return -EINVAL; - if (IS_SINK(fse->pad)) { + if (VIMC_IS_SINK(fse->pad)) { const struct vimc_deb_pix_map *vpix = vimc_deb_pix_map_by_code(fse->code); @@ -211,7 +208,7 @@ static int vimc_deb_get_fmt(struct v4l2_subdev *sd, vdeb->sink_fmt; /* Set the right code for the source pad */ - if (IS_SRC(fmt->pad)) + if (VIMC_IS_SRC(fmt->pad)) fmt->format.code = vdeb->src_code; return 0; @@ -258,7 +255,7 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, * Do not change the format of the source pad, * it is propagated from the sink */ - if (IS_SRC(fmt->pad)) { + if (VIMC_IS_SRC(fmt->pad)) { fmt->format = *sink_fmt; /* TODO: Add support for other formats */ fmt->format.code = vdeb->src_code; diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index 1a593d81ea7c..a6a3cc5be872 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -16,8 +16,6 @@ static unsigned int sca_mult = 3; module_param(sca_mult, uint, 0000); MODULE_PARM_DESC(sca_mult, " the image size multiplier"); -#define IS_SINK(pad) (!pad) -#define IS_SRC(pad) (pad) #define MAX_ZOOM 8 struct vimc_sca_device { @@ -93,7 +91,7 @@ static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd, fse->min_width = VIMC_FRAME_MIN_WIDTH; fse->min_height = VIMC_FRAME_MIN_HEIGHT; - if (IS_SINK(fse->pad)) { + if (VIMC_IS_SINK(fse->pad)) { fse->max_width = VIMC_FRAME_MAX_WIDTH; fse->max_height = VIMC_FRAME_MAX_HEIGHT; } else { @@ -116,7 +114,7 @@ static int vimc_sca_get_fmt(struct v4l2_subdev *sd, vsca->sink_fmt; /* Scale the frame size for the source pad */ - if (IS_SRC(format->pad)) { + if (VIMC_IS_SRC(format->pad)) { format->format.width = vsca->sink_fmt.width * sca_mult; format->format.height = vsca->sink_fmt.height * sca_mult; } @@ -165,7 +163,7 @@ static int vimc_sca_set_fmt(struct v4l2_subdev *sd, * Do not change the format of the source pad, * it is propagated from the sink */ - if (IS_SRC(fmt->pad)) { + if (VIMC_IS_SRC(fmt->pad)) { fmt->format = *sink_fmt; fmt->format.width = sink_fmt->width * sca_mult; fmt->format.height = sink_fmt->height * sca_mult; -- cgit v1.2.3 From b0e41bf23b590082c9109b691a1f949a0b8defae Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Fri, 20 Sep 2019 14:05:42 -0300 Subject: media: am437x-vpfe: Fix suspend path to always handle pinctrl config Currently if vpfe is not active then it returns immediately in the suspend and resume handlers. Change this so that it always performs the pinctrl config so that we can still get proper sleep state configuration on the pins even if we do not need to worry about fully saving and restoring context. Signed-off-by: Dave Gerlach Signed-off-by: Jyri Sarha Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 46 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 2b42ba1f5949..a3d22f90e64c 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2653,22 +2653,21 @@ static int vpfe_suspend(struct device *dev) struct vpfe_device *vpfe = dev_get_drvdata(dev); struct vpfe_ccdc *ccdc = &vpfe->ccdc; - /* if streaming has not started we don't care */ - if (!vb2_start_streaming_called(&vpfe->buffer_queue)) - return 0; - - pm_runtime_get_sync(dev); - vpfe_config_enable(ccdc, 1); + /* only do full suspend if streaming has started */ + if (vb2_start_streaming_called(&vpfe->buffer_queue)) { + pm_runtime_get_sync(dev); + vpfe_config_enable(ccdc, 1); - /* Save VPFE context */ - vpfe_save_context(ccdc); + /* Save VPFE context */ + vpfe_save_context(ccdc); - /* Disable CCDC */ - vpfe_pcr_enable(ccdc, 0); - vpfe_config_enable(ccdc, 0); + /* Disable CCDC */ + vpfe_pcr_enable(ccdc, 0); + vpfe_config_enable(ccdc, 0); - /* Disable both master and slave clock */ - pm_runtime_put_sync(dev); + /* Disable both master and slave clock */ + pm_runtime_put_sync(dev); + } /* Select sleep pin state */ pinctrl_pm_select_sleep_state(dev); @@ -2710,19 +2709,18 @@ static int vpfe_resume(struct device *dev) struct vpfe_device *vpfe = dev_get_drvdata(dev); struct vpfe_ccdc *ccdc = &vpfe->ccdc; - /* if streaming has not started we don't care */ - if (!vb2_start_streaming_called(&vpfe->buffer_queue)) - return 0; - - /* Enable both master and slave clock */ - pm_runtime_get_sync(dev); - vpfe_config_enable(ccdc, 1); + /* only do full resume if streaming has started */ + if (vb2_start_streaming_called(&vpfe->buffer_queue)) { + /* Enable both master and slave clock */ + pm_runtime_get_sync(dev); + vpfe_config_enable(ccdc, 1); - /* Restore VPFE context */ - vpfe_restore_context(ccdc); + /* Restore VPFE context */ + vpfe_restore_context(ccdc); - vpfe_config_enable(ccdc, 0); - pm_runtime_put_sync(dev); + vpfe_config_enable(ccdc, 0); + pm_runtime_put_sync(dev); + } /* Select default pin state */ pinctrl_pm_select_default_state(dev); -- cgit v1.2.3 From 47c7bcfdb387141126392ff4857a03910d95b4b6 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:43 -0300 Subject: media: am437x-vpfe: Fix missing first line Previous generation of this driver were hard coded to handle encoder/decoder where the first line never contains any data and was therefore always skipped, however when dealing with actual camera sensors the first line is always present. Signed-off-by: Benoit Parrot Signed-off-by: Jyri Sarha Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index a3d22f90e64c..1521c072f3e3 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -345,13 +345,9 @@ static void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc, if (frm_fmt == CCDC_FRMFMT_INTERLACED) { vert_nr_lines = (image_win->height >> 1) - 1; vert_start >>= 1; - /* Since first line doesn't have any data */ - vert_start += 1; /* configure VDINT0 */ val = (vert_start << VPFE_VDINT_VDINT0_SHIFT); } else { - /* Since first line doesn't have any data */ - vert_start += 1; vert_nr_lines = image_win->height - 1; /* * configure VDINT0 and VDINT1. VDINT1 will be at half -- cgit v1.2.3 From e6784f9e4ebb6fac0c8741eeab4009a3a68a3831 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:44 -0300 Subject: media: am437x-vpfe: Rework ISR routine for clarity Make the ISR code simpler to follow by removing goto and relocating/eliminating duplicate spinlock accesses. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 128 ++++++++++++++-------------- 1 file changed, 66 insertions(+), 62 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 1521c072f3e3..13bf4b32b40b 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1233,22 +1233,29 @@ unlock: * This function will get next buffer from the dma queue and * set the buffer address in the vpfe register for capture. * the buffer is marked active - * - * Assumes caller is holding vpfe->dma_queue_lock already */ -static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe) +static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe) { + dma_addr_t addr; + + spin_lock(&vpfe->dma_queue_lock); + if (list_empty(&vpfe->dma_queue)) { + spin_unlock(&vpfe->dma_queue_lock); + return; + } + vpfe->next_frm = list_entry(vpfe->dma_queue.next, struct vpfe_cap_buffer, list); list_del(&vpfe->next_frm->list); + spin_unlock(&vpfe->dma_queue_lock); - vpfe_set_sdr_addr(&vpfe->ccdc, - vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0)); + addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0); + vpfe_set_sdr_addr(&vpfe->ccdc, addr); } static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe) { - unsigned long addr; + dma_addr_t addr; addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0) + vpfe->field_off; @@ -1273,6 +1280,55 @@ static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe) vpfe->cur_frm = vpfe->next_frm; } +static void vpfe_handle_interlaced_irq(struct vpfe_device *vpfe, + enum v4l2_field field) +{ + int fid; + + /* interlaced or TB capture check which field + * we are in hardware + */ + fid = vpfe_ccdc_getfid(&vpfe->ccdc); + + /* switch the software maintained field id */ + vpfe->field ^= 1; + if (fid == vpfe->field) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the + * current frame and move on + */ + if (vpfe->cur_frm != vpfe->next_frm) + vpfe_process_buffer_complete(vpfe); + + /* + * based on whether the two fields are stored + * interleave or separately in memory, + * reconfigure the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(vpfe); + } else { + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + if (vpfe->cur_frm == vpfe->next_frm) + vpfe_schedule_next_buffer(vpfe); + } + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe->field = fid; + } +} + /* * vpfe_isr : ISR handler for vpfe capture (VINT0) * @irq: irq number @@ -1284,76 +1340,24 @@ static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe) static irqreturn_t vpfe_isr(int irq, void *dev) { struct vpfe_device *vpfe = (struct vpfe_device *)dev; - enum v4l2_field field; + enum v4l2_field field = vpfe->fmt.fmt.pix.field; int intr_status; - int fid; intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS); if (intr_status & VPFE_VDINT0) { - field = vpfe->fmt.fmt.pix.field; - if (field == V4L2_FIELD_NONE) { - /* handle progressive frame capture */ if (vpfe->cur_frm != vpfe->next_frm) vpfe_process_buffer_complete(vpfe); - goto next_intr; - } - - /* interlaced or TB capture check which field - we are in hardware */ - fid = vpfe_ccdc_getfid(&vpfe->ccdc); - - /* switch the software maintained field id */ - vpfe->field ^= 1; - if (fid == vpfe->field) { - /* we are in-sync here,continue */ - if (fid == 0) { - /* - * One frame is just being captured. If the - * next frame is available, release the - * current frame and move on - */ - if (vpfe->cur_frm != vpfe->next_frm) - vpfe_process_buffer_complete(vpfe); - /* - * based on whether the two fields are stored - * interleave or separately in memory, - * reconfigure the CCDC memory address - */ - if (field == V4L2_FIELD_SEQ_TB) - vpfe_schedule_bottom_field(vpfe); - - goto next_intr; - } - /* - * if one field is just being captured configure - * the next frame get the next frame from the empty - * queue if no frame is available hold on to the - * current buffer - */ - spin_lock(&vpfe->dma_queue_lock); - if (!list_empty(&vpfe->dma_queue) && - vpfe->cur_frm == vpfe->next_frm) - vpfe_schedule_next_buffer(vpfe); - spin_unlock(&vpfe->dma_queue_lock); - } else if (fid == 0) { - /* - * out of sync. Recover from any hardware out-of-sync. - * May loose one frame - */ - vpfe->field = fid; + } else { + vpfe_handle_interlaced_irq(vpfe, field); } } -next_intr: if (intr_status & VPFE_VDINT1) { - spin_lock(&vpfe->dma_queue_lock); - if (vpfe->fmt.fmt.pix.field == V4L2_FIELD_NONE && - !list_empty(&vpfe->dma_queue) && + if (field == V4L2_FIELD_NONE && vpfe->cur_frm == vpfe->next_frm) vpfe_schedule_next_buffer(vpfe); - spin_unlock(&vpfe->dma_queue_lock); } vpfe_clear_intr(&vpfe->ccdc, intr_status); -- cgit v1.2.3 From b58e69e9a573e74946498077ae25e5244303e718 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:45 -0300 Subject: media: am437x-vpfe: Wait for end of frame before tear-down We were originally attempting to stop all processing as soon as possible, but the in-progress DMA operation cannot be canceled. This led to the module being in a busy state and prevented proper power management functionality. The existing implementation would attempt to clean things up by waiting up to 50ms. However when receiving video frame at 15fps or lower, it meant an inter frame arrival rate of 66.6 ms or higher. In such cases upon tear down the following message could be seen: omap_hwmod: vpfe0: _wait_target_disable failed This patch fixes this issue by adding a stopping state where we would wait for the next Vsync before disabling the hardware. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 52 ++++++++++++++--------------- drivers/media/platform/am437x/am437x-vpfe.h | 3 ++ 2 files changed, 29 insertions(+), 26 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 13bf4b32b40b..66df87d310a2 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -441,40 +441,25 @@ static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc) static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev) { - int dma_cntl, i, pcr; + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + u32 dma_cntl, pcr; - /* If the CCDC module is still busy wait for it to be done */ - for (i = 0; i < 10; i++) { - usleep_range(5000, 6000); - pcr = vpfe_reg_read(ccdc, VPFE_PCR); - if (!pcr) - break; + pcr = vpfe_reg_read(ccdc, VPFE_PCR); + if (pcr) + vpfe_dbg(1, vpfe, "VPFE_PCR is still set (%x)", pcr); - /* make sure it it is disabled */ - vpfe_pcr_enable(ccdc, 0); - } + dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL); + if ((dma_cntl & VPFE_DMA_CNTL_OVERFLOW)) + vpfe_dbg(1, vpfe, "VPFE_DMA_CNTL_OVERFLOW is still set (%x)", + dma_cntl); /* Disable CCDC by resetting all register to default POR values */ vpfe_ccdc_restore_defaults(ccdc); - /* if DMA_CNTL overflow bit is set. Clear it - * It appears to take a while for this to become quiescent ~20ms - */ - for (i = 0; i < 10; i++) { - dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL); - if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW)) - break; - - /* Clear the overflow bit */ - vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL); - usleep_range(5000, 6000); - } - /* Disabled the module at the CONFIG level */ vpfe_config_enable(ccdc, 0); pm_runtime_put_sync(dev); - return 0; } @@ -1303,6 +1288,9 @@ static void vpfe_handle_interlaced_irq(struct vpfe_device *vpfe, if (vpfe->cur_frm != vpfe->next_frm) vpfe_process_buffer_complete(vpfe); + if (vpfe->stopping) + return; + /* * based on whether the two fields are stored * interleave or separately in memory, @@ -1341,7 +1329,7 @@ static irqreturn_t vpfe_isr(int irq, void *dev) { struct vpfe_device *vpfe = (struct vpfe_device *)dev; enum v4l2_field field = vpfe->fmt.fmt.pix.field; - int intr_status; + int intr_status, stopping = vpfe->stopping; intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS); @@ -1352,9 +1340,13 @@ static irqreturn_t vpfe_isr(int irq, void *dev) } else { vpfe_handle_interlaced_irq(vpfe, field); } + if (stopping) { + vpfe->stopping = false; + complete(&vpfe->capture_stop); + } } - if (intr_status & VPFE_VDINT1) { + if (intr_status & VPFE_VDINT1 && !stopping) { if (field == V4L2_FIELD_NONE && vpfe->cur_frm == vpfe->next_frm) vpfe_schedule_next_buffer(vpfe); @@ -1980,6 +1972,9 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) vpfe_attach_irq(vpfe); + vpfe->stopping = false; + init_completion(&vpfe->capture_stop); + if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER) vpfe_ccdc_config_raw(&vpfe->ccdc); else @@ -2032,6 +2027,11 @@ static void vpfe_stop_streaming(struct vb2_queue *vq) vpfe_pcr_enable(&vpfe->ccdc, 0); + /* Wait for the last frame to be captured */ + vpfe->stopping = true; + wait_for_completion_timeout(&vpfe->capture_stop, + msecs_to_jiffies(250)); + vpfe_detach_irq(vpfe); sdinfo = vpfe->current_subdev; diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index 4678285f34c6..2dde09780215 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -270,6 +271,8 @@ struct vpfe_device { */ u32 field_off; struct vpfe_ccdc ccdc; + int stopping; + struct completion capture_stop; }; #endif /* AM437X_VPFE_H */ -- cgit v1.2.3 From 158a1dddf2dbce5fdb3053213c1e9b36a54b4d4d Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:46 -0300 Subject: media: am437x-vpfe: fix start streaming error path When start_streaming fails the h/w module might be left enabled inadvertently. Make sure it is disabled in the error path. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 66df87d310a2..e0a4c8920df8 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2008,6 +2008,7 @@ err: vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } + vpfe_pcr_enable(&vpfe->ccdc, 0); return ret; } -- cgit v1.2.3 From 73940235337ee91ba32eaf71d641d74d9b1c9b63 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:47 -0300 Subject: media: am437x-vpfe: Streamlined vb2 buffer cleanup Returning queued vb2 buffers back to user space is a common task best handled by a helper function. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 53 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 28 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index e0a4c8920df8..03415c179c85 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1949,6 +1949,29 @@ static void vpfe_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); } +static void vpfe_return_all_buffers(struct vpfe_device *vpfe, + enum vb2_buffer_state state) +{ + struct vpfe_cap_buffer *buf, *node; + unsigned long flags; + + spin_lock_irqsave(&vpfe->dma_queue_lock, flags); + list_for_each_entry_safe(buf, node, &vpfe->dma_queue, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } + + if (vpfe->cur_frm) + vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, state); + + if (vpfe->next_frm && vpfe->next_frm != vpfe->cur_frm) + vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, state); + + vpfe->cur_frm = NULL; + vpfe->next_frm = NULL; + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); +} + /* * vpfe_start_streaming : Starts the DMA engine for streaming * @vb: ptr to vb2_buffer @@ -1957,7 +1980,6 @@ static void vpfe_buffer_queue(struct vb2_buffer *vb) static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vpfe_device *vpfe = vb2_get_drv_priv(vq); - struct vpfe_cap_buffer *buf, *tmp; struct vpfe_subdev_info *sdinfo; unsigned long flags; unsigned long addr; @@ -2003,11 +2025,7 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; err: - list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - + vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_QUEUED); vpfe_pcr_enable(&vpfe->ccdc, 0); return ret; } @@ -2023,7 +2041,6 @@ static void vpfe_stop_streaming(struct vb2_queue *vq) { struct vpfe_device *vpfe = vb2_get_drv_priv(vq); struct vpfe_subdev_info *sdinfo; - unsigned long flags; int ret; vpfe_pcr_enable(&vpfe->ccdc, 0); @@ -2041,27 +2058,7 @@ static void vpfe_stop_streaming(struct vb2_queue *vq) vpfe_dbg(1, vpfe, "stream off failed in subdev\n"); /* release all active buffers */ - spin_lock_irqsave(&vpfe->dma_queue_lock, flags); - if (vpfe->cur_frm == vpfe->next_frm) { - vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } else { - if (vpfe->cur_frm != NULL) - vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - if (vpfe->next_frm != NULL) - vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - - while (!list_empty(&vpfe->dma_queue)) { - vpfe->next_frm = list_entry(vpfe->dma_queue.next, - struct vpfe_cap_buffer, list); - list_del(&vpfe->next_frm->list); - vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); + vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_ERROR); } static int vpfe_g_pixelaspect(struct file *file, void *priv, -- cgit v1.2.3 From 13aa21cfe92ce9ebb51824029d89f19c33f81419 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:48 -0300 Subject: media: am437x-vpfe: Setting STD to current value is not an error VIDIOC_S_STD should not return an error if the value is identical to the current one. This error was highlighted by the v4l2-compliance test. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 03415c179c85..8b218f48428f 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1822,6 +1822,10 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) return -ENODATA; + /* if trying to set the same std then nothing to do */ + if (vpfe_standards[vpfe->std_index].std_id == std_id) + return 0; + /* If streaming is started, return error */ if (vb2_is_busy(&vpfe->buffer_queue)) { vpfe_err(vpfe, "%s device busy\n", __func__); -- cgit v1.2.3 From 0512ccba072a6c2517d2926009eb223ad163f413 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:49 -0300 Subject: media: am437x-vpfe: Use a per instance format array instead of a static one Using a statically defined format array would cause issue when multiple vpfe instance would be connected to sub-device of different capabilities. We need to use an instance based array instead to properly maintain a per port/instance format list. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 110 +++++++++++----------------- drivers/media/platform/am437x/am437x-vpfe.h | 34 +++++++++ 2 files changed, 75 insertions(+), 69 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 8b218f48428f..b213348fd1c4 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -69,31 +69,7 @@ static const struct vpfe_standard vpfe_standards[] = { {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, }; -struct bus_format { - unsigned int width; - unsigned int bpp; -}; - -/* - * struct vpfe_fmt - VPFE media bus format information - * @code: V4L2 media bus format code - * @shifted: V4L2 media bus format code for the same pixel layout but - * shifted to be 8 bits per pixel. =0 if format is not shiftable. - * @pixelformat: V4L2 pixel format FCC identifier - * @width: Bits per pixel (when transferred over a bus) - * @bpp: Bytes per pixel (when stored in memory) - * @supported: Indicates format supported by subdev - */ -struct vpfe_fmt { - u32 fourcc; - u32 code; - struct bus_format l; - struct bus_format s; - bool supported; - u32 index; -}; - -static struct vpfe_fmt formats[] = { +static struct vpfe_fmt formats[VPFE_NUM_FORMATS] = { { .fourcc = V4L2_PIX_FMT_YUYV, .code = MEDIA_BUS_FMT_YUYV8_2X8, @@ -101,7 +77,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 4, .s.width = 8, .s.bpp = 2, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_UYVY, .code = MEDIA_BUS_FMT_UYVY8_2X8, @@ -109,7 +84,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 4, .s.width = 8, .s.bpp = 2, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_YVYU, .code = MEDIA_BUS_FMT_YVYU8_2X8, @@ -117,7 +91,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 4, .s.width = 8, .s.bpp = 2, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_VYUY, .code = MEDIA_BUS_FMT_VYUY8_2X8, @@ -125,7 +98,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 4, .s.width = 8, .s.bpp = 2, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_SBGGR8, .code = MEDIA_BUS_FMT_SBGGR8_1X8, @@ -133,7 +105,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 2, .s.width = 8, .s.bpp = 1, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_SGBRG8, .code = MEDIA_BUS_FMT_SGBRG8_1X8, @@ -141,7 +112,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 2, .s.width = 8, .s.bpp = 1, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_SGRBG8, .code = MEDIA_BUS_FMT_SGRBG8_1X8, @@ -149,7 +119,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 2, .s.width = 8, .s.bpp = 1, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_SRGGB8, .code = MEDIA_BUS_FMT_SRGGB8_1X8, @@ -157,7 +126,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 2, .s.width = 8, .s.bpp = 1, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_RGB565, .code = MEDIA_BUS_FMT_RGB565_2X8_LE, @@ -165,7 +133,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 4, .s.width = 8, .s.bpp = 2, - .supported = false, }, { .fourcc = V4L2_PIX_FMT_RGB565X, .code = MEDIA_BUS_FMT_RGB565_2X8_BE, @@ -173,7 +140,6 @@ static struct vpfe_fmt formats[] = { .l.bpp = 4, .s.width = 8, .s.bpp = 2, - .supported = false, }, }; @@ -181,13 +147,14 @@ static int __vpfe_get_format(struct vpfe_device *vpfe, struct v4l2_format *format, unsigned int *bpp); -static struct vpfe_fmt *find_format_by_code(unsigned int code) +static struct vpfe_fmt *find_format_by_code(struct vpfe_device *vpfe, + unsigned int code) { struct vpfe_fmt *fmt; unsigned int k; - for (k = 0; k < ARRAY_SIZE(formats); k++) { - fmt = &formats[k]; + for (k = 0; k < vpfe->num_active_fmt; k++) { + fmt = vpfe->active_fmt[k]; if (fmt->code == code) return fmt; } @@ -195,13 +162,14 @@ static struct vpfe_fmt *find_format_by_code(unsigned int code) return NULL; } -static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat) +static struct vpfe_fmt *find_format_by_pix(struct vpfe_device *vpfe, + unsigned int pixelformat) { struct vpfe_fmt *fmt; unsigned int k; - for (k = 0; k < ARRAY_SIZE(formats); k++) { - fmt = &formats[k]; + for (k = 0; k < vpfe->num_active_fmt; k++) { + fmt = vpfe->active_fmt[k]; if (fmt->fourcc == pixelformat) return fmt; } @@ -218,7 +186,7 @@ mbus_to_pix(struct vpfe_device *vpfe, unsigned int bus_width = sdinfo->vpfe_param.bus_width; struct vpfe_fmt *fmt; - fmt = find_format_by_code(mbus->code); + fmt = find_format_by_code(vpfe, mbus->code); if (WARN_ON(fmt == NULL)) { pr_err("Invalid mbus code set\n"); *bpp = 1; @@ -241,12 +209,12 @@ static void pix_to_mbus(struct vpfe_device *vpfe, { struct vpfe_fmt *fmt; - fmt = find_format_by_pix(pix_fmt->pixelformat); + fmt = find_format_by_pix(vpfe, pix_fmt->pixelformat); if (!fmt) { /* default to first entry */ vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", pix_fmt->pixelformat); - fmt = &formats[0]; + fmt = vpfe->active_fmt[0]; } memset(mbus_fmt, 0, sizeof(*mbus_fmt)); @@ -1494,8 +1462,7 @@ static int vpfe_enum_fmt(struct file *file, void *priv, { struct vpfe_device *vpfe = video_drvdata(file); struct vpfe_subdev_info *sdinfo; - struct vpfe_fmt *fmt = NULL; - unsigned int k; + struct vpfe_fmt *fmt; vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n", f->index); @@ -1504,17 +1471,10 @@ static int vpfe_enum_fmt(struct file *file, void *priv, if (!sdinfo->sd) return -EINVAL; - if (f->index > ARRAY_SIZE(formats)) + if (f->index >= vpfe->num_active_fmt) return -EINVAL; - for (k = 0; k < ARRAY_SIZE(formats); k++) { - if (formats[k].index == f->index) { - fmt = &formats[k]; - break; - } - } - if (!fmt) - return -EINVAL; + fmt = vpfe->active_fmt[f->index]; f->pixelformat = fmt->fourcc; @@ -1593,7 +1553,7 @@ static int vpfe_enum_size(struct file *file, void *priv, vpfe_dbg(2, vpfe, "vpfe_enum_size\n"); /* check for valid format */ - fmt = find_format_by_pix(fsize->pixel_format); + fmt = find_format_by_pix(vpfe, fsize->pixel_format); if (!fmt) { vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", fsize->pixel_format); @@ -2281,8 +2241,10 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, struct vpfe_device, v4l2_dev); struct v4l2_subdev_mbus_code_enum mbus_code; struct vpfe_subdev_info *sdinfo; + struct vpfe_fmt *fmt; + int ret = 0; bool found = false; - int i, j; + int i, j, k; vpfe_dbg(1, vpfe, "vpfe_async_bound\n"); @@ -2304,27 +2266,37 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, vpfe->video_dev.tvnorms |= sdinfo->inputs[0].std; - /* setup the supported formats & indexes */ - for (j = 0, i = 0; ; ++j) { - struct vpfe_fmt *fmt; - int ret; - + vpfe->num_active_fmt = 0; + for (j = 0, i = 0; (ret != -EINVAL); ++j) { memset(&mbus_code, 0, sizeof(mbus_code)); mbus_code.index = j; mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code); + NULL, &mbus_code); if (ret) - break; - - fmt = find_format_by_code(mbus_code.code); - if (!fmt) continue; - fmt->supported = true; - fmt->index = i++; + vpfe_dbg(3, vpfe, + "subdev %s: code: %04x idx: %d\n", + subdev->name, mbus_code.code, j); + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + fmt = &formats[k]; + if (mbus_code.code != fmt->code) + continue; + vpfe->active_fmt[i] = fmt; + vpfe_dbg(3, vpfe, + "matched fourcc: %s code: %04x idx: %d\n", + print_fourcc(fmt->fourcc), mbus_code.code, i); + vpfe->num_active_fmt = ++i; + } } + if (!i) { + vpfe_err(vpfe, "No suitable format reported by subdev %s\n", + subdev->name); + return -EINVAL; + } return 0; } diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index 2dde09780215..6c7bcb837f4e 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -215,6 +215,37 @@ struct vpfe_ccdc { u32 ccdc_ctx[VPFE_REG_END / sizeof(u32)]; }; +/* + * struct bus_format - VPFE bus format information + * width: Bits per pixel (when transferred over a bus) + * bpp: Bytes per pixel (when stored in memory) + */ +struct bus_format { + unsigned int width; + unsigned int bpp; +}; + +/* + * struct vpfe_fmt - VPFE media bus format information + * fourcc: V4L2 pixel format code + * code: V4L2 media bus format code + * l: 10 bit bus format info + * s: 8 bit bus format info + */ +struct vpfe_fmt { + u32 fourcc; + u32 code; + struct bus_format l; + struct bus_format s; +}; + +/* + * When formats[] is modified make sure to adjust this value also. + * Expect compile time warnings if VPFE_NUM_FORMATS is smaller then + * the number of elements in formats[]. + */ +#define VPFE_NUM_FORMATS 10 + struct vpfe_device { /* V4l2 specific parameters */ /* Identifies video device for this channel */ @@ -252,6 +283,9 @@ struct vpfe_device { struct v4l2_format fmt; /* Used to store current bytes per pixel based on current format */ unsigned int bpp; + struct vpfe_fmt *active_fmt[VPFE_NUM_FORMATS]; + unsigned int num_active_fmt; + /* * used when IMP is chained to store the crop window which * is different from the image window -- cgit v1.2.3 From f60de889d7db5428d4d1894f708d78aa58cc9036 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:50 -0300 Subject: media: am437x-vpfe: fix function trace debug log checkpatch.pl nows reports several: WARNING: Prefer using '"%s...", __func__' to using '', this function's name, in a string. So fix these for the whole driver. At the same time remove the function entry trace log as those can be enabled using ftrace instead. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 72 ++++++----------------------- 1 file changed, 13 insertions(+), 59 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index b213348fd1c4..9b781ab21893 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -443,8 +443,8 @@ static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params) x = copy_from_user(&raw_params, params, sizeof(raw_params)); if (x) { vpfe_dbg(1, vpfe, - "vpfe_ccdc_set_params: error in copying ccdc params, %d\n", - x); + "%s: error in copying ccdc params, %d\n", + __func__, x); return -EFAULT; } @@ -462,11 +462,9 @@ static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params) */ static void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc) { - struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr; u32 syn_mode; - vpfe_dbg(3, vpfe, "vpfe_ccdc_config_ycbcr:\n"); /* * first restore the CCDC registers to default values * This is important since we assume default values to be set in @@ -598,8 +596,6 @@ static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc) unsigned int syn_mode; unsigned int val; - vpfe_dbg(3, vpfe, "vpfe_ccdc_config_raw:\n"); - /* Reset CCDC */ vpfe_ccdc_restore_defaults(ccdc); @@ -700,8 +696,8 @@ static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt) { struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); - vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n", - ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt)); + vpfe_dbg(1, vpfe, "%s: if_type: %d, pixfmt:%s\n", + __func__, ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt)); if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; @@ -987,8 +983,6 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; int ret = 0; - vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n"); - vpfe_dbg(1, vpfe, "pixelformat: %s\n", print_fourcc(vpfe->fmt.fmt.pix.pixelformat)); @@ -1354,8 +1348,6 @@ static int vpfe_querycap(struct file *file, void *priv, { struct vpfe_device *vpfe = video_drvdata(file); - vpfe_dbg(2, vpfe, "vpfe_querycap\n"); - strscpy(cap->driver, VPFE_MODULE_NAME, sizeof(cap->driver)); strscpy(cap->card, "TI AM437x VPFE", sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), @@ -1400,7 +1392,7 @@ static int __vpfe_get_format(struct vpfe_device *vpfe, format->type = vpfe->fmt.type; vpfe_dbg(1, vpfe, - "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", + "%s: size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", __func__, format->fmt.pix.width, format->fmt.pix.height, print_fourcc(format->fmt.pix.pixelformat), format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp); @@ -1416,8 +1408,6 @@ static int __vpfe_set_format(struct vpfe_device *vpfe, struct v4l2_subdev_format fmt; int ret; - vpfe_dbg(2, vpfe, "__vpfe_set_format\n"); - sdinfo = vpfe->current_subdev; if (!sdinfo->sd) return -EINVAL; @@ -1450,8 +1440,6 @@ static int vpfe_g_fmt(struct file *file, void *priv, { struct vpfe_device *vpfe = video_drvdata(file); - vpfe_dbg(2, vpfe, "vpfe_g_fmt\n"); - *fmt = vpfe->fmt; return 0; @@ -1464,9 +1452,6 @@ static int vpfe_enum_fmt(struct file *file, void *priv, struct vpfe_subdev_info *sdinfo; struct vpfe_fmt *fmt; - vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n", - f->index); - sdinfo = vpfe->current_subdev; if (!sdinfo->sd) return -EINVAL; @@ -1478,8 +1463,8 @@ static int vpfe_enum_fmt(struct file *file, void *priv, f->pixelformat = fmt->fourcc; - vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %s\n", - f->index, fmt->code, print_fourcc(fmt->fourcc)); + vpfe_dbg(1, vpfe, "%s: mbus index: %d code: %x pixelformat: %s\n", + __func__, f->index, fmt->code, print_fourcc(fmt->fourcc)); return 0; } @@ -1490,8 +1475,6 @@ static int vpfe_try_fmt(struct file *file, void *priv, struct vpfe_device *vpfe = video_drvdata(file); unsigned int bpp; - vpfe_dbg(2, vpfe, "vpfe_try_fmt\n"); - return __vpfe_get_format(vpfe, fmt, &bpp); } @@ -1503,8 +1486,6 @@ static int vpfe_s_fmt(struct file *file, void *priv, unsigned int bpp; int ret; - vpfe_dbg(2, vpfe, "vpfe_s_fmt\n"); - /* If streaming is started, return error */ if (vb2_is_busy(&vpfe->buffer_queue)) { vpfe_err(vpfe, "%s device busy\n", __func__); @@ -1550,8 +1531,6 @@ static int vpfe_enum_size(struct file *file, void *priv, struct vpfe_fmt *fmt; int ret; - vpfe_dbg(2, vpfe, "vpfe_enum_size\n"); - /* check for valid format */ fmt = find_format_by_pix(vpfe, fsize->pixel_format); if (!fmt) { @@ -1584,17 +1563,17 @@ static int vpfe_enum_size(struct file *file, void *priv, if (ret) return -EINVAL; - vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); + vpfe_dbg(1, vpfe, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = fse.max_width; fsize->discrete.height = fse.max_height; - vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %s size: %dx%d\n", - fsize->index, print_fourcc(fsize->pixel_format), - fsize->discrete.width, fsize->discrete.height); + vpfe_dbg(1, vpfe, "%s: index: %d pixformat: %s size: %dx%d\n", + __func__, fsize->index, print_fourcc(fsize->pixel_format), + fsize->discrete.width, fsize->discrete.height); return 0; } @@ -1659,8 +1638,6 @@ static int vpfe_enum_input(struct file *file, void *priv, struct vpfe_subdev_info *sdinfo; int subdev, index; - vpfe_dbg(2, vpfe, "vpfe_enum_input\n"); - if (vpfe_get_subdev_input_index(vpfe, &subdev, &index, inp->index) < 0) { vpfe_dbg(1, vpfe, @@ -1677,8 +1654,6 @@ static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) { struct vpfe_device *vpfe = video_drvdata(file); - vpfe_dbg(2, vpfe, "vpfe_g_input\n"); - return vpfe_get_app_input_index(vpfe, index); } @@ -1691,8 +1666,6 @@ static int vpfe_set_input(struct vpfe_device *vpfe, unsigned int index) u32 input, output; int ret; - vpfe_dbg(2, vpfe, "vpfe_set_input: index: %d\n", index); - /* If streaming is started, return error */ if (vb2_is_busy(&vpfe->buffer_queue)) { vpfe_err(vpfe, "%s device busy\n", __func__); @@ -1748,9 +1721,6 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) { struct vpfe_device *vpfe = video_drvdata(file); - vpfe_dbg(2, vpfe, - "vpfe_s_input: index: %d\n", index); - return vpfe_set_input(vpfe, index); } @@ -1759,8 +1729,6 @@ static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) struct vpfe_device *vpfe = video_drvdata(file); struct vpfe_subdev_info *sdinfo; - vpfe_dbg(2, vpfe, "vpfe_querystd\n"); - sdinfo = vpfe->current_subdev; if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) return -ENODATA; @@ -1776,8 +1744,6 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) struct vpfe_subdev_info *sdinfo; int ret; - vpfe_dbg(2, vpfe, "vpfe_s_std\n"); - sdinfo = vpfe->current_subdev; if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) return -ENODATA; @@ -1809,8 +1775,6 @@ static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) struct vpfe_device *vpfe = video_drvdata(file); struct vpfe_subdev_info *sdinfo; - vpfe_dbg(2, vpfe, "vpfe_g_std\n"); - sdinfo = vpfe->current_subdev; if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD) return -ENODATA; @@ -1828,8 +1792,6 @@ static void vpfe_calculate_offsets(struct vpfe_device *vpfe) { struct v4l2_rect image_win; - vpfe_dbg(2, vpfe, "vpfe_calculate_offsets\n"); - vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win); vpfe->field_off = image_win.height * image_win.width; } @@ -2030,8 +1992,6 @@ static int vpfe_g_pixelaspect(struct file *file, void *priv, { struct vpfe_device *vpfe = video_drvdata(file); - vpfe_dbg(2, vpfe, "vpfe_g_pixelaspect\n"); - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || vpfe->std_index >= ARRAY_SIZE(vpfe_standards)) return -EINVAL; @@ -2134,8 +2094,6 @@ static long vpfe_ioctl_default(struct file *file, void *priv, struct vpfe_device *vpfe = video_drvdata(file); int ret; - vpfe_dbg(2, vpfe, "vpfe_ioctl_default\n"); - if (!valid_prio) { vpfe_err(vpfe, "%s device busy\n", __func__); return -EBUSY; @@ -2246,8 +2204,6 @@ vpfe_async_bound(struct v4l2_async_notifier *notifier, bool found = false; int i, j, k; - vpfe_dbg(1, vpfe, "vpfe_async_bound\n"); - for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { if (vpfe->cfg->asd[i]->match.fwnode == asd[i].match.fwnode) { @@ -2579,8 +2535,6 @@ static int vpfe_remove(struct platform_device *pdev) { struct vpfe_device *vpfe = platform_get_drvdata(pdev); - vpfe_dbg(2, vpfe, "vpfe_remove\n"); - pm_runtime_disable(&pdev->dev); v4l2_async_notifier_unregister(&vpfe->notifier); -- cgit v1.2.3 From 750ef54b7e2b03ce33dd3d7903aa8254e6f2adab Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:51 -0300 Subject: media: am437x-vpfe: TRY_FMT ioctl is not really trying anything The try_fmt was not actually trying out the provided format but merely returning the current format basically like get_fmt. In addition set_fmt should first invoked try_fmt to validate the given format before applying it to the hardware. To fix all of these the whole get/try/set ioctl functions had to be reworked. When calculating the bytesperline/stride and sizeimage format member we don't need to locally store the current value of bytesperpixel as it can easily get derived dynamically. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 338 ++++++++++++++-------------- drivers/media/platform/am437x/am437x-vpfe.h | 4 +- 2 files changed, 171 insertions(+), 171 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 9b781ab21893..e5ce9c8431ee 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -143,9 +143,11 @@ static struct vpfe_fmt formats[VPFE_NUM_FORMATS] = { }, }; -static int -__vpfe_get_format(struct vpfe_device *vpfe, - struct v4l2_format *format, unsigned int *bpp); +static int __subdev_get_format(struct vpfe_device *vpfe, + struct v4l2_mbus_framefmt *fmt); +static int vpfe_calc_format_size(struct vpfe_device *vpfe, + const struct vpfe_fmt *fmt, + struct v4l2_format *f); static struct vpfe_fmt *find_format_by_code(struct vpfe_device *vpfe, unsigned int code) @@ -177,48 +179,16 @@ static struct vpfe_fmt *find_format_by_pix(struct vpfe_device *vpfe, return NULL; } -static void -mbus_to_pix(struct vpfe_device *vpfe, - const struct v4l2_mbus_framefmt *mbus, - struct v4l2_pix_format *pix, unsigned int *bpp) +static unsigned int __get_bytesperpixel(struct vpfe_device *vpfe, + const struct vpfe_fmt *fmt) { struct vpfe_subdev_info *sdinfo = vpfe->current_subdev; unsigned int bus_width = sdinfo->vpfe_param.bus_width; - struct vpfe_fmt *fmt; - - fmt = find_format_by_code(vpfe, mbus->code); - if (WARN_ON(fmt == NULL)) { - pr_err("Invalid mbus code set\n"); - *bpp = 1; - return; - } + u32 bpp; - memset(pix, 0, sizeof(*pix)); - v4l2_fill_pix_format(pix, mbus); - pix->pixelformat = fmt->fourcc; - *bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp; + bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp; - /* pitch should be 32 bytes aligned */ - pix->bytesperline = ALIGN(pix->width * *bpp, 32); - pix->sizeimage = pix->bytesperline * pix->height; -} - -static void pix_to_mbus(struct vpfe_device *vpfe, - struct v4l2_pix_format *pix_fmt, - struct v4l2_mbus_framefmt *mbus_fmt) -{ - struct vpfe_fmt *fmt; - - fmt = find_format_by_pix(vpfe, pix_fmt->pixelformat); - if (!fmt) { - /* default to first entry */ - vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", - pix_fmt->pixelformat); - fmt = vpfe->active_fmt[0]; - } - - memset(mbus_fmt, 0, sizeof(*mbus_fmt)); - v4l2_fill_mbus_format(mbus_fmt, pix_fmt, fmt->code); + return bpp; } /* Print Four-character-code (FOURCC) */ @@ -235,20 +205,6 @@ static char *print_fourcc(u32 fmt) return code; } -static int -cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs) -{ - return lhs->type == rhs->type && - lhs->fmt.pix.width == rhs->fmt.pix.width && - lhs->fmt.pix.height == rhs->fmt.pix.height && - lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat && - lhs->fmt.pix.field == rhs->fmt.pix.field && - lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace && - lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc && - lhs->fmt.pix.quantization == rhs->fmt.pix.quantization && - lhs->fmt.pix.xfer_func == rhs->fmt.pix.xfer_func; -} - static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset) { return ioread32(ccdc->ccdc_cfg.base_addr + offset); @@ -981,6 +937,7 @@ static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe, static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) { enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + u32 bpp; int ret = 0; vpfe_dbg(1, vpfe, "pixelformat: %s\n", @@ -993,7 +950,8 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) } /* configure the image window */ - vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp); + bpp = __get_bytesperpixel(vpfe, vpfe->current_vpfe_fmt); + vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, bpp); switch (vpfe->fmt.fmt.pix.field) { case V4L2_FIELD_INTERLACED: @@ -1037,7 +995,8 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) static int vpfe_config_image_format(struct vpfe_device *vpfe, v4l2_std_id std_id) { - struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix; + struct vpfe_fmt *fmt; + struct v4l2_mbus_framefmt mbus_fmt; int i, ret; for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { @@ -1059,26 +1018,29 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe, return -EINVAL; } - vpfe->crop.top = vpfe->crop.left = 0; - vpfe->crop.width = vpfe->std_info.active_pixels; - vpfe->crop.height = vpfe->std_info.active_lines; - pix->width = vpfe->crop.width; - pix->height = vpfe->crop.height; - pix->pixelformat = V4L2_PIX_FMT_YUYV; - - /* first field and frame format based on standard frame format */ - if (vpfe->std_info.frame_format) - pix->field = V4L2_FIELD_INTERLACED; - else - pix->field = V4L2_FIELD_NONE; - - ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp); + ret = __subdev_get_format(vpfe, &mbus_fmt); if (ret) return ret; + fmt = find_format_by_code(vpfe, mbus_fmt.code); + if (!fmt) { + vpfe_dbg(3, vpfe, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current subdev format */ + v4l2_fill_pix_format(&vpfe->fmt.fmt.pix, &mbus_fmt); + vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vpfe->fmt.fmt.pix.pixelformat = fmt->fourcc; + vpfe_calc_format_size(vpfe, fmt, &vpfe->fmt); + vpfe->current_vpfe_fmt = fmt; + /* Update the crop window based on found values */ - vpfe->crop.width = pix->width; - vpfe->crop.height = pix->height; + vpfe->crop.top = 0; + vpfe->crop.left = 0; + vpfe->crop.width = mbus_fmt.width; + vpfe->crop.height = mbus_fmt.height; return vpfe_config_ccdc_image_format(vpfe); } @@ -1356,81 +1318,74 @@ static int vpfe_querycap(struct file *file, void *priv, } /* get the format set at output pad of the adjacent subdev */ -static int __vpfe_get_format(struct vpfe_device *vpfe, - struct v4l2_format *format, unsigned int *bpp) +static int __subdev_get_format(struct vpfe_device *vpfe, + struct v4l2_mbus_framefmt *fmt) { - struct v4l2_mbus_framefmt mbus_fmt; - struct vpfe_subdev_info *sdinfo; - struct v4l2_subdev_format fmt; + struct v4l2_subdev *sd = vpfe->current_subdev->sd; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - sdinfo = vpfe->current_subdev; - if (!sdinfo->sd) - return -EINVAL; - - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt.pad = 0; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; - ret = v4l2_subdev_call(sdinfo->sd, pad, get_fmt, NULL, &fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); + if (ret) return ret; - if (!ret) { - v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); - mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); - } else { - ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, - sdinfo->grp_id, - pad, get_fmt, - NULL, &fmt); - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt); - mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp); - } - - format->type = vpfe->fmt.type; + *fmt = *mbus_fmt; - vpfe_dbg(1, vpfe, - "%s: size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", - __func__, format->fmt.pix.width, format->fmt.pix.height, - print_fourcc(format->fmt.pix.pixelformat), - format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp); + vpfe_dbg(1, vpfe, "%s: %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); return 0; } /* set the format at output pad of the adjacent subdev */ -static int __vpfe_set_format(struct vpfe_device *vpfe, - struct v4l2_format *format, unsigned int *bpp) +static int __subdev_set_format(struct vpfe_device *vpfe, + struct v4l2_mbus_framefmt *fmt) { - struct vpfe_subdev_info *sdinfo; - struct v4l2_subdev_format fmt; + struct v4l2_subdev *sd = vpfe->current_subdev->sd; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - sdinfo = vpfe->current_subdev; - if (!sdinfo->sd) - return -EINVAL; - - fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt.pad = 0; - - pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format); + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + *mbus_fmt = *fmt; - ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sd_fmt); if (ret) return ret; - v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); - mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); + vpfe_dbg(1, vpfe, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); - format->type = vpfe->fmt.type; + return 0; +} - vpfe_dbg(1, vpfe, - "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", - __func__, format->fmt.pix.width, format->fmt.pix.height, - print_fourcc(format->fmt.pix.pixelformat), - format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp); +static int vpfe_calc_format_size(struct vpfe_device *vpfe, + const struct vpfe_fmt *fmt, + struct v4l2_format *f) +{ + u32 bpp; + + if (!fmt) { + vpfe_dbg(3, vpfe, "No vpfe_fmt provided!\n"); + return -EINVAL; + } + + bpp = __get_bytesperpixel(vpfe, fmt); + + /* pitch should be 32 bytes aligned */ + f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.width * bpp, 32); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + + vpfe_dbg(3, vpfe, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", + __func__, print_fourcc(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); return 0; } @@ -1470,20 +1425,70 @@ static int vpfe_enum_fmt(struct file *file, void *priv, } static int vpfe_try_fmt(struct file *file, void *priv, - struct v4l2_format *fmt) + struct v4l2_format *f) { struct vpfe_device *vpfe = video_drvdata(file); - unsigned int bpp; + struct v4l2_subdev *sd = vpfe->current_subdev->sd; + const struct vpfe_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret, found; + + fmt = find_format_by_pix(vpfe, f->fmt.pix.pixelformat); + if (!fmt) { + /* default to first entry */ + vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", + f->fmt.pix.pixelformat); + fmt = vpfe->active_fmt[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } - return __vpfe_get_format(vpfe, fmt, &bpp); + f->fmt.pix.field = vpfe->fmt.fmt.pix.field; + + /* check for/find a valid width/height */ + ret = 0; + found = false; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + for (fse.index = 0; ; fse.index++) { + ret = v4l2_subdev_call(sd, pad, enum_frame_size, + NULL, &fse); + if (ret) + break; + + if (f->fmt.pix.width == fse.max_width && + f->fmt.pix.height == fse.max_height) { + found = true; + break; + } else if (f->fmt.pix.width >= fse.min_width && + f->fmt.pix.width <= fse.max_width && + f->fmt.pix.height >= fse.min_height && + f->fmt.pix.height <= fse.max_height) { + found = true; + break; + } + } + + if (!found) { + /* use existing values as default */ + f->fmt.pix.width = vpfe->fmt.fmt.pix.width; + f->fmt.pix.height = vpfe->fmt.fmt.pix.height; + } + + /* + * Use current colorspace for now, it will get + * updated properly during s_fmt + */ + f->fmt.pix.colorspace = vpfe->fmt.fmt.pix.colorspace; + return vpfe_calc_format_size(vpfe, fmt, f); } static int vpfe_s_fmt(struct file *file, void *priv, struct v4l2_format *fmt) { struct vpfe_device *vpfe = video_drvdata(file); - struct v4l2_format format; - unsigned int bpp; + struct vpfe_fmt *f; + struct v4l2_mbus_framefmt mbus_fmt; int ret; /* If streaming is started, return error */ @@ -1492,25 +1497,32 @@ static int vpfe_s_fmt(struct file *file, void *priv, return -EBUSY; } - ret = __vpfe_get_format(vpfe, &format, &bpp); - if (ret) + ret = vpfe_try_fmt(file, priv, fmt); + if (ret < 0) return ret; + f = find_format_by_pix(vpfe, fmt->fmt.pix.pixelformat); - if (!cmp_v4l2_format(fmt, &format)) { - /* Sensor format is different from the requested format - * so we need to change it - */ - ret = __vpfe_set_format(vpfe, fmt, &bpp); - if (ret) - return ret; - } else /* Just make sure all of the fields are consistent */ - *fmt = format; + v4l2_fill_mbus_format(&mbus_fmt, &fmt->fmt.pix, f->code); - /* First detach any IRQ if currently attached */ - vpfe_detach_irq(vpfe); - vpfe->fmt = *fmt; - vpfe->bpp = bpp; + ret = __subdev_set_format(vpfe, &mbus_fmt); + if (ret) + return ret; + + /* Just double check nothing has gone wrong */ + if (mbus_fmt.code != f->code) { + vpfe_dbg(3, vpfe, + "%s subdev changed format on us, this should not happen\n", + __func__); + return -EINVAL; + } + + v4l2_fill_pix_format(&vpfe->fmt.fmt.pix, &mbus_fmt); + vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vpfe->fmt.fmt.pix.pixelformat = f->fourcc; + vpfe_calc_format_size(vpfe, f, &vpfe->fmt); + *fmt = vpfe->fmt; + vpfe->current_vpfe_fmt = f; /* Update the crop window based on found values */ vpfe->crop.width = fmt->fmt.pix.width; @@ -1525,43 +1537,28 @@ static int vpfe_enum_size(struct file *file, void *priv, { struct vpfe_device *vpfe = video_drvdata(file); struct v4l2_subdev_frame_size_enum fse; - struct vpfe_subdev_info *sdinfo; - struct v4l2_mbus_framefmt mbus; - struct v4l2_pix_format pix; + struct v4l2_subdev *sd = vpfe->current_subdev->sd; struct vpfe_fmt *fmt; int ret; /* check for valid format */ fmt = find_format_by_pix(vpfe, fsize->pixel_format); if (!fmt) { - vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", - fsize->pixel_format); + vpfe_dbg(3, vpfe, "Invalid pixel code: %x\n", + fsize->pixel_format); return -EINVAL; } memset(fsize->reserved, 0x0, sizeof(fsize->reserved)); - sdinfo = vpfe->current_subdev; - if (!sdinfo->sd) - return -EINVAL; - - memset(&pix, 0x0, sizeof(pix)); - /* Construct pix from parameter and use default for the rest */ - pix.pixelformat = fsize->pixel_format; - pix.width = 640; - pix.height = 480; - pix.colorspace = V4L2_COLORSPACE_SRGB; - pix.field = V4L2_FIELD_NONE; - pix_to_mbus(vpfe, &pix, &mbus); - memset(&fse, 0x0, sizeof(fse)); fse.index = fsize->index; fse.pad = 0; - fse.code = mbus.code; + fse.code = fmt->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse); + ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); if (ret) - return -EINVAL; + return ret; vpfe_dbg(1, vpfe, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", __func__, fse.index, fse.code, fse.min_width, fse.max_width, @@ -2050,6 +2047,7 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) struct vpfe_device *vpfe = video_drvdata(file); struct v4l2_rect cr = vpfe->crop; struct v4l2_rect r = s->r; + u32 bpp; /* If streaming is started, return error */ if (vb2_is_busy(&vpfe->buffer_queue)) { @@ -2075,10 +2073,12 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) s->r = vpfe->crop = r; - vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, vpfe->bpp); + bpp = __get_bytesperpixel(vpfe, vpfe->current_vpfe_fmt); + vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, bpp); vpfe->fmt.fmt.pix.width = r.width; vpfe->fmt.fmt.pix.height = r.height; - vpfe->fmt.fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc); + vpfe->fmt.fmt.pix.bytesperline = + vpfe_ccdc_get_line_length(&vpfe->ccdc); vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline * vpfe->fmt.fmt.pix.height; diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index 6c7bcb837f4e..b08b8e19c46b 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -281,8 +281,8 @@ struct vpfe_device { struct vpfe_cap_buffer *next_frm; /* Used to store pixel format */ struct v4l2_format fmt; - /* Used to store current bytes per pixel based on current format */ - unsigned int bpp; + /* Used to keep a reference to the current vpfe_fmt */ + struct vpfe_fmt *current_vpfe_fmt; struct vpfe_fmt *active_fmt[VPFE_NUM_FORMATS]; unsigned int num_active_fmt; -- cgit v1.2.3 From ea169d2d656100cb76c5bf163eeb5e435efe58b7 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:52 -0300 Subject: media: am437x-vpfe: Remove per bus width static data The bus related static data included in the vpfe_fmt static table can be derived dynamically instead. This simplify the table and it's use. We instead replace the per bus data info with just the usual bit per pixel value for each supported pixel format. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 56 ++++++++--------------------- drivers/media/platform/am437x/am437x-vpfe.h | 16 ++------- 2 files changed, 16 insertions(+), 56 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index e5ce9c8431ee..ff507501057e 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -73,73 +73,43 @@ static struct vpfe_fmt formats[VPFE_NUM_FORMATS] = { { .fourcc = V4L2_PIX_FMT_YUYV, .code = MEDIA_BUS_FMT_YUYV8_2X8, - .l.width = 10, - .l.bpp = 4, - .s.width = 8, - .s.bpp = 2, + .bitsperpixel = 16, }, { .fourcc = V4L2_PIX_FMT_UYVY, .code = MEDIA_BUS_FMT_UYVY8_2X8, - .l.width = 10, - .l.bpp = 4, - .s.width = 8, - .s.bpp = 2, + .bitsperpixel = 16, }, { .fourcc = V4L2_PIX_FMT_YVYU, .code = MEDIA_BUS_FMT_YVYU8_2X8, - .l.width = 10, - .l.bpp = 4, - .s.width = 8, - .s.bpp = 2, + .bitsperpixel = 16, }, { .fourcc = V4L2_PIX_FMT_VYUY, .code = MEDIA_BUS_FMT_VYUY8_2X8, - .l.width = 10, - .l.bpp = 4, - .s.width = 8, - .s.bpp = 2, + .bitsperpixel = 16, }, { .fourcc = V4L2_PIX_FMT_SBGGR8, .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .l.width = 10, - .l.bpp = 2, - .s.width = 8, - .s.bpp = 1, + .bitsperpixel = 8, }, { .fourcc = V4L2_PIX_FMT_SGBRG8, .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .l.width = 10, - .l.bpp = 2, - .s.width = 8, - .s.bpp = 1, + .bitsperpixel = 8, }, { .fourcc = V4L2_PIX_FMT_SGRBG8, .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .l.width = 10, - .l.bpp = 2, - .s.width = 8, - .s.bpp = 1, + .bitsperpixel = 8, }, { .fourcc = V4L2_PIX_FMT_SRGGB8, .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .l.width = 10, - .l.bpp = 2, - .s.width = 8, - .s.bpp = 1, + .bitsperpixel = 8, }, { .fourcc = V4L2_PIX_FMT_RGB565, .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .l.width = 10, - .l.bpp = 4, - .s.width = 8, - .s.bpp = 2, + .bitsperpixel = 16, }, { .fourcc = V4L2_PIX_FMT_RGB565X, .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .l.width = 10, - .l.bpp = 4, - .s.width = 8, - .s.bpp = 2, + .bitsperpixel = 16, }, }; @@ -184,9 +154,11 @@ static unsigned int __get_bytesperpixel(struct vpfe_device *vpfe, { struct vpfe_subdev_info *sdinfo = vpfe->current_subdev; unsigned int bus_width = sdinfo->vpfe_param.bus_width; - u32 bpp; + u32 bpp, bus_width_bytes, clocksperpixel; - bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp; + bus_width_bytes = ALIGN(bus_width, 8) >> 3; + clocksperpixel = DIV_ROUND_UP(fmt->bitsperpixel, bus_width); + bpp = clocksperpixel * bus_width_bytes; return bpp; } diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index b08b8e19c46b..77ebffeec160 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -215,28 +215,16 @@ struct vpfe_ccdc { u32 ccdc_ctx[VPFE_REG_END / sizeof(u32)]; }; -/* - * struct bus_format - VPFE bus format information - * width: Bits per pixel (when transferred over a bus) - * bpp: Bytes per pixel (when stored in memory) - */ -struct bus_format { - unsigned int width; - unsigned int bpp; -}; - /* * struct vpfe_fmt - VPFE media bus format information * fourcc: V4L2 pixel format code * code: V4L2 media bus format code - * l: 10 bit bus format info - * s: 8 bit bus format info + * bitsperpixel: Bits per pixel over the bus */ struct vpfe_fmt { u32 fourcc; u32 code; - struct bus_format l; - struct bus_format s; + u32 bitsperpixel; }; /* -- cgit v1.2.3 From 724de7b5fe72188838bd1f049a85a1f37d8ede26 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Fri, 20 Sep 2019 14:05:53 -0300 Subject: media: am437x-vpfe: Switch to SPDX Licensing Switch to SPDX licensing and drop the redundant GPL text. Signed-off-by: Benoit Parrot Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 14 +------------- drivers/media/platform/am437x/am437x-vpfe.h | 14 +------------- drivers/media/platform/am437x/am437x-vpfe_regs.h | 10 +--------- 3 files changed, 3 insertions(+), 35 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index ff507501057e..447610b67db4 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI VPFE capture Driver * @@ -5,19 +6,6 @@ * * Benoit Parrot * Lad, Prabhakar - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h index 77ebffeec160..05ee37db0273 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.h +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -1,21 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2013 - 2014 Texas Instruments, Inc. * * Benoit Parrot * Lad, Prabhakar - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef AM437X_VPFE_H diff --git a/drivers/media/platform/am437x/am437x-vpfe_regs.h b/drivers/media/platform/am437x/am437x-vpfe_regs.h index 0746c48ec23f..63ecdca3b908 100644 --- a/drivers/media/platform/am437x/am437x-vpfe_regs.h +++ b/drivers/media/platform/am437x/am437x-vpfe_regs.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI AM437x Image Sensor Interface Registers * @@ -5,15 +6,6 @@ * * Benoit Parrot * Lad, Prabhakar - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef AM437X_VPFE_REGS_H -- cgit v1.2.3 From a9cc4cbcdfd378b65fd4e398800cfa14e3855042 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 31 Aug 2019 03:42:58 -0300 Subject: media: seco-cec: Add a missing 'release_region()' in an error handling path At the beginning of the probe function, we have a call to 'request_muxed_region(BRA_SMB_BASE_ADDR, 7, "CEC00001")()' A corresponding 'release_region()' is performed in the remove function but is lacking in the error handling path. Add it. Fixes: b03c2fb97adc ("media: add SECO cec driver") Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/seco-cec/seco-cec.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/seco-cec/seco-cec.c b/drivers/media/platform/seco-cec/seco-cec.c index 9cd60fe1867c..a86b6e8f9196 100644 --- a/drivers/media/platform/seco-cec/seco-cec.c +++ b/drivers/media/platform/seco-cec/seco-cec.c @@ -675,6 +675,7 @@ err_notifier: err_delete_adapter: cec_delete_adapter(secocec->cec_adap); err: + release_region(BRA_SMB_BASE_ADDR, 7); dev_err(dev, "%s device probe failed\n", dev_name(dev)); return ret; -- cgit v1.2.3 From 5914ecf4b18fcc242e81ebbae255bb32ba2f3da2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 6 Sep 2019 05:11:33 -0300 Subject: media: s3c-camif: make array 'registers' static const, makes object smaller Don't populate the array 'registers' on the stack but instead make it static const. Makes the object code smaller by 45 bytes. Before: text data bss dec hex filename 17364 5000 0 22364 575c platform/s3c-camif/camif-regs.o After: text data bss dec hex filename 17255 5064 0 22319 572f platform/s3c-camif/camif-regs.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s3c-camif/camif-regs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c index 1a65532dc36d..e80204f5720c 100644 --- a/drivers/media/platform/s3c-camif/camif-regs.c +++ b/drivers/media/platform/s3c-camif/camif-regs.c @@ -553,7 +553,7 @@ void camif_hw_disable_capture(struct camif_vp *vp) void camif_hw_dump_regs(struct camif_dev *camif, const char *label) { - struct { + static const struct { u32 offset; const char * const name; } registers[] = { -- cgit v1.2.3 From ba0c8e6eed0712d69a6199d2797a5cc2ee07db57 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 6 Sep 2019 05:58:39 -0300 Subject: media: em28xx: make various arrays static const, makes object smaller Don't populate the arrays on the stack but instead make them static const. Makes the object code smaller by 767 bytes. Before: text data bss dec hex filename 41567 15088 192 56847 de0f em28xx/em28xx-dvb.o After: text data bss dec hex filename 39872 16016 192 56080 db10 em28xx/em28xx-dvb.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index a73faf12f7e4..0ab6c493bc74 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -471,13 +471,13 @@ static void hauppauge_hvr930c_init(struct em28xx *dev) { int i; - struct em28xx_reg_seq hauppauge_hvr930c_init[] = { + static const struct em28xx_reg_seq hauppauge_hvr930c_init[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0x65}, {EM2874_R80_GPIO_P0_CTRL, 0xfb, 0xff, 0x32}, {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0xb8}, { -1, -1, -1, -1}, }; - struct em28xx_reg_seq hauppauge_hvr930c_end[] = { + static const struct em28xx_reg_seq hauppauge_hvr930c_end[] = { {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x01}, {EM2874_R80_GPIO_P0_CTRL, 0xaf, 0xff, 0x65}, {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x76}, @@ -493,7 +493,7 @@ static void hauppauge_hvr930c_init(struct em28xx *dev) { -1, -1, -1, -1}, }; - struct { + static const struct { unsigned char r[4]; int len; } regs[] = { @@ -537,20 +537,20 @@ static void hauppauge_hvr930c_init(struct em28xx *dev) static void terratec_h5_init(struct em28xx *dev) { int i; - struct em28xx_reg_seq terratec_h5_init[] = { + static const struct em28xx_reg_seq terratec_h5_init[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xf2, 0xff, 50}, {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, { -1, -1, -1, -1}, }; - struct em28xx_reg_seq terratec_h5_end[] = { + static const struct em28xx_reg_seq terratec_h5_end[] = { {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 50}, {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, { -1, -1, -1, -1}, }; - struct { + static const struct { unsigned char r[4]; int len; } regs[] = { @@ -594,14 +594,14 @@ static void terratec_htc_stick_init(struct em28xx *dev) * 0xe6: unknown (does not affect DVB-T). * 0xb6: unknown (does not affect DVB-T). */ - struct em28xx_reg_seq terratec_htc_stick_init[] = { + static const struct em28xx_reg_seq terratec_htc_stick_init[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 50}, {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100}, { -1, -1, -1, -1}, }; - struct em28xx_reg_seq terratec_htc_stick_end[] = { + static const struct em28xx_reg_seq terratec_htc_stick_end[] = { {EM2874_R80_GPIO_P0_CTRL, 0xb6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 50}, { -1, -1, -1, -1}, @@ -611,7 +611,7 @@ static void terratec_htc_stick_init(struct em28xx *dev) * Init the analog decoder (not yet supported), but * it's probably still a good idea. */ - struct { + static const struct { unsigned char r[4]; int len; } regs[] = { @@ -642,14 +642,14 @@ static void terratec_htc_usb_xs_init(struct em28xx *dev) { int i; - struct em28xx_reg_seq terratec_htc_usb_xs_init[] = { + static const struct em28xx_reg_seq terratec_htc_usb_xs_init[] = { {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10}, {EM2874_R80_GPIO_P0_CTRL, 0xb2, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xb2, 0xff, 50}, {EM2874_R80_GPIO_P0_CTRL, 0xb6, 0xff, 100}, { -1, -1, -1, -1}, }; - struct em28xx_reg_seq terratec_htc_usb_xs_end[] = { + static const struct em28xx_reg_seq terratec_htc_usb_xs_end[] = { {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 50}, {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100}, @@ -660,7 +660,7 @@ static void terratec_htc_usb_xs_init(struct em28xx *dev) * Init the analog decoder (not yet supported), but * it's probably still a good idea. */ - struct { + static const struct { unsigned char r[4]; int len; } regs[] = { @@ -704,7 +704,7 @@ static void pctv_520e_init(struct em28xx *dev) * digital demodulator and tuner are routed via AVF4910B. */ int i; - struct { + static const struct { unsigned char r[4]; int len; } regs[] = { @@ -800,7 +800,7 @@ static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) static void px_bcud_init(struct em28xx *dev) { int i; - struct { + static const struct { unsigned char r[4]; int len; } regs1[] = { @@ -818,7 +818,7 @@ static void px_bcud_init(struct em28xx *dev) {{ 0x85, 0x7a }, 2}, {{ 0x87, 0x04 }, 2}, }; - static struct em28xx_reg_seq gpio[] = { + static const struct em28xx_reg_seq gpio[] = { {EM28XX_R06_I2C_CLK, 0x40, 0xff, 300}, {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 60}, {EM28XX_R15_RGAIN, 0x20, 0xff, 0}, -- cgit v1.2.3 From dda8415e17c99ad3d6ac56d8aa722198f816fc74 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Fri, 6 Sep 2019 08:55:01 -0300 Subject: media: mtk-vcodec: vdec: fix incorrect pointer dereference mtk_q_data::fmt is actually a pointer and must be dereferenced as such. This went under the radar because mtk_v4l2_debug() evaluates to nothing unless DEBUG is defined. [acourbot: split into its own commit] Signed-off-by: Yunfei Dong Signed-off-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 26a55c3e807e..653f111532ab 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -284,7 +284,7 @@ static void mtk_vdec_update_fmt(struct mtk_vcodec_ctx *ctx, fmt = &mtk_video_formats[k]; if (fmt->fourcc == pixelformat) { mtk_v4l2_debug(1, "Update cap fourcc(%d -> %d)", - dst_q_data->fmt.fourcc, pixelformat); + dst_q_data->fmt->fourcc, pixelformat); dst_q_data->fmt = fmt; return; } -- cgit v1.2.3 From 2abb4db289d757cddf2bf8aae34be65bad653bb7 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Sep 2019 08:55:02 -0300 Subject: media: mtk-vcodec: vdec: set VPI IPI handler in one place Each of the supported decoder formats used to set the same RPC interrupt handler by themselves, even though this could be done by the IF init function itself. Move it to the right place and stop making its symbol public. Signed-off-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c | 1 - drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c | 1 - drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c | 1 - drivers/media/platform/mtk-vcodec/vdec_vpu_if.c | 9 ++++++++- drivers/media/platform/mtk-vcodec/vdec_vpu_if.h | 9 --------- 5 files changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c index 49aa85a9bb5a..50048c170b99 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c @@ -283,7 +283,6 @@ static int vdec_h264_init(struct mtk_vcodec_ctx *ctx) inst->vpu.id = IPI_VDEC_H264; inst->vpu.dev = ctx->dev->vpu_plat_dev; inst->vpu.ctx = ctx; - inst->vpu.handler = vpu_dec_ipi_handler; err = vpu_dec_init(&inst->vpu); if (err) { diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c index 63a8708ce682..6011fdd60a22 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c @@ -402,7 +402,6 @@ static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) inst->vpu.id = IPI_VDEC_VP8; inst->vpu.dev = ctx->dev->vpu_plat_dev; inst->vpu.ctx = ctx; - inst->vpu.handler = vpu_dec_ipi_handler; err = vpu_dec_init(&inst->vpu); if (err) { diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c index 5066c283d86d..24c1f0bf2147 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c @@ -793,7 +793,6 @@ static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) inst->vpu.id = IPI_VDEC_VP9; inst->vpu.dev = ctx->dev->vpu_plat_dev; inst->vpu.ctx = ctx; - inst->vpu.handler = vpu_dec_ipi_handler; if (vpu_dec_init(&inst->vpu)) { mtk_vcodec_err(inst, "vp9_dec_vpu_init failed"); diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c index 3f38cc4509ef..70abfd4cd4b9 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c @@ -25,10 +25,16 @@ static void handle_init_ack_msg(struct vdec_vpu_ipi_init_ack *msg) } /* + * vpu_dec_ipi_handler - Handler for VPU ipi message. + * + * @data: ipi message + * @len : length of ipi message + * @priv: callback private data which is passed by decoder when register. + * * This function runs in interrupt context and it means there's an IPI MSG * from VPU. */ -void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) +static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) { struct vdec_vpu_ipi_ack *msg = data; struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) @@ -102,6 +108,7 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) mtk_vcodec_debug_enter(vpu); init_waitqueue_head(&vpu->wq); + vpu->handler = vpu_dec_ipi_handler; err = vpu_ipi_register(vpu->dev, vpu->id, vpu->handler, "vdec", NULL); if (err != 0) { diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h index b76f717e4fd7..f779b0676fbd 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h @@ -76,13 +76,4 @@ int vpu_dec_deinit(struct vdec_vpu_inst *vpu); */ int vpu_dec_reset(struct vdec_vpu_inst *vpu); -/** - * vpu_dec_ipi_handler - Handler for VPU ipi message. - * - * @data: ipi message - * @len : length of ipi message - * @priv: callback private data which is passed by decoder when register. - */ -void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv); - #endif -- cgit v1.2.3 From 1c7b5eedb9884dc012a9146d523f7c5194e757d5 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 6 Sep 2019 08:55:03 -0300 Subject: media: mtk-vcodec: vdec: clean up vidioc_vdec_s_fmt a bit Check for a potentially NULL pointer that was overlooked and use shorter accessors to the same data. While we are at it, sprinkle a few comments. Signed-off-by: Alexandre Courbot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 653f111532ab..858727824889 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -841,12 +841,20 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, return -EINVAL; pix_mp = &f->fmt.pix_mp; + /* + * Setting OUTPUT format after OUTPUT buffers are allocated is invalid + * if using the stateful API. + */ if ((f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && vb2_is_busy(&ctx->m2m_ctx->out_q_ctx.q)) { mtk_v4l2_err("out_q_ctx buffers already requested"); ret = -EBUSY; } + /* + * Setting CAPTURE format after CAPTURE buffers are allocated is + * invalid. + */ if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && vb2_is_busy(&ctx->m2m_ctx->cap_q_ctx.q)) { mtk_v4l2_err("cap_q_ctx buffers already requested"); @@ -865,6 +873,8 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, fmt = mtk_vdec_find_format(f); } } + if (fmt == NULL) + return -EINVAL; q_data->fmt = fmt; vidioc_try_fmt(f, q_data->fmt); @@ -873,10 +883,10 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, q_data->coded_width = pix_mp->width; q_data->coded_height = pix_mp->height; - ctx->colorspace = f->fmt.pix_mp.colorspace; - ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; - ctx->quantization = f->fmt.pix_mp.quantization; - ctx->xfer_func = f->fmt.pix_mp.xfer_func; + ctx->colorspace = pix_mp->colorspace; + ctx->ycbcr_enc = pix_mp->ycbcr_enc; + ctx->quantization = pix_mp->quantization; + ctx->xfer_func = pix_mp->xfer_func; if (ctx->state == MTK_STATE_FREE) { ret = vdec_if_init(ctx, q_data->fmt->fourcc); -- cgit v1.2.3 From 2455d417c03aa0cbafed04c46cbb354643238318 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Sun, 8 Sep 2019 01:12:54 -0300 Subject: media: vim2m: Fix BUG_ON in vim2m_device_release() If v4l2_m2m_init() fails, m2m_dev pointer will be set ERR_PTR(-ENOMEM), then kfree m2m_dev will trigger BUG_ON, see below, fix it by setting m2m_dev to NULL. vim2m vim2m.0: Failed to init mem2mem device ------------[ cut here ]------------ kernel BUG at mm/slub.c:3944! invalid opcode: 0000 [#1] SMP PTI CPU: 11 PID: 9061 Comm: insmod Tainted: G E 5.2.0-rc2 #81 Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011 RIP: 0010:kfree+0x11a/0x160 Call Trace: vim2m_device_release+0x3f/0x50 [vim2m] device_release+0x27/0x80 kobject_release+0x68/0x190 vim2m_probe+0x20f/0x280 [vim2m] platform_drv_probe+0x37/0x90 really_probe+0xef/0x3d0 driver_probe_device+0x110/0x120 device_driver_attach+0x4f/0x60 __driver_attach+0x9a/0x140 ? device_driver_attach+0x60/0x60 bus_for_each_dev+0x76/0xc0 ? klist_add_tail+0x57/0x70 bus_add_driver+0x141/0x210 driver_register+0x5b/0xe0 vim2m_init+0x29/0x1000 [vim2m] do_one_initcall+0x46/0x1f4 ? __slab_alloc+0x1c/0x30 ? kmem_cache_alloc_trace+0x167/0x1b0 do_init_module+0x5b/0x21f load_module+0x1add/0x1fb0 ? __do_sys_finit_module+0xe9/0x110 __do_sys_finit_module+0xe9/0x110 do_syscall_64+0x5b/0x1c0 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fixes: ea6c7e34f3b2 ("media: vim2m: replace devm_kzalloc by kzalloc") Reported-by: Hulk Robot Cc: Laurent Pinchart Signed-off-by: Kefeng Wang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index acd3bd48c7e2..eb8f398d41f4 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1343,6 +1343,7 @@ static int vim2m_probe(struct platform_device *pdev) if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(dev->m2m_dev); + dev->m2m_dev = NULL; goto error_dev; } -- cgit v1.2.3 From 1adbb8276f216df6b06e2989ebf9eb3427b01030 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 9 Sep 2019 04:16:27 -0300 Subject: media: v4l2-dv-timings.c: fix format string It should be "%u.%02u" instead of "%u.%u". Signed-off-by: Hans Verkuil Reported-by: Bard Winther Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 4f23e939ead0..0607a5d0d051 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -293,7 +293,7 @@ void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, if (prefix == NULL) prefix = ""; - pr_info("%s: %s%ux%u%s%u.%u (%ux%u)\n", dev_prefix, prefix, + pr_info("%s: %s%ux%u%s%u.%02u (%ux%u)\n", dev_prefix, prefix, bt->width, bt->height, bt->interlaced ? "i" : "p", fps / 100, fps % 100, htot, vtot); -- cgit v1.2.3 From b4add02d2236fd5f568db141cfd8eb4290972eb3 Mon Sep 17 00:00:00 2001 From: Vandana BN Date: Mon, 9 Sep 2019 06:43:31 -0300 Subject: media: vivid: Set vid_cap_streaming and vid_out_streaming to true When vbi stream is started, followed by video streaming, the vid_cap_streaming and vid_out_streaming were not being set to true, which would cause the video stream to stop when vbi stream is stopped. This patch allows to set vid_cap_streaming and vid_out_streaming to true. According to Hans Verkuil it appears that these 'if (dev->kthread_vid_cap)' checks are a left-over from the original vivid development and should never have been there. Signed-off-by: Vandana BN Cc: # for v3.18 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-vid-cap.c | 3 --- drivers/media/platform/vivid/vivid-vid-out.c | 3 --- 2 files changed, 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index 8cbaa0c998ed..2d030732feac 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -223,9 +223,6 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) if (vb2_is_streaming(&dev->vb_vid_out_q)) dev->can_loop_video = vivid_vid_can_loop(dev); - if (dev->kthread_vid_cap) - return 0; - dev->vid_cap_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); for (i = 0; i < VIDEO_MAX_FRAME; i++) diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 148b663a6075..a0364ac497f9 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -161,9 +161,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) if (vb2_is_streaming(&dev->vb_vid_cap_q)) dev->can_loop_video = vivid_vid_can_loop(dev); - if (dev->kthread_vid_out) - return 0; - dev->vid_out_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); if (dev->start_streaming_error) { -- cgit v1.2.3 From c362f77a243bfd1daec21b6c36491c061ee2f31b Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Thu, 12 Sep 2019 15:55:55 -0300 Subject: media: vim2m: Fix abort issue Currently, if start streaming -> stop streaming -> start streaming sequence is executed, driver will end job prematurely, if ctx->translen is higher than 1, because "aborting" flag is still set from previous stop streaming command. Fix that by clearing "aborting" flag in start streaming handler. Fixes: 96d8eab5d0a1 ("V4L/DVB: [v5,2/2] v4l: Add a mem-to-mem videobuf framework test device") Signed-off-by: Jernej Skrabec Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index eb8f398d41f4..e17792f837f8 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1073,6 +1073,9 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned int count) if (!q_data) return -EINVAL; + if (V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->aborting = 0; + q_data->sequence = 0; return 0; } -- cgit v1.2.3 From a0862a40364e2f87109317e31c51c9d7bc89e33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 4 Sep 2019 18:54:04 -0300 Subject: media: rcar-vin: Fix incorrect return statement in rvin_try_format() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While refactoring code the return statement became corrupted, fix it by returning the correct return code. Reported-by: Kieran Bingham Fixes: 897e371389e77514 ("media: rcar-vin: simplify how formats are set and reset" Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index cbc1c07f0a96..ec2796413e26 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -208,6 +208,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format); if (ret < 0 && ret != -ENOIOCTLCMD) goto done; + ret = 0; v4l2_fill_pix_format(pix, &format.format); @@ -242,7 +243,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, done: v4l2_subdev_free_pad_config(pad_cfg); - return 0; + return ret; } static int rvin_querycap(struct file *file, void *priv, -- cgit v1.2.3 From d23e12dc4e26b5a642a47756f260a6623053b784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 4 Sep 2019 18:54:05 -0300 Subject: media: rcar-vin: Make use of V4L2_FIELD_IS_INTERLACED() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The V4L2_FIELD_IS_INTERLACED() can be used to make the code more readable, use it. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 3cb29b2e0b2b..7d40d71c8632 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -557,16 +557,11 @@ static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin) rvin_write(vin, 0, VNSPPOC_REG); rvin_write(vin, 0, VNSLPOC_REG); rvin_write(vin, vin->format.width - 1, VNEPPOC_REG); - switch (vin->format.field) { - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: + + if (V4L2_FIELD_IS_INTERLACED(vin->format.field)) rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG); - break; - default: + else rvin_write(vin, vin->format.height - 1, VNELPOC_REG); - break; - } vin_dbg(vin, "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n", -- cgit v1.2.3 From 20aca4a33bc4c35e8e157db4b653dd14780a33de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 4 Sep 2019 18:54:06 -0300 Subject: media: rcar-vin: Rename rectangle holding the video source information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable to hold the video source information dimensions was poorly named 'source'. This is confusing as a lot of other members of structs share the same name with different purposes, rename it src_rect in preparation of refactoring code. Signed-off-by: Niklas Söderlund Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 33 +++++++++++++++-------------- drivers/media/platform/rcar-vin/rcar-vin.h | 4 ++-- 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index ec2796413e26..4d01a13ad14e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -166,13 +166,13 @@ static int rvin_reset_format(struct rvin_dev *vin) rvin_format_align(vin, &vin->format); - vin->source.top = 0; - vin->source.left = 0; - vin->source.width = vin->format.width; - vin->source.height = vin->format.height; + vin->src_rect.top = 0; + vin->src_rect.left = 0; + vin->src_rect.width = vin->format.width; + vin->src_rect.height = vin->format.height; - vin->crop = vin->source; - vin->compose = vin->source; + vin->crop = vin->src_rect; + vin->compose = vin->src_rect; return 0; } @@ -285,7 +285,7 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv, vin->format = f->fmt.pix; vin->crop = crop; vin->compose = compose; - vin->source = crop; + vin->src_rect = crop; return 0; } @@ -323,8 +323,8 @@ static int rvin_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: s->r.left = s->r.top = 0; - s->r.width = vin->source.width; - s->r.height = vin->source.height; + s->r.width = vin->src_rect.width; + s->r.height = vin->src_rect.height; break; case V4L2_SEL_TGT_CROP: s->r = vin->crop; @@ -366,21 +366,22 @@ static int rvin_s_selection(struct file *file, void *fh, case V4L2_SEL_TGT_CROP: /* Can't crop outside of source input */ max_rect.top = max_rect.left = 0; - max_rect.width = vin->source.width; - max_rect.height = vin->source.height; + max_rect.width = vin->src_rect.width; + max_rect.height = vin->src_rect.height; v4l2_rect_map_inside(&r, &max_rect); - v4l_bound_align_image(&r.width, 6, vin->source.width, 0, - &r.height, 2, vin->source.height, 0, 0); + v4l_bound_align_image(&r.width, 6, vin->src_rect.width, 0, + &r.height, 2, vin->src_rect.height, 0, 0); - r.top = clamp_t(s32, r.top, 0, vin->source.height - r.height); - r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width); + r.top = clamp_t(s32, r.top, 0, + vin->src_rect.height - r.height); + r.left = clamp_t(s32, r.left, 0, vin->src_rect.width - r.width); vin->crop = s->r = r; vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", r.width, r.height, r.left, r.top, - vin->source.width, vin->source.height); + vin->src_rect.width, vin->src_rect.height); break; case V4L2_SEL_TGT_COMPOSE: /* Make sure compose rect fits inside output format */ diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index e562c2ff21ec..86e9bad44484 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -176,7 +176,7 @@ struct rvin_info { * * @crop: active cropping * @compose: active composing - * @source: active size of the video source + * @src_rect: active size of the video source * @std: active video standard of the video source * * @alpha: Alpha component to fill in for supported pixel formats @@ -215,7 +215,7 @@ struct rvin_dev { struct v4l2_rect crop; struct v4l2_rect compose; - struct v4l2_rect source; + struct v4l2_rect src_rect; v4l2_std_id std; unsigned int alpha; -- cgit v1.2.3 From 104464f573d5e1c58e2901fce209fc4682b6f8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 4 Sep 2019 18:54:07 -0300 Subject: media: rcar-vin: Do not reset the crop and compose rectangles in s_fmt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The crop and compose rectangles are reset when s_fmt is called resulting in potentially valid rectangles being lost when updating the format. Fix this by mapping the rectangles inside the new format. Signed-off-by: Niklas Söderlund Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 4d01a13ad14e..3f175e2e0a9a 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -283,8 +283,8 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv, return ret; vin->format = f->fmt.pix; - vin->crop = crop; - vin->compose = compose; + v4l2_rect_map_inside(&vin->crop, &crop); + v4l2_rect_map_inside(&vin->compose, &compose); vin->src_rect = crop; return 0; -- cgit v1.2.3 From 083693214f28b979b50cfcd689010725ca579eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 4 Sep 2019 18:54:08 -0300 Subject: media: rcar-vin: Add support for V4L2_FIELD_ALTERNATE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware is capable to passing V4L2_FIELD_ALTERNATE to user-space. Allow users to request this field format but still default to using the hardware interlacer if alternating is not explicitly requested. Before this change a sensor providing data using alternate would be always combined to an interlaced frame. After this change the user can request to receive frames as alternate if the sensor provides it. Signed-off-by: Niklas Söderlund Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 43 +++++++++++++++++------------ drivers/media/platform/rcar-vin/rcar-v4l2.c | 31 +++++++++------------ 2 files changed, 39 insertions(+), 35 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 7d40d71c8632..c7859b329dd4 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -529,12 +529,17 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin) { + unsigned int crop_height; u32 xs, ys; /* Set scaling coefficient */ + crop_height = vin->crop.height; + if (V4L2_FIELD_IS_INTERLACED(vin->format.field)) + crop_height *= 2; + ys = 0; - if (vin->crop.height != vin->compose.height) - ys = (4096 * vin->crop.height) / vin->compose.height; + if (crop_height != vin->compose.height) + ys = (4096 * crop_height) / vin->compose.height; rvin_write(vin, ys, VNYS_REG); xs = 0; @@ -578,21 +583,9 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) /* Set Start/End Pixel/Line Pre-Clip */ rvin_write(vin, vin->crop.left, VNSPPRC_REG); rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG); + rvin_write(vin, vin->crop.top, VNSLPRC_REG); + rvin_write(vin, vin->crop.top + vin->crop.height - 1, VNELPRC_REG); - switch (vin->format.field) { - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG); - rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1, - VNELPRC_REG); - break; - default: - rvin_write(vin, vin->crop.top, VNSLPRC_REG); - rvin_write(vin, vin->crop.top + vin->crop.height - 1, - VNELPRC_REG); - break; - } /* TODO: Add support for the UDS scaler. */ if (vin->info->model != RCAR_GEN3) @@ -636,6 +629,9 @@ static int rvin_setup(struct rvin_dev *vin) vnmc = VNMC_IM_ODD_EVEN; progressive = true; break; + case V4L2_FIELD_ALTERNATE: + vnmc = VNMC_IM_ODD_EVEN; + break; default: vnmc = VNMC_IM_ODD; break; @@ -794,6 +790,18 @@ static bool rvin_capture_active(struct rvin_dev *vin) return rvin_read(vin, VNMS_REG) & VNMS_CA; } +static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms) +{ + if (vin->format.field == V4L2_FIELD_ALTERNATE) { + /* If FS is set it is an Even field. */ + if (vnms & VNMS_FS) + return V4L2_FIELD_BOTTOM; + return V4L2_FIELD_TOP; + } + + return vin->format.field; +} + static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) { const struct rvin_video_format *fmt; @@ -943,7 +951,7 @@ static irqreturn_t rvin_irq(int irq, void *data) /* Capture frame */ if (vin->queue_buf[slot]) { - vin->queue_buf[slot]->field = vin->format.field; + vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); vin->queue_buf[slot]->sequence = vin->sequence; vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, @@ -1070,6 +1078,7 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: case V4L2_FIELD_NONE: + case V4L2_FIELD_ALTERNATE: break; case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 3f175e2e0a9a..a7ee44dd248e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -117,15 +117,7 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - break; case V4L2_FIELD_ALTERNATE: - /* - * Driver does not (yet) support outputting ALTERNATE to a - * userspace. It does support outputting INTERLACED so use - * the VIN hardware to combine the two fields. - */ - pix->field = V4L2_FIELD_INTERLACED; - pix->height *= 2; break; default: pix->field = RVIN_DEFAULT_FIELD; @@ -164,15 +156,25 @@ static int rvin_reset_format(struct rvin_dev *vin) v4l2_fill_pix_format(&vin->format, &fmt.format); - rvin_format_align(vin, &vin->format); - vin->src_rect.top = 0; vin->src_rect.left = 0; vin->src_rect.width = vin->format.width; vin->src_rect.height = vin->format.height; + /* Make use of the hardware interlacer by default. */ + if (vin->format.field == V4L2_FIELD_ALTERNATE) { + vin->format.field = V4L2_FIELD_INTERLACED; + vin->format.height *= 2; + } + + rvin_format_align(vin, &vin->format); + vin->crop = vin->src_rect; - vin->compose = vin->src_rect; + + vin->compose.top = 0; + vin->compose.left = 0; + vin->compose.width = vin->format.width; + vin->compose.height = vin->format.height; return 0; } @@ -217,13 +219,6 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, crop->left = 0; crop->width = pix->width; crop->height = pix->height; - - /* - * If source is ALTERNATE the driver will use the VIN hardware - * to INTERLACE it. The crop height then needs to be doubled. - */ - if (pix->field == V4L2_FIELD_ALTERNATE) - crop->height *= 2; } if (field != V4L2_FIELD_ANY) -- cgit v1.2.3 From b52c2ed31332ad446156626e20c226e6bcea8d2b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 5 Sep 2019 04:33:40 -0300 Subject: media: cec/cec-adap.c: use new UI_CMD defines Instead of hardcoding the UI Command key values, use the new defines. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 5ef7daeb8cbd..284f9b845161 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -1976,7 +1976,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, * Play function, this message can have variable length * depending on the specific play function that is used. */ - case 0x60: + case CEC_OP_UI_CMD_PLAY_FUNCTION: if (msg->len == 2) rc_keydown(adap->rc, RC_PROTO_CEC, msg->msg[2], 0); @@ -1993,8 +1993,12 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, * For the time being these messages are not processed by the * framework and are simply forwarded to the user space. */ - case 0x56: case 0x57: - case 0x67: case 0x68: case 0x69: case 0x6a: + case CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE: + case CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION: + case CEC_OP_UI_CMD_TUNE_FUNCTION: + case CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION: + case CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION: + case CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION: break; default: rc_keydown(adap->rc, RC_PROTO_CEC, msg->msg[2], 0); -- cgit v1.2.3 From 9098c1c251ff0e6a4cea3a60486d9f40e7fdcea2 Mon Sep 17 00:00:00 2001 From: Dariusz Marcinkiewicz Date: Fri, 23 Aug 2019 09:20:58 -0300 Subject: media: cec: expose the new connector info API Until now the connector info API was a kernel-internal API only. This moves it to the public API and adds the new ioctl to retrieve this information. Signed-off-by: Dariusz Marcinkiewicz Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 2 ++ drivers/media/cec/cec-api.c | 20 ++++++++++++++++++++ drivers/media/cec/cec-core.c | 5 ----- 3 files changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 284f9b845161..9340435a94a0 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -319,6 +319,8 @@ static void cec_post_state_event(struct cec_adapter *adap) ev.state_change.phys_addr = adap->phys_addr; ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; + ev.state_change.have_conn_info = + adap->conn_info.type != CEC_CONNECTOR_TYPE_NO_CONNECTOR; cec_queue_event(adap, &ev); } diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 12d676484472..17d1cb2e5f97 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -187,6 +187,21 @@ static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh, return 0; } +static long cec_adap_g_connector_info(struct cec_adapter *adap, + struct cec_log_addrs __user *parg) +{ + int ret = 0; + + if (!(adap->capabilities & CEC_CAP_CONNECTOR_INFO)) + return -ENOTTY; + + mutex_lock(&adap->lock); + if (copy_to_user(parg, &adap->conn_info, sizeof(adap->conn_info))) + ret = -EFAULT; + mutex_unlock(&adap->lock); + return ret; +} + static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh, bool block, struct cec_msg __user *parg) { @@ -506,6 +521,9 @@ static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case CEC_ADAP_S_LOG_ADDRS: return cec_adap_s_log_addrs(adap, fh, block, parg); + case CEC_ADAP_G_CONNECTOR_INFO: + return cec_adap_g_connector_info(adap, parg); + case CEC_TRANSMIT: return cec_transmit(adap, fh, block, parg); @@ -578,6 +596,8 @@ static int cec_open(struct inode *inode, struct file *filp) /* Queue up initial state events */ ev.state_change.phys_addr = adap->phys_addr; ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; + ev.state_change.have_conn_info = + adap->conn_info.type != CEC_CONNECTOR_TYPE_NO_CONNECTOR; cec_queue_event_fh(fh, &ev, 0); #ifdef CONFIG_CEC_PIN if (adap->pin && adap->pin->ops->read_hpd) { diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index 9c610e1e99b8..db7adffcdc76 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -257,11 +257,6 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, struct cec_adapter *adap; int res; - /* - * Disable this capability until the connector info public API - * is ready. - */ - caps &= ~CEC_CAP_CONNECTOR_INFO; #ifndef CONFIG_MEDIA_CEC_RC caps &= ~CEC_CAP_RC; #endif -- cgit v1.2.3 From 7e86efa2ff03db92067b2f6f4fa3f1f8b8da7c76 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 19 Sep 2019 08:15:53 -0300 Subject: media: cec-gpio: add notifier support Add support for cec-notifier to the cec-gpio driver. This makes it possible to associate the CEC gpio pin with an HDMI connector. This feature was always documented in the cec-gpio bindings: Documentation/devicetree/bindings/media/cec-gpio.txt But support for the hdmi-phandle property was never actually implemented in this driver. This patch adds support for this property. It also fixes a few incorrect error returns in the probe() function, which skipped the call to cec_delete_adapter(). Tested on a Raspberry Pi 3B with a modified vc4 driver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 1 + drivers/media/platform/cec-gpio/cec-gpio.c | 41 +++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f1f61419fd29..1175f122bb1d 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -588,6 +588,7 @@ config CEC_GPIO depends on PREEMPT || COMPILE_TEST select CEC_CORE select CEC_PIN + select CEC_NOTIFIER select GPIOLIB help This is a generic GPIO-based CEC driver. diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c index 5b17d3a31896..7be91e712c4a 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -8,10 +8,12 @@ #include #include #include +#include #include struct cec_gpio { struct cec_adapter *adap; + struct cec_notifier *notifier; struct device *dev; struct gpio_desc *cec_gpio; @@ -173,9 +175,17 @@ static const struct cec_pin_ops cec_gpio_pin_ops = { static int cec_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device *hdmi_dev; struct cec_gpio *cec; + u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; int ret; + hdmi_dev = cec_notifier_parse_hdmi_phandle(dev); + if (PTR_ERR(hdmi_dev) == -EPROBE_DEFER) + return PTR_ERR(hdmi_dev); + if (IS_ERR(hdmi_dev)) + caps |= CEC_CAP_PHYS_ADDR; + cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); if (!cec) return -ENOMEM; @@ -196,8 +206,7 @@ static int cec_gpio_probe(struct platform_device *pdev) return PTR_ERR(cec->v5_gpio); cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops, - cec, pdev->name, CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | - CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN); + cec, pdev->name, caps); if (IS_ERR(cec->adap)) return PTR_ERR(cec->adap); @@ -205,7 +214,7 @@ static int cec_gpio_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, cec->adap->name, cec); if (ret) - return ret; + goto del_adap; cec_gpio_disable_irq(cec->adap); @@ -218,7 +227,7 @@ static int cec_gpio_probe(struct platform_device *pdev) IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "hpd-gpio", cec); if (ret) - return ret; + goto del_adap; } if (cec->v5_gpio) { @@ -230,23 +239,37 @@ static int cec_gpio_probe(struct platform_device *pdev) IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "v5-gpio", cec); if (ret) - return ret; + goto del_adap; } - ret = cec_register_adapter(cec->adap, &pdev->dev); - if (ret) { - cec_delete_adapter(cec->adap); - return ret; + if (!IS_ERR(hdmi_dev)) { + cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL, + cec->adap); + if (!cec->notifier) { + ret = -ENOMEM; + goto del_adap; + } } + ret = cec_register_adapter(cec->adap, &pdev->dev); + if (ret) + goto unreg_notifier; + platform_set_drvdata(pdev, cec); return 0; + +unreg_notifier: + cec_notifier_cec_adap_unregister(cec->notifier); +del_adap: + cec_delete_adapter(cec->adap); + return ret; } static int cec_gpio_remove(struct platform_device *pdev) { struct cec_gpio *cec = platform_get_drvdata(pdev); + cec_notifier_cec_adap_unregister(cec->notifier); cec_unregister_adapter(cec->adap); return 0; } -- cgit v1.2.3 From 7c617138b8254a6bb60bfb5b8fc53eb8b3d6c3ab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 5 Sep 2019 08:31:11 -0300 Subject: media: cx231xx: convert to the vb2 framework This patch converts the cx231xx driver to the vb2 framework. Since you can't do a partial conversion this is a big-bang patch, i.e. large and hard to review. I never found a way around this. Signed-off-by: Hans Verkuil Co-developed-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/Kconfig | 2 +- drivers/media/usb/cx231xx/cx231xx-417.c | 509 ++++++------------- drivers/media/usb/cx231xx/cx231xx-cards.c | 6 +- drivers/media/usb/cx231xx/cx231xx-vbi.c | 172 +++---- drivers/media/usb/cx231xx/cx231xx-vbi.h | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 795 +++++++----------------------- drivers/media/usb/cx231xx/cx231xx.h | 30 +- 7 files changed, 407 insertions(+), 1109 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 74f3b29d9c60..2fe2b2d335ba 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -4,7 +4,7 @@ config VIDEO_CX231XX depends on VIDEO_DEV && I2C && I2C_MUX select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEOBUF_VMALLOC + select VIDEOBUF2_VMALLOC select VIDEO_CX25840 select VIDEO_CX2341X diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 6d218a036966..46d0215deee6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -60,10 +61,6 @@ #define MCI_MODE_MEMORY_READ 0x000 #define MCI_MODE_MEMORY_WRITE 0x4000 -static unsigned int mpegbufs = 8; -module_param(mpegbufs, int, 0644); -MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32"); - static unsigned int mpeglines = 128; module_param(mpeglines, int, 0644); MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32"); @@ -1080,16 +1077,6 @@ static int cx231xx_load_firmware(struct cx231xx *dev) return 0; } -static void cx231xx_417_check_encoder(struct cx231xx *dev) -{ - u32 status, seq; - - status = 0; - seq = 0; - cx231xx_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq); - dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq); -} - static void cx231xx_codec_settings(struct cx231xx *dev) { dprintk(1, "%s()\n", __func__); @@ -1227,40 +1214,25 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) /* ------------------------------------------------------------------ */ -static int bb_buf_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) { - struct cx231xx_fh *fh = q->priv_data; + struct cx231xx *dev = vb2_get_drv_priv(vq); + unsigned int size = mpeglinesize * mpeglines; - fh->dev->ts1.ts_packet_size = mpeglinesize; - fh->dev->ts1.ts_packet_count = mpeglines; + dev->ts1.ts_packet_size = mpeglinesize; + dev->ts1.ts_packet_count = mpeglines; - *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; - *count = mpegbufs; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - unsigned long flags = 0; + if (vq->num_buffers + *nbuffers < CX231XX_MIN_BUF) + *nbuffers = CX231XX_MIN_BUF - vq->num_buffers; - BUG_ON(in_interrupt()); + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = mpeglinesize * mpeglines; - spin_lock_irqsave(&dev->video_mode.slock, flags); - if (dev->USE_ISO) { - if (dev->video_mode.isoc_ctl.buf == buf) - dev->video_mode.isoc_ctl.buf = NULL; - } else { - if (dev->video_mode.bulk_ctl.buf == buf) - dev->video_mode.bulk_ctl.buf = NULL; - } - spin_unlock_irqrestore(&dev->video_mode.slock, flags); - videobuf_waiton(vq, &buf->vb, 0, 0); - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + return 0; } static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb, @@ -1276,13 +1248,13 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur return; buf = list_entry(dma_q->active.next, - struct cx231xx_buffer, vb.queue); + struct cx231xx_buffer, list); dev->video_mode.isoc_ctl.buf = buf; dma_q->mpeg_buffer_done = 1; } /* Fill buffer */ buf = dev->video_mode.isoc_ctl.buf; - vbuf = videobuf_to_vmalloc(&buf->vb); + vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); if ((dma_q->mpeg_buffer_completed+len) < mpeglines*mpeglinesize) { @@ -1306,11 +1278,10 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur memcpy(vbuf+dma_q->mpeg_buffer_completed, data, tail_data); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - buf->vb.ts = ktime_get_ns(); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.sequence = dma_q->sequence++; + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); dma_q->mpeg_buffer_completed = 0; if (len - tail_data > 0) { @@ -1331,17 +1302,15 @@ static void buffer_filled(char *data, int len, struct urb *urb, if (list_empty(&dma_q->active)) return; - buf = list_entry(dma_q->active.next, - struct cx231xx_buffer, vb.queue); + buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list); /* Fill buffer */ - vbuf = videobuf_to_vmalloc(&buf->vb); + vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); memcpy(vbuf, data, len); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - buf->vb.ts = ktime_get_ns(); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + buf->vb.sequence = dma_q->sequence++; + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } static int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) @@ -1394,100 +1363,104 @@ static int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) return 0; } -static int bb_buf_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, enum v4l2_field field) +static void buffer_queue(struct vb2_buffer *vb) { - struct cx231xx_fh *fh = q->priv_data; struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - int rc = 0, urb_init = 0; - int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; + container_of(vb, struct cx231xx_buffer, vb.vb2_buf); + struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + unsigned long flags; - if (0 != buf->vb.baddr && buf->vb.bsize < size) - return -EINVAL; - buf->vb.width = fh->dev->ts1.ts_packet_size; - buf->vb.height = fh->dev->ts1.ts_packet_count; - buf->vb.size = size; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; - } + spin_lock_irqsave(&dev->video_mode.slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->video_mode.slock, flags); +} - if (dev->USE_ISO) { - if (!dev->video_mode.isoc_ctl.num_bufs) - urb_init = 1; - } else { - if (!dev->video_mode.bulk_ctl.num_bufs) - urb_init = 1; +static void return_all_buffers(struct cx231xx *dev, + enum vb2_buffer_state state) +{ + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + struct cx231xx_buffer *buf, *node; + unsigned long flags; + + spin_lock_irqsave(&dev->video_mode.slock, flags); + list_for_each_entry_safe(buf, node, &vidq->active, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); } - dev_dbg(dev->dev, - "urb_init=%d dev->video_mode.max_pkt_size=%d\n", - urb_init, dev->video_mode.max_pkt_size); + spin_unlock_irqrestore(&dev->video_mode.slock, flags); +} + +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cx231xx *dev = vb2_get_drv_priv(vq); + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + int ret = 0; + + vidq->sequence = 0; dev->mode_tv = 1; - if (urb_init) { - rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - rc = cx231xx_unmute_audio(dev); - if (dev->USE_ISO) { - cx231xx_set_alt_setting(dev, INDEX_TS1, 4); - rc = cx231xx_init_isoc(dev, mpeglines, - mpegbufs, - dev->ts1_mode.max_pkt_size, - cx231xx_isoc_copy); - } else { - cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - rc = cx231xx_init_bulk(dev, mpeglines, - mpegbufs, - dev->ts1_mode.max_pkt_size, - cx231xx_bulk_copy); - } - if (rc < 0) - goto fail; - } + cx231xx_set_alt_setting(dev, INDEX_VANC, 1); + cx231xx_set_gpio_value(dev, 2, 0); - buf->vb.state = VIDEOBUF_PREPARED; - return 0; + cx231xx_initialize_codec(dev); + + cx231xx_start_TS1(dev); -fail: - free_buffer(q, buf); - return rc; + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + if (dev->USE_ISO) + ret = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->ts1_mode.max_pkt_size, + cx231xx_isoc_copy); + else + ret = cx231xx_init_bulk(dev, 320, 5, + dev->ts1_mode.max_pkt_size, + cx231xx_bulk_copy); + if (ret) + return_all_buffers(dev, VB2_BUF_STATE_QUEUED); + + call_all(dev, video, s_stream, 1); + return ret; } -static void bb_buf_queue(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void stop_streaming(struct vb2_queue *vq) { - struct cx231xx_fh *fh = q->priv_data; + struct cx231xx *dev = vb2_get_drv_priv(vq); + unsigned long flags; - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + call_all(dev, video, s_stream, 0); - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + cx231xx_stop_TS1(dev); -} + /* do this before setting alternate! */ + if (dev->USE_ISO) + cx231xx_uninit_isoc(dev); + else + cx231xx_uninit_bulk(dev); + cx231xx_set_mode(dev, CX231XX_SUSPEND); -static void bb_buf_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) -{ - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - /*struct cx231xx_fh *fh = q->priv_data;*/ - /*struct cx231xx *dev = (struct cx231xx *)fh->dev;*/ + cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX231xx_END_NOW, CX231xx_MPEG_CAPTURE, + CX231xx_RAW_BITS_NONE); - free_buffer(q, buf); + spin_lock_irqsave(&dev->video_mode.slock, flags); + if (dev->USE_ISO) + dev->video_mode.isoc_ctl.buf = NULL; + else + dev->video_mode.bulk_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->video_mode.slock, flags); + return_all_buffers(dev, VB2_BUF_STATE_ERROR); } -static const struct videobuf_queue_ops cx231xx_qops = { - .buf_setup = bb_buf_setup, - .buf_prepare = bb_buf_prepare, - .buf_queue = bb_buf_queue, - .buf_release = bb_buf_release, +static struct vb2_ops cx231xx_video_qops = { + .queue_setup = queue_setup, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* ------------------------------------------------------------------ */ @@ -1495,8 +1468,7 @@ static const struct videobuf_queue_ops cx231xx_qops = { static int vidioc_g_pixelaspect(struct file *file, void *priv, int type, struct v4l2_fract *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); bool is_50hz = dev->encodernorm.id & V4L2_STD_625_50; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1511,8 +1483,7 @@ static int vidioc_g_pixelaspect(struct file *file, void *priv, static int vidioc_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1533,8 +1504,7 @@ static int vidioc_g_selection(struct file *file, void *priv, static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm) { - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); *norm = dev->encodernorm.id; return 0; @@ -1542,8 +1512,7 @@ static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) { - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); unsigned int i; for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++) @@ -1575,8 +1544,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctl) { - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); struct v4l2_subdev *sd; dprintk(3, "enter vidioc_s_ctrl()\n"); @@ -1601,8 +1569,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); dprintk(3, "enter vidioc_g_fmt_vid_cap()\n"); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; @@ -1621,8 +1588,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); dprintk(3, "enter vidioc_try_fmt_vid_cap()\n"); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; @@ -1636,230 +1602,21 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_reqbufs(&fh->vidq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_querybuf(&fh->vidq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_qbuf(&fh->vidq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - - return videobuf_dqbuf(&fh->vidq, b, file->f_flags & O_NONBLOCK); -} - - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - dprintk(3, "enter vidioc_streamon()\n"); - cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); - if (dev->USE_ISO) - cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, - CX231XX_NUM_BUFS, - dev->video_mode.max_pkt_size, - cx231xx_isoc_copy); - else { - cx231xx_init_bulk(dev, 320, - 5, - dev->ts1_mode.max_pkt_size, - cx231xx_bulk_copy); - } - dprintk(3, "exit vidioc_streamon()\n"); - return videobuf_streamon(&fh->vidq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx231xx_fh *fh = file->private_data; - - return videobuf_streamoff(&fh->vidq); -} - static int vidioc_log_status(struct file *file, void *priv) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); call_all(dev, core, log_status); return v4l2_ctrl_log_status(file, priv); } -static int mpeg_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx231xx *dev = video_drvdata(file); - struct cx231xx_fh *fh; - - dprintk(2, "%s()\n", __func__); - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - mutex_unlock(&dev->lock); - return -ENOMEM; - } - - file->private_data = fh; - v4l2_fh_init(&fh->fh, vdev); - fh->dev = dev; - - - videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops, - NULL, &dev->video_mode.slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, - sizeof(struct cx231xx_buffer), fh, &dev->lock); -/* - videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops, - dev->dev, &dev->ts1.slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx231xx_buffer), - fh, &dev->lock); -*/ - - cx231xx_set_alt_setting(dev, INDEX_VANC, 1); - cx231xx_set_gpio_value(dev, 2, 0); - - cx231xx_initialize_codec(dev); - - mutex_unlock(&dev->lock); - v4l2_fh_add(&fh->fh); - cx231xx_start_TS1(dev); - - return 0; -} - -static int mpeg_release(struct file *file) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - dprintk(3, "mpeg_release()! dev=0x%p\n", dev); - - mutex_lock(&dev->lock); - - cx231xx_stop_TS1(dev); - - /* do this before setting alternate! */ - if (dev->USE_ISO) - cx231xx_uninit_isoc(dev); - else - cx231xx_uninit_bulk(dev); - cx231xx_set_mode(dev, CX231XX_SUSPEND); - - cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - CX231xx_END_NOW, CX231xx_MPEG_CAPTURE, - CX231xx_RAW_BITS_NONE); - - /* FIXME: Review this crap */ - /* Shut device down on last close */ - if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { - if (atomic_dec_return(&dev->v4l_reader_count) == 0) { - /* stop mpeg capture */ - - msleep(500); - cx231xx_417_check_encoder(dev); - - } - } - - if (fh->vidq.streaming) - videobuf_streamoff(&fh->vidq); - if (fh->vidq.reading) - videobuf_read_stop(&fh->vidq); - - videobuf_mmap_free(&fh->vidq); - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - kfree(fh); - mutex_unlock(&dev->lock); - return 0; -} - -static ssize_t mpeg_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - - /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ - /* Start mpeg encoder on first read. */ - if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { - if (atomic_inc_return(&dev->v4l_reader_count) == 1) { - if (cx231xx_initialize_codec(dev) < 0) - return -EINVAL; - } - } - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static __poll_t mpeg_poll(struct file *file, - struct poll_table_struct *wait) -{ - __poll_t req_events = poll_requested_events(wait); - struct cx231xx_fh *fh = file->private_data; - struct cx231xx *dev = fh->dev; - __poll_t res = 0; - - if (v4l2_event_pending(&fh->fh)) - res |= EPOLLPRI; - else - poll_wait(file, &fh->fh.wait, wait); - - if (!(req_events & (EPOLLIN | EPOLLRDNORM))) - return res; - - mutex_lock(&dev->lock); - res |= videobuf_poll_stream(file, &fh->vidq, wait); - mutex_unlock(&dev->lock); - return res; -} - -static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx231xx_fh *fh = file->private_data; - - dprintk(2, "%s()\n", __func__); - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - static const struct v4l2_file_operations mpeg_fops = { .owner = THIS_MODULE, - .open = mpeg_open, - .release = mpeg_release, - .read = mpeg_read, - .poll = mpeg_poll, - .mmap = mpeg_mmap, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -1881,12 +1638,12 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = vidioc_log_status, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = cx231xx_g_register, @@ -1980,6 +1737,7 @@ int cx231xx_417_register(struct cx231xx *dev) /* FIXME: Port1 hardcoded here */ int err = -ENODEV; struct cx231xx_tsport *tsport = &dev->ts1; + struct vb2_queue *q; dprintk(1, "%s()\n", __func__); @@ -2017,6 +1775,21 @@ int cx231xx_417_register(struct cx231xx *dev) /* Allocate and initialize V4L video device */ cx231xx_video_dev_init(dev, dev->udev, &dev->v4l_device, &cx231xx_mpeg_template, "mpeg"); + q = &dev->mpegq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx231xx_buffer); + q->ops = &cx231xx_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 1; + q->lock = &dev->lock; + err = vb2_queue_init(q); + if (err) + return err; + dev->v4l_device.queue = q; + err = video_register_device(&dev->v4l_device, VFL_TYPE_GRABBER, -1); if (err < 0) { diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index e123e74c549e..92efe6c1f47b 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1479,13 +1479,11 @@ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev, goto err_dev_init; } - /* init video dma queues */ + /* init video dma queue */ INIT_LIST_HEAD(&dev->video_mode.vidq.active); - INIT_LIST_HEAD(&dev->video_mode.vidq.queued); - /* init vbi dma queues */ + /* init vbi dma queue */ INIT_LIST_HEAD(&dev->vbi_mode.vidq.active); - INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued); /* Reset other chips required if they are tied up with GPIO pins */ cx231xx_add_into_devlist(dev); diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index fba7ccdf5a25..d2f143a096d1 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -153,131 +153,98 @@ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) Vbi buf operations ------------------------------------------------------------------*/ -static int -vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int vbi_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) { - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = vb2_get_drv_priv(vq); u32 height = 0; height = ((dev->norm & V4L2_STD_625_50) ? PAL_VBI_LINES : NTSC_VBI_LINES); - *size = (dev->width * height * 2 * 2); - if (0 == *count) - *count = CX231XX_DEF_VBI_BUF; - - if (*count < CX231XX_MIN_BUF) - *count = CX231XX_MIN_BUF; - + *nplanes = 1; + sizes[0] = (dev->width * height * 2 * 2); return 0; } /* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) -{ - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - unsigned long flags = 0; - BUG_ON(in_interrupt()); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->vbi_mode.slock, flags); - if (dev->vbi_mode.bulk_ctl.buf == buf) - dev->vbi_mode.bulk_ctl.buf = NULL; - spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +static int vbi_buf_prepare(struct vb2_buffer *vb) { - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - int rc = 0, urb_init = 0; + struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue); u32 height = 0; + u32 size; height = ((dev->norm & V4L2_STD_625_50) ? PAL_VBI_LINES : NTSC_VBI_LINES); - buf->vb.size = ((dev->width << 1) * height * 2); + size = ((dev->width << 1) * height * 2); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < size) return -EINVAL; - - buf->vb.width = dev->width; - buf->vb.height = height; - buf->vb.field = field; - buf->vb.field = V4L2_FIELD_SEQ_TB; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - if (!dev->vbi_mode.bulk_ctl.num_bufs) - urb_init = 1; - - if (urb_init) { - rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS, - CX231XX_NUM_VBI_BUFS, - dev->vbi_mode.alt_max_pkt_size[0], - cx231xx_isoc_vbi_copy); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; + vb2_set_plane_payload(vb, 0, size); return 0; - -fail: - free_buffer(vq, buf); - return rc; } -static void -vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static void vbi_buf_queue(struct vb2_buffer *vb) { + struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; + container_of(vb, struct cx231xx_buffer, vb.vb2_buf); struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq; + unsigned long flags; - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + spin_lock_irqsave(&dev->vbi_mode.slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); +} + +static void return_all_buffers(struct cx231xx *dev, + enum vb2_buffer_state state) +{ + struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq; + struct cx231xx_buffer *buf, *node; + unsigned long flags; + spin_lock_irqsave(&dev->vbi_mode.slock, flags); + dev->vbi_mode.bulk_ctl.buf = NULL; + list_for_each_entry_safe(buf, node, &vidq->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); } -static void vbi_buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static int vbi_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = vb2_get_drv_priv(vq); + struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq; + int ret; + + vidq->sequence = 0; + ret = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS, + CX231XX_NUM_VBI_BUFS, + dev->vbi_mode.alt_max_pkt_size[0], + cx231xx_isoc_vbi_copy); + if (ret) + return_all_buffers(dev, VB2_BUF_STATE_QUEUED); + return ret; +} +static void vbi_stop_streaming(struct vb2_queue *vq) +{ + struct cx231xx *dev = vb2_get_drv_priv(vq); - free_buffer(vq, buf); + return_all_buffers(dev, VB2_BUF_STATE_ERROR); } -const struct videobuf_queue_ops cx231xx_vbi_qops = { - .buf_setup = vbi_buffer_setup, - .buf_prepare = vbi_buffer_prepare, - .buf_queue = vbi_buffer_queue, - .buf_release = vbi_buffer_release, +struct vb2_ops cx231xx_vbi_qops = { + .queue_setup = vbi_queue_setup, + .buf_prepare = vbi_buf_prepare, + .buf_queue = vbi_buf_queue, + .start_streaming = vbi_start_streaming, + .stop_streaming = vbi_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* ------------------------------------------------------------------ @@ -512,16 +479,15 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, struct cx231xx_buffer *buf) { /* Advice that buffer was filled */ - /* dev_dbg(dev->dev, "[%p/%d] wakeup\n", buf, buf->vb.i); */ + /* dev_dbg(dev->dev, "[%p/%d] wakeup\n", buf, buf->vb.index); */ - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - buf->vb.ts = ktime_get_ns(); + buf->vb.sequence = dma_q->sequence++; + buf->vb.vb2_buf.timestamp = ktime_get_ns(); dev->vbi_mode.bulk_ctl.buf = NULL; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, @@ -611,11 +577,11 @@ static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); + outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0); + memset(outp, 0, vb2_plane_size(&(*buf)->vb.vb2_buf, 0)); dev->vbi_mode.bulk_ctl.buf = *buf; @@ -656,7 +622,7 @@ int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, if (buf == NULL) return -EINVAL; - p_out_buffer = videobuf_to_vmalloc(&buf->vb); + p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); if (dma_q->bytes_left_in_line != _line_size) { current_line_bytes_copied = diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.h b/drivers/media/usb/cx231xx/cx231xx-vbi.h index 7cddd629fbfc..0b21bee5fa30 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.h +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.h @@ -10,7 +10,7 @@ #ifndef _CX231XX_VBI_H #define _CX231XX_VBI_H -extern const struct videobuf_queue_ops cx231xx_vbi_qops; +extern struct vb2_ops cx231xx_vbi_qops; #define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ #define NTSC_VBI_END_LINE 21 diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 9b51f07a729e..69abafaebbf3 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -58,10 +58,10 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(CX231XX_VERSION); -static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U }; +static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U }; +static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U }; +static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U }; module_param_array(card, int, NULL, 0444); module_param_array(video_nr, int, NULL, 0444); @@ -166,18 +166,19 @@ static inline void buffer_filled(struct cx231xx *dev, struct cx231xx_buffer *buf) { /* Advice that buffer was filled */ - cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - buf->vb.ts = ktime_get_ns(); + cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.vb2_buf.index); + buf->vb.sequence = dma_q->sequence++; + buf->vb.field = V4L2_FIELD_INTERLACED; + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, dev->size); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = NULL; else dev->video_mode.bulk_ctl.buf = NULL; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } static inline void print_err_status(struct cx231xx *dev, int packet, int status) @@ -241,11 +242,11 @@ static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); + outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0); + memset(outp, 0, dev->size); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = *buf; @@ -653,7 +654,7 @@ int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, if (buf == NULL) return -1; - p_out_buffer = videobuf_to_vmalloc(&buf->vb); + p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line; @@ -672,7 +673,7 @@ int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? bytes_to_copy : dma_q->bytes_left_in_line; - if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size)) + if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + dev->size)) return 0; /* The below copies the UYVY data straight into video buffer */ @@ -708,149 +709,98 @@ u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q) Videobuf operations ------------------------------------------------------------------*/ -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) { - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = vb2_get_drv_priv(vq); - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3; - if (0 == *count) - *count = CX231XX_DEF_BUF; + dev->size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - if (*count < CX231XX_MIN_BUF) - *count = CX231XX_MIN_BUF; + if (vq->num_buffers + *nbuffers < CX231XX_MIN_BUF) + *nbuffers = CX231XX_MIN_BUF - vq->num_buffers; - - cx231xx_enable_analog_tuner(dev); + if (*nplanes) + return sizes[0] < dev->size ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = dev->size; return 0; } -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +static void buffer_queue(struct vb2_buffer *vb) { - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; - unsigned long flags = 0; - - BUG_ON(in_interrupt()); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb.vb2_buf); + struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + unsigned long flags; - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ spin_lock_irqsave(&dev->video_mode.slock, flags); - if (dev->USE_ISO) { - if (dev->video_mode.isoc_ctl.buf == buf) - dev->video_mode.isoc_ctl.buf = NULL; - } else { - if (dev->video_mode.bulk_ctl.buf == buf) - dev->video_mode.bulk_ctl.buf = NULL; - } + list_add_tail(&buf->list, &vidq->active); spin_unlock_irqrestore(&dev->video_mode.slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; } -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +static void return_all_buffers(struct cx231xx *dev, + enum vb2_buffer_state state) { - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx *dev = fh->dev; - int rc = 0, urb_init = 0; - - /* The only currently supported format is 16 bits/pixel */ - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth - + 7) >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + struct cx231xx_buffer *buf, *node; + unsigned long flags; - if (dev->USE_ISO) { - if (!dev->video_mode.isoc_ctl.num_bufs) - urb_init = 1; - } else { - if (!dev->video_mode.bulk_ctl.num_bufs) - urb_init = 1; - } - dev_dbg(dev->dev, - "urb_init=%d dev->video_mode.max_pkt_size=%d\n", - urb_init, dev->video_mode.max_pkt_size); - if (urb_init) { - dev->mode_tv = 0; - if (dev->USE_ISO) - rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, - CX231XX_NUM_BUFS, - dev->video_mode.max_pkt_size, - cx231xx_isoc_copy); - else - rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS, - CX231XX_NUM_BUFS, - dev->video_mode.max_pkt_size, - cx231xx_bulk_copy); - if (rc < 0) - goto fail; + spin_lock_irqsave(&dev->video_mode.slock, flags); + if (dev->USE_ISO) + dev->video_mode.isoc_ctl.buf = NULL; + else + dev->video_mode.bulk_ctl.buf = NULL; + list_for_each_entry_safe(buf, node, &vidq->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); } - - buf->vb.state = VIDEOBUF_PREPARED; - - return 0; - -fail: - free_buffer(vq, buf); - return rc; + spin_unlock_irqrestore(&dev->video_mode.slock, flags); } -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static int start_streaming(struct vb2_queue *vq, unsigned int count) { - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = vb2_get_drv_priv(vq); struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + int ret = 0; - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + vidq->sequence = 0; + dev->mode_tv = 0; + cx231xx_enable_analog_tuner(dev); + if (dev->USE_ISO) + ret = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_isoc_copy); + else + ret = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_bulk_copy); + if (ret) + return_all_buffers(dev, VB2_BUF_STATE_QUEUED); + call_all(dev, video, s_stream, 1); + return ret; } -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void stop_streaming(struct vb2_queue *vq) { - struct cx231xx_buffer *buf = - container_of(vb, struct cx231xx_buffer, vb); - struct cx231xx_fh *fh = vq->priv_data; - struct cx231xx *dev = (struct cx231xx *)fh->dev; - - cx231xx_isocdbg("cx231xx: called buffer_release\n"); + struct cx231xx *dev = vb2_get_drv_priv(vq); - free_buffer(vq, buf); + call_all(dev, video, s_stream, 0); + return_all_buffers(dev, VB2_BUF_STATE_ERROR); } -static const struct videobuf_queue_ops cx231xx_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, +static struct vb2_ops cx231xx_video_qops = { + .queue_setup = queue_setup, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /********************* v4l2 interface **************************************/ @@ -872,58 +822,6 @@ void video_mux(struct cx231xx *dev, int index) cx231xx_do_mode_ctrl_overrides(dev); } -/* Usage lock check functions */ -static int res_get(struct cx231xx_fh *fh) -{ - struct cx231xx *dev = fh->dev; - int rc = 0; - - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (dev->stream_on) - return -EBUSY; - dev->stream_on = 1; - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (dev->vbi_stream_on) - return -EBUSY; - dev->vbi_stream_on = 1; - } else - return -EINVAL; - - fh->stream_on = 1; - - return rc; -} - -static int res_check(struct cx231xx_fh *fh) -{ - return fh->stream_on; -} - -static void res_free(struct cx231xx_fh *fh) -{ - struct cx231xx *dev = fh->dev; - - fh->stream_on = 0; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - dev->stream_on = 0; - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - dev->vbi_stream_on = 0; -} - -static int check_dev(struct cx231xx *dev) -{ - if (dev->state & DEV_DISCONNECTED) { - dev_err(dev->dev, "v4l2 ioctl: device not present\n"); - return -ENODEV; - } - return 0; -} - /* ------------------------------------------------------------------ IOCTL vidioc handling ------------------------------------------------------------------*/ @@ -931,8 +829,7 @@ static int check_dev(struct cx231xx *dev) static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; @@ -960,8 +857,7 @@ static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc) static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); unsigned int width = f->fmt.pix.width; unsigned int height = f->fmt.pix.height; unsigned int maxw = norm_maxw(dev); @@ -993,39 +889,25 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - struct cx231xx_fmt *fmt; + struct cx231xx *dev = video_drvdata(file); struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; + int rc; - rc = check_dev(dev); - if (rc < 0) + rc = vidioc_try_fmt_vid_cap(file, priv, f); + if (rc) return rc; - vidioc_try_fmt_vid_cap(file, priv, f); - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (!fmt) - return -EINVAL; - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { + if (vb2_is_busy(&dev->vidq)) { dev_err(dev->dev, "%s: queue busy\n", __func__); return -EBUSY; } - if (dev->stream_on && !fh->stream_on) { - dev_err(dev->dev, - "%s: device in use by another fh\n", __func__); - return -EBUSY; - } - /* set new image size */ dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; - dev->format = fmt; + dev->format = format_by_fourcc(f->fmt.pix.pixelformat); v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED); call_all(dev, pad, set_fmt, NULL, &format); @@ -1036,8 +918,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); *id = dev->norm; return 0; @@ -1045,21 +926,15 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (dev->norm == norm) return 0; - if (videobuf_queue_is_busy(&fh->vb_vidq)) + if (vb2_is_busy(&dev->vidq)) return -EBUSY; dev->norm = norm; @@ -1141,8 +1016,7 @@ void cx231xx_v4l2_create_entities(struct cx231xx *dev) int cx231xx_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); u32 gen_stat; unsigned int n; int ret; @@ -1181,8 +1055,7 @@ int cx231xx_enum_input(struct file *file, void *priv, int cx231xx_g_input(struct file *file, void *priv, unsigned int *i) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); *i = dev->video_input; @@ -1191,14 +1064,9 @@ int cx231xx_g_input(struct file *file, void *priv, unsigned int *i) int cx231xx_s_input(struct file *file, void *priv, unsigned int i) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; + struct cx231xx *dev = video_drvdata(file); dev->mode_tv = 0; - rc = check_dev(dev); - if (rc < 0) - return rc; if (i >= MAX_CX231XX_INPUT) return -EINVAL; @@ -1220,13 +1088,7 @@ int cx231xx_s_input(struct file *file, void *priv, unsigned int i) int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; + struct cx231xx *dev = video_drvdata(file); if (0 != t->index) return -EINVAL; @@ -1244,27 +1106,15 @@ int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) int cx231xx_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - if (0 != t->index) return -EINVAL; -#if 0 - call_all(dev, tuner, s_tuner, t); -#endif return 0; } int cx231xx_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); if (f->tuner) return -EINVAL; @@ -1277,8 +1127,7 @@ int cx231xx_g_frequency(struct file *file, void *priv, int cx231xx_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); struct v4l2_frequency new_freq = *f; int rc; u32 if_frequency = 5400000; @@ -1287,10 +1136,6 @@ int cx231xx_s_frequency(struct file *file, void *priv, "Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n", f->frequency, f->type); - rc = check_dev(dev); - if (rc < 0) - return rc; - if (0 != f->tuner) return -EINVAL; @@ -1365,8 +1210,7 @@ int cx231xx_g_chip_info(struct file *file, void *fh, int cx231xx_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); int ret; u8 value[4] = { 0, 0, 0, 0 }; u32 data = 0; @@ -1424,8 +1268,7 @@ int cx231xx_g_register(struct file *file, void *priv, int cx231xx_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *reg) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); int ret; u8 data[4] = { 0, 0, 0, 0 }; @@ -1472,8 +1315,7 @@ int cx231xx_s_register(struct file *file, void *priv, static int vidioc_g_pixelaspect(struct file *file, void *priv, int type, struct v4l2_fract *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); bool is_50hz = dev->norm & V4L2_STD_625_50; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1488,8 +1330,7 @@ static int vidioc_g_pixelaspect(struct file *file, void *priv, static int vidioc_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1508,54 +1349,10 @@ static int vidioc_g_selection(struct file *file, void *priv, return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - rc = res_get(fh); - - if (likely(rc >= 0)) - rc = videobuf_streamon(&fh->vb_vidq); - - call_all(dev, video, s_stream, 1); - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (type != fh->type) - return -EINVAL; - - cx25840_call(dev, video, s_stream, 0); - - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - - return 0; -} - int cx231xx_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); strscpy(cap->driver, "cx231xx", sizeof(cap->driver)); strscpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); @@ -1587,8 +1384,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); f->fmt.vbi.sampling_rate = 6750000 * 4; f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; @@ -1610,8 +1406,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(file); f->fmt.vbi.sampling_rate = 6750000 * 4; f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; @@ -1634,77 +1429,16 @@ static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - - if (dev->vbi_stream_on && !fh->stream_on) { - dev_err(dev->dev, - "%s device in use by another fh\n", __func__); - return -EBUSY; - } return vidioc_try_fmt_vbi_cap(file, priv, f); } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_reqbufs(&fh->vb_vidq, rb); -} - -static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_querybuf(&fh->vb_vidq, b); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_qbuf(&fh->vb_vidq, b); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx231xx_fh *fh = priv; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); -} - /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + struct cx231xx *dev = video_drvdata(file); if (t->index) return -EINVAL; @@ -1717,7 +1451,7 @@ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) } static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + struct cx231xx *dev = video_drvdata(file); if (t->index) return -EINVAL; @@ -1733,52 +1467,20 @@ static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner */ static int cx231xx_v4l2_open(struct file *filp) { - int radio = 0; struct video_device *vdev = video_devdata(filp); struct cx231xx *dev = video_drvdata(filp); - struct cx231xx_fh *fh; - enum v4l2_buf_type fh_type = 0; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - case VFL_TYPE_RADIO: - radio = 1; - break; - default: - return -EINVAL; - } - - cx231xx_videodbg("open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[fh_type], - dev->users); - -#if 0 - errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); - if (errCode < 0) { - dev_err(dev->dev, - "Device locked on digital mode. Can't open analog\n"); - return -EBUSY; - } -#endif + int ret; - fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - if (mutex_lock_interruptible(&dev->lock)) { - kfree(fh); + if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; + + ret = v4l2_fh_open(filp); + if (ret) { + mutex_unlock(&dev->lock); + return ret; } - fh->dev = dev; - fh->type = fh_type; - filp->private_data = fh; - v4l2_fh_init(&fh->fh, vdev); - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + if (dev->users++ == 0) { /* Power up in Analog TV mode */ if (dev->board.external_av) cx231xx_set_power_mode(dev, @@ -1786,10 +1488,6 @@ static int cx231xx_v4l2_open(struct file *filp) else cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV); -#if 0 - cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); -#endif - /* set video alternate setting */ cx231xx_set_video_alternate(dev); @@ -1799,38 +1497,21 @@ static int cx231xx_v4l2_open(struct file *filp) /* device needs to be initialized before isoc transfer */ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; - } - if (radio) { + + if (vdev->vfl_type == VFL_TYPE_RADIO) { cx231xx_videodbg("video_open: setting radio device\n"); /* cx231xx_start_radio(dev); */ call_all(dev, tuner, s_radio); } - - dev->users++; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops, - NULL, &dev->video_mode.slock, - fh->type, V4L2_FIELD_INTERLACED, - sizeof(struct cx231xx_buffer), - fh, &dev->lock); - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (vdev->vfl_type == VFL_TYPE_VBI) { /* Set the required alternate setting VBI interface works in Bulk mode only */ cx231xx_set_alt_setting(dev, INDEX_VANC, 0); - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops, - NULL, &dev->vbi_mode.slock, - fh->type, V4L2_FIELD_SEQ_TB, - sizeof(struct cx231xx_buffer), - fh, &dev->lock); } mutex_unlock(&dev->lock); - v4l2_fh_add(&fh->fh); - return 0; } @@ -1871,68 +1552,12 @@ void cx231xx_release_analog_resources(struct cx231xx *dev) */ static int cx231xx_close(struct file *filp) { - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - - cx231xx_videodbg("users=%d\n", dev->users); - - cx231xx_videodbg("users=%d\n", dev->users); - if (res_check(fh)) - res_free(fh); - - /* - * To workaround error number=-71 on EP0 for VideoGrabber, - * need exclude following. - * FIXME: It is probably safe to remove most of these, as we're - * now avoiding the alternate setting for INDEX_VANC - */ - if (!dev->board.no_alt_vanc) - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - /* the device is already disconnect, - free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { - if (atomic_read(&dev->devlist_count) > 0) { - cx231xx_release_resources(dev); - fh->dev = NULL; - return 0; - } - return 0; - } - - /* do this before setting alternate! */ - cx231xx_uninit_vbi_isoc(dev); - - /* set alternate 0 */ - if (!dev->vbi_or_sliced_cc_mode) - cx231xx_set_alt_setting(dev, INDEX_VANC, 0); - else - cx231xx_set_alt_setting(dev, INDEX_HANC, 0); - - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - kfree(fh); - dev->users--; - wake_up_interruptible(&dev->open); - return 0; - } + struct cx231xx *dev = video_drvdata(filp); + struct video_device *vdev = video_devdata(filp); - v4l2_fh_del(&fh->fh); - dev->users--; - if (!dev->users) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - /* the device is already disconnect, - free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { - cx231xx_release_resources(dev); - fh->dev = NULL; - return 0; - } + _vb2_fop_release(filp, NULL); + if (--dev->users == 0) { /* Save some power by putting tuner to sleep */ call_all(dev, tuner, standby); @@ -1942,20 +1567,40 @@ static int cx231xx_close(struct file *filp) else cx231xx_uninit_bulk(dev); cx231xx_set_mode(dev, CX231XX_SUSPEND); + } + + /* + * To workaround error number=-71 on EP0 for VideoGrabber, + * need exclude following. + * FIXME: It is probably safe to remove most of these, as we're + * now avoiding the alternate setting for INDEX_VANC + */ + if (!dev->board.no_alt_vanc && vdev->vfl_type == VFL_TYPE_VBI) { + /* do this before setting alternate! */ + cx231xx_uninit_vbi_isoc(dev); + /* set alternate 0 */ + if (!dev->vbi_or_sliced_cc_mode) + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + else + cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + + wake_up_interruptible_nr(&dev->open, 1); + return 0; + } + + if (dev->users == 0) { /* set alternate 0 */ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); } - v4l2_fh_exit(&fh->fh); - kfree(fh); + wake_up_interruptible(&dev->open); return 0; } static int cx231xx_v4l2_close(struct file *filp) { - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; + struct cx231xx *dev = video_drvdata(filp); int rc; mutex_lock(&dev->lock); @@ -1964,116 +1609,13 @@ static int cx231xx_v4l2_close(struct file *filp) return rc; } -/* - * cx231xx_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t -cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || - (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) { - rc = res_get(fh); - - if (unlikely(rc < 0)) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - mutex_unlock(&dev->lock); - return rc; - } - return 0; -} - -/* - * cx231xx_v4l2_poll() - * will allocate buffers when called for the first time - */ -static __poll_t cx231xx_v4l2_poll(struct file *filp, poll_table *wait) -{ - __poll_t req_events = poll_requested_events(wait); - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - __poll_t res = 0; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return EPOLLERR; - - rc = res_get(fh); - - if (unlikely(rc < 0)) - return EPOLLERR; - - if (v4l2_event_pending(&fh->fh)) - res |= EPOLLPRI; - else - poll_wait(filp, &fh->fh.wait, wait); - - if (!(req_events & (EPOLLIN | EPOLLRDNORM))) - return res; - - if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) || - (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) { - mutex_lock(&dev->lock); - res |= videobuf_poll_stream(filp, &fh->vb_vidq, wait); - mutex_unlock(&dev->lock); - return res; - } - return res | EPOLLERR; -} - -/* - * cx231xx_v4l2_mmap() - */ -static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct cx231xx_fh *fh = filp->private_data; - struct cx231xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - rc = res_get(fh); - - if (unlikely(rc < 0)) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - mutex_unlock(&dev->lock); - - cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - - (unsigned long)vma->vm_start, rc); - - return rc; -} - static const struct v4l2_file_operations cx231xx_v4l_fops = { .owner = THIS_MODULE, .open = cx231xx_v4l2_open, .release = cx231xx_v4l2_close, - .read = cx231xx_v4l2_read, - .poll = cx231xx_v4l2_poll, - .mmap = cx231xx_v4l2_mmap, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -2088,17 +1630,17 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_g_pixelaspect = vidioc_g_pixelaspect, .vidioc_g_selection = vidioc_g_selection, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_s_std = vidioc_s_std, .vidioc_g_std = vidioc_g_std, .vidioc_enum_input = cx231xx_enum_input, .vidioc_g_input = cx231xx_g_input, .vidioc_s_input = cx231xx_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = cx231xx_g_tuner, .vidioc_s_tuner = cx231xx_s_tuner, .vidioc_g_frequency = cx231xx_g_frequency, @@ -2175,6 +1717,7 @@ static void cx231xx_vdev_init(struct cx231xx *dev, int cx231xx_register_analog_devices(struct cx231xx *dev) { + struct vb2_queue *q; int ret; dev_info(dev->dev, "v4l2 driver version %s\n", CX231XX_VERSION); @@ -2221,6 +1764,21 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev_err(dev->dev, "failed to initialize video media entity!\n"); #endif dev->vdev.ctrl_handler = &dev->ctrl_handler; + + q = &dev->vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx231xx_buffer); + q->ops = &cx231xx_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 1; + q->lock = &dev->lock; + ret = vb2_queue_init(q); + if (ret) + return ret; + dev->vdev.queue = q; dev->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; if (dev->tuner_type != TUNER_ABSENT) @@ -2254,6 +1812,21 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev_err(dev->dev, "failed to initialize vbi media entity!\n"); #endif dev->vbi_dev.ctrl_handler = &dev->ctrl_handler; + + q = &dev->vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx231xx_buffer); + q->ops = &cx231xx_vbi_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 1; + q->lock = &dev->lock; + ret = vb2_queue_init(q); + if (ret) + return ret; + dev->vbi_dev.queue = q; dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VBI_CAPTURE; if (dev->tuner_type != TUNER_ABSENT) diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index b007611abc37..b32eab641793 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -20,7 +20,7 @@ #include -#include +#include #include #include #include @@ -223,8 +223,8 @@ struct cx231xx_fmt { /* buffer for one video frame */ struct cx231xx_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - + struct vb2_v4l2_buffer vb; + struct list_head list; struct list_head frame; int top_field; int receiving; @@ -237,7 +237,6 @@ enum ps_package_head { struct cx231xx_dmaqueue { struct list_head active; - struct list_head queued; wait_queue_head_t wq; @@ -251,6 +250,7 @@ struct cx231xx_dmaqueue { u32 lines_completed; u8 field1_done; u32 lines_per_field; + u32 sequence; /*Mpeg2 control buffer*/ u8 *p_left_data; @@ -427,23 +427,6 @@ struct cx231xx_audio { struct cx231xx; -struct cx231xx_fh { - struct v4l2_fh fh; - struct cx231xx *dev; - unsigned int stream_on:1; /* Locks streams */ - enum v4l2_buf_type type; - - struct videobuf_queue vb_vidq; - - /* vbi capture */ - struct videobuf_queue vidq; - struct videobuf_queue vbiq; - - /* MPEG Encoder specifics ONLY */ - - atomic_t v4l_reading; -}; - /*****************************************************************/ /* set/get i2c */ /* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */ @@ -634,6 +617,7 @@ struct cx231xx { int width; /* current frame width */ int height; /* current frame height */ int interlaced; /* 1=interlace fields, 0=just top fields */ + unsigned int size; struct cx231xx_audio adev; @@ -657,6 +641,9 @@ struct cx231xx { struct media_pad input_pad[MAX_CX231XX_INPUT]; #endif + struct vb2_queue vidq; + struct vb2_queue vbiq; + unsigned char eedata[256]; struct cx231xx_video_mode video_mode; @@ -717,6 +704,7 @@ struct cx231xx { u8 USE_ISO; struct cx231xx_tvnorm encodernorm; struct cx231xx_tsport ts1, ts2; + struct vb2_queue mpegq; struct video_device v4l_device; atomic_t v4l_reader_count; u32 freq; -- cgit v1.2.3 From 36756fbff1e4a31d71d262ae6a04a20b38efa874 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 3 Sep 2019 09:09:45 -0300 Subject: media: max2175: Fix build error without CONFIG_REGMAP_I2C If CONFIG_REGMAP_I2C is not set, building fails: drivers/media/i2c/max2175.o: In function `max2175_probe': max2175.c:(.text+0x1404): undefined reference to `__devm_regmap_init_i2c' Select REGMAP_I2C to fix this. Reported-by: Hulk Robot Fixes: b47b79d8a231 ("[media] media: i2c: max2175: Add MAX2175 support") Signed-off-by: YueHaibing Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7eee1812bba3..fcffcc31d168 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1113,6 +1113,7 @@ comment "SDR tuner chips" config SDR_MAX2175 tristate "Maxim 2175 RF to Bits tuner" depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C + select REGMAP_I2C help Support for Maxim 2175 tuner. It is an advanced analog/digital radio receiver with RF-to-Bits front-end designed for SDR solutions. -- cgit v1.2.3 From 1b23ee9ccf86217e2e56d6966c7a0679ddc02a04 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:36 -0300 Subject: media: ov6650: Fix MODULE_DESCRIPTION Commit 23a52386fabe ("media: ov6650: convert to standalone v4l2 subdevice") converted the driver from a soc_camera sensor to a standalone V4L subdevice driver. Unfortunately, module description was not updated to reflect the change. Fix it. While being at it, update email address of the module author. Fixes: 23a52386fabe ("media: ov6650: convert to standalone v4l2 subdevice") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 5b9af5e5b7f1..53550cae2353 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -1041,6 +1041,6 @@ static struct i2c_driver ov6650_i2c_driver = { module_i2c_driver(ov6650_i2c_driver); -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650"); -MODULE_AUTHOR("Janusz Krzysztofik "); +MODULE_DESCRIPTION("V4L2 subdevice driver for OmniVision OV6650 camera sensor"); +MODULE_AUTHOR("Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:37 -0300 Subject: media: ov6650: Fix control handler not freed on init error Since commit afd9690c72c3 ("[media] ov6650: convert to the control framework"), if an error occurs during initialization of a control handler, resources possibly allocated to the handler are not freed before device initialiaton is aborted. Fix it. Fixes: afd9690c72c3 ("[media] ov6650: convert to the control framework") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 53550cae2353..cb2aa76cd6cc 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -989,8 +989,10 @@ static int ov6650_probe(struct i2c_client *client, V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; + if (priv->hdl.error) { + ret = priv->hdl.error; + goto ectlhdlfree; + } v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); @@ -1008,8 +1010,10 @@ static int ov6650_probe(struct i2c_client *client, priv->subdev.internal_ops = &ov6650_internal_ops; ret = v4l2_async_register_subdev(&priv->subdev); - if (ret) - v4l2_ctrl_handler_free(&priv->hdl); + if (!ret) + return 0; +ectlhdlfree: + v4l2_ctrl_handler_free(&priv->hdl); return ret; } -- cgit v1.2.3 From 7b188d6ba27a131e7934a51a14ece331c0491f18 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:38 -0300 Subject: media: ov6650: Fix crop rectangle alignment not passed back Commit 4f996594ceaf ("[media] v4l2: make vidioc_s_crop const") introduced a writable copy of constified user requested crop rectangle in order to be able to perform hardware alignments on it. Later on, commit 10d5509c8d50 ("[media] v4l2: remove g/s_crop from video ops") replaced s_crop() video operation using that const argument with set_selection() pad operation which had a corresponding argument not constified, however the original behavior of the driver was not restored. Since that time, any hardware alignment applied on a user requested crop rectangle is not passed back to the user calling .set_selection() as it should be. Fix the issue by dropping the copy and replacing all references to it with references to the crop rectangle embedded in the user argument. Fixes: 10d5509c8d50 ("[media] v4l2: remove g/s_crop from video ops") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index cb2aa76cd6cc..983b72f33930 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -465,38 +465,37 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - struct v4l2_rect rect = sel->r; int ret; if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - v4l_bound_align_image(&rect.width, 2, W_CIF, 1, - &rect.height, 2, H_CIF, 1, 0); - v4l_bound_align_image(&rect.left, DEF_HSTRT << 1, - (DEF_HSTRT << 1) + W_CIF - (__s32)rect.width, 1, - &rect.top, DEF_VSTRT << 1, - (DEF_VSTRT << 1) + H_CIF - (__s32)rect.height, 1, - 0); + v4l_bound_align_image(&sel->r.width, 2, W_CIF, 1, + &sel->r.height, 2, H_CIF, 1, 0); + v4l_bound_align_image(&sel->r.left, DEF_HSTRT << 1, + (DEF_HSTRT << 1) + W_CIF - (__s32)sel->r.width, 1, + &sel->r.top, DEF_VSTRT << 1, + (DEF_VSTRT << 1) + H_CIF - (__s32)sel->r.height, + 1, 0); - ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1); + ret = ov6650_reg_write(client, REG_HSTRT, sel->r.left >> 1); if (!ret) { - priv->rect.left = rect.left; + priv->rect.left = sel->r.left; ret = ov6650_reg_write(client, REG_HSTOP, - (rect.left + rect.width) >> 1); + (sel->r.left + sel->r.width) >> 1); } if (!ret) { - priv->rect.width = rect.width; - ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1); + priv->rect.width = sel->r.width; + ret = ov6650_reg_write(client, REG_VSTRT, sel->r.top >> 1); } if (!ret) { - priv->rect.top = rect.top; + priv->rect.top = sel->r.top; ret = ov6650_reg_write(client, REG_VSTOP, - (rect.top + rect.height) >> 1); + (sel->r.top + sel->r.height) >> 1); } if (!ret) - priv->rect.height = rect.height; + priv->rect.height = sel->r.height; return ret; } -- cgit v1.2.3 From 12500731895ef09afc5b66b86b76c0884fb9c7bf Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:39 -0300 Subject: media: ov6650: Fix incorrect use of JPEG colorspace Since its initial submission, the driver selects V4L2_COLORSPACE_JPEG for supported formats other than V4L2_MBUS_FMT_SBGGR8_1X8. According to v4l2-compliance test program, V4L2_COLORSPACE_JPEG applies exclusively to V4L2_PIX_FMT_JPEG. Since the sensor does not support JPEG format, fix it to always select V4L2_COLORSPACE_SRGB. Fixes: 2f6e2404799a ("[media] SoC Camera: add driver for OV6650 sensor") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 983b72f33930..a7930ace3744 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -200,7 +200,6 @@ struct ov6650 { unsigned long pclk_max; /* from resolution and format */ struct v4l2_fract tpf; /* as requested with s_frame_interval */ u32 code; - enum v4l2_colorspace colorspace; }; @@ -514,7 +513,7 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, mf->width = priv->rect.width >> priv->half_scale; mf->height = priv->rect.height >> priv->half_scale; mf->code = priv->code; - mf->colorspace = priv->colorspace; + mf->colorspace = V4L2_COLORSPACE_SRGB; mf->field = V4L2_FIELD_NONE; return 0; @@ -622,11 +621,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) priv->pclk_max = 8000000; } - if (code == MEDIA_BUS_FMT_SBGGR8_1X8) - priv->colorspace = V4L2_COLORSPACE_SRGB; - else if (code != 0) - priv->colorspace = V4L2_COLORSPACE_JPEG; - if (half_scale) { dev_dbg(&client->dev, "max resolution: QCIF\n"); coma_set |= COMA_QCIF; @@ -657,7 +651,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask); if (!ret) { - mf->colorspace = priv->colorspace; mf->width = priv->rect.width >> half_scale; mf->height = priv->rect.height >> half_scale; } @@ -680,6 +673,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, &mf->height, 2, H_CIF, 1, 0); mf->field = V4L2_FIELD_NONE; + mf->colorspace = V4L2_COLORSPACE_SRGB; switch (mf->code) { case MEDIA_BUS_FMT_Y10_1X10: @@ -690,13 +684,11 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; break; default: mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; /* fall through */ case MEDIA_BUS_FMT_SBGGR8_1X8: - mf->colorspace = V4L2_COLORSPACE_SRGB; break; } @@ -1004,7 +996,6 @@ static int ov6650_probe(struct i2c_client *client, priv->rect.height = H_CIF; priv->half_scale = false; priv->code = MEDIA_BUS_FMT_YUYV8_2X8; - priv->colorspace = V4L2_COLORSPACE_JPEG; priv->subdev.internal_ops = &ov6650_internal_ops; -- cgit v1.2.3 From 1c6a2b63095154bbf9e8f38d79487a728331bf65 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:40 -0300 Subject: media: ov6650: Fix some format attributes not under control User arguments passed to .get/set_fmt() pad operation callbacks may contain unsupported values. The driver takes control over frame size and pixel code as well as colorspace and field attributes but has never cared for remainig format attributes, i.e., ycbcr_enc, quantization and xfer_func, introduced by commit 11ff030c7365 ("[media] v4l2-mediabus: improve colorspace support"). Fix it. Set up a static v4l2_mbus_framefmt structure with attributes initialized to reasonable defaults and use it for updating content of user provided arguments. In case of V4L2_SUBDEV_FORMAT_ACTIVE, postpone frame size update, now performed from inside ov6650_s_fmt() helper, util the user argument is first updated in ov6650_set_fmt() with default frame format content. For V4L2_SUBDEV_FORMAT_TRY, don't copy all attributes to pad config, only those handled by the driver, then fill the response with the default frame format updated with resulting pad config format code and frame size. Fixes: 11ff030c7365 ("[media] v4l2-mediabus: improve colorspace support") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 51 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index a7930ace3744..fd608aebb68d 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -212,6 +212,17 @@ static u32 ov6650_codes[] = { MEDIA_BUS_FMT_Y8_1X8, }; +static const struct v4l2_mbus_framefmt ov6650_def_fmt = { + .width = W_CIF, + .height = H_CIF, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + /* read a register */ static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val) { @@ -510,11 +521,13 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; + /* initialize response with default media bus frame format */ + *mf = ov6650_def_fmt; + + /* update media bus format code and frame size */ mf->width = priv->rect.width >> priv->half_scale; mf->height = priv->rect.height >> priv->half_scale; mf->code = priv->code; - mf->colorspace = V4L2_COLORSPACE_SRGB; - mf->field = V4L2_FIELD_NONE; return 0; } @@ -650,10 +663,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) if (!ret) ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask); - if (!ret) { - mf->width = priv->rect.width >> half_scale; - mf->height = priv->rect.height >> half_scale; - } return ret; } @@ -672,9 +681,6 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 2, W_CIF, 1, &mf->height, 2, H_CIF, 1, 0); - mf->field = V4L2_FIELD_NONE; - mf->colorspace = V4L2_COLORSPACE_SRGB; - switch (mf->code) { case MEDIA_BUS_FMT_Y10_1X10: mf->code = MEDIA_BUS_FMT_Y8_1X8; @@ -692,10 +698,31 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, break; } - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov6650_s_fmt(sd, mf); - cfg->try_fmt = *mf; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + /* store media bus format code and frame size in pad config */ + cfg->try_fmt.width = mf->width; + cfg->try_fmt.height = mf->height; + cfg->try_fmt.code = mf->code; + /* return default mbus frame format updated with pad config */ + *mf = ov6650_def_fmt; + mf->width = cfg->try_fmt.width; + mf->height = cfg->try_fmt.height; + mf->code = cfg->try_fmt.code; + + } else { + /* apply new media bus format code and frame size */ + int ret = ov6650_s_fmt(sd, mf); + + if (ret) + return ret; + + /* return default format updated with active size and code */ + *mf = ov6650_def_fmt; + mf->width = priv->rect.width >> priv->half_scale; + mf->height = priv->rect.height >> priv->half_scale; + mf->code = priv->code; + } return 0; } -- cgit v1.2.3 From 39034bb0c26b76a2c3abc54aa28c185f18b40c2f Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:41 -0300 Subject: media: ov6650: Fix .get_fmt() V4L2_SUBDEV_FORMAT_TRY support Commit da298c6d98d5 ("[media] v4l2: replace video op g_mbus_fmt by pad op get_fmt") converted a former ov6650_g_fmt() video operation callback to an ov6650_get_fmt() pad operation callback. However, the converted function disregards a format->which flag that pad operations should obey and always returns active frame format settings. That can be fixed by always responding to V4L2_SUBDEV_FORMAT_TRY with -EINVAL, or providing the response from a pad config argument, likely updated by a former user call to V4L2_SUBDEV_FORMAT_TRY .set_fmt(). Since implementation of the latter is trivial, go for it. Fixes: da298c6d98d5 ("[media] v4l2: replace video op g_mbus_fmt by pad op get_fmt") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index fd608aebb68d..b62a238a83d0 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -525,10 +525,16 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, *mf = ov6650_def_fmt; /* update media bus format code and frame size */ - mf->width = priv->rect.width >> priv->half_scale; - mf->height = priv->rect.height >> priv->half_scale; - mf->code = priv->code; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + mf->width = cfg->try_fmt.width; + mf->height = cfg->try_fmt.height; + mf->code = cfg->try_fmt.code; + } else { + mf->width = priv->rect.width >> priv->half_scale; + mf->height = priv->rect.height >> priv->half_scale; + mf->code = priv->code; + } return 0; } -- cgit v1.2.3 From 5439fa9263cb293e41168bc03711ec18c4f11cba Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:42 -0300 Subject: media: ov6650: Fix default format not applied on device probe It is not clear what pixel format is actually configured in hardware on reset. MEDIA_BUS_FMT_YUYV8_2X8, assumed on device probe since the driver was intiially submitted, is for sure not the one. Fix it by explicitly applying a known, driver default frame format just after initial device reset. Fixes: 2f6e2404799a ("[media] SoC Camera: add driver for OV6650 sensor") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index b62a238a83d0..4fd8ac8e3994 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -873,6 +873,11 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) ret = ov6650_reset(client); if (!ret) ret = ov6650_prog_dflt(client); + if (!ret) { + struct v4l2_mbus_framefmt mf = ov6650_def_fmt; + + ret = ov6650_s_fmt(sd, &mf); + } if (!ret) ret = v4l2_ctrl_handler_setup(&priv->hdl); @@ -1027,8 +1032,6 @@ static int ov6650_probe(struct i2c_client *client, priv->rect.top = DEF_VSTRT << 1; priv->rect.width = W_CIF; priv->rect.height = H_CIF; - priv->half_scale = false; - priv->code = MEDIA_BUS_FMT_YUYV8_2X8; priv->subdev.internal_ops = &ov6650_internal_ops; -- cgit v1.2.3 From 3143b459de4cdcce67b36827476c966e93c1cf01 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:43 -0300 Subject: media: ov6650: Fix stored frame format not in sync with hardware The driver stores frame format settings supposed to be in line with hardware state in a device private structure. Since the driver initial submission, those settings are updated before they are actually applied on hardware. If an error occurs on device update, the stored settings my not reflect hardware state anymore and consecutive calls to .get_fmt() may return incorrect information. That in turn may affect ability of a bridge device to use correct DMA transfer settings if such incorrect informmation on active frame format returned by .get_fmt() is used. Assuming a failed device update means its state hasn't changed, update frame format related settings stored in the device private structure only after they are successfully applied so the stored values always reflect hardware state as closely as possible. Fixes: 2f6e2404799a ("[media] SoC Camera: add driver for OV6650 sensor") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 4fd8ac8e3994..126a662be301 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -627,7 +627,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code); return -EINVAL; } - priv->code = code; if (code == MEDIA_BUS_FMT_Y8_1X8 || code == MEDIA_BUS_FMT_SBGGR8_1X8) { @@ -648,7 +647,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) dev_dbg(&client->dev, "max resolution: CIF\n"); coma_mask |= COMA_QCIF; } - priv->half_scale = half_scale; clkrc = CLKRC_12MHz; mclk = 12000000; @@ -666,8 +664,13 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); if (!ret) ret = ov6650_reg_write(client, REG_CLKRC, clkrc); - if (!ret) + if (!ret) { + priv->half_scale = half_scale; + ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask); + } + if (!ret) + priv->code = code; return ret; } -- cgit v1.2.3 From 1463b371aff0682c70141f7521db13cc4bbf3016 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 3 Sep 2019 17:11:44 -0300 Subject: media: ov6650: Fix stored crop rectangle not in sync with hardware The driver stores crop rectangle settings supposed to be in line with hardware state in a device private structure. Since the driver initial submission, crop rectangle width and height settings are not updated correctly when rectangle offset settings are applied on hardware. If an error occurs while the device is updated, the stored settings my no longer reflect hardware state and consecutive calls to .get_selection() as well as .get/set_fmt() may return incorrect information. That in turn may affect ability of a bridge device to use correct DMA transfer settings if such incorrect informamtion on active frame format returned by .get/set_fmt() is used. Assuming a failed update of the device means its actual settings haven't changed, update crop rectangle width and height settings stored in the device private structure correctly while the rectangle offset is successfully applied on hardware so the stored values always reflect actual hardware state to the extent possible. Fixes: 2f6e2404799a ("[media] SoC Camera: add driver for OV6650 sensor") Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 126a662be301..16887049f0cd 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -491,6 +491,7 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, ret = ov6650_reg_write(client, REG_HSTRT, sel->r.left >> 1); if (!ret) { + priv->rect.width += priv->rect.left - sel->r.left; priv->rect.left = sel->r.left; ret = ov6650_reg_write(client, REG_HSTOP, (sel->r.left + sel->r.width) >> 1); @@ -500,6 +501,7 @@ static int ov6650_set_selection(struct v4l2_subdev *sd, ret = ov6650_reg_write(client, REG_VSTRT, sel->r.top >> 1); } if (!ret) { + priv->rect.height += priv->rect.top - sel->r.top; priv->rect.top = sel->r.top; ret = ov6650_reg_write(client, REG_VSTOP, (sel->r.top + sel->r.height) >> 1); -- cgit v1.2.3 From 8f4ac27af9d61776ee7218175ce300c2cb6a7121 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Sep 2019 05:51:24 -0300 Subject: media: v4l: Put camera sensor, lens and flash drivers under MEDIA_CAMERA_SUPPORT Instead of individually depending on MEDIA_CAMERA_SUPPORT (or forgetting it), put all camera sensor, lens and flash drivers under MEDIA_CAMERA_SUPPORT as a whole. The lens VCM devices didn't use to do this, but make them depend on MEDIA_CAMERA_SUPPORT as well since there's no use for these devices without that in practice. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 51 ++++++++++------------------------------------- 1 file changed, 11 insertions(+), 40 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index fcffcc31d168..3ef778819c5f 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -566,10 +566,11 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +if MEDIA_CAMERA_SUPPORT + config VIDEO_IMX214 tristate "Sony IMX214 sensor support" depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT depends on V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony @@ -581,7 +582,6 @@ config VIDEO_IMX214 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony IMX258 camera. @@ -592,7 +592,6 @@ config VIDEO_IMX258 config VIDEO_IMX274 tristate "Sony IMX274 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C help This is a V4L2 sensor driver for the Sony IMX274 @@ -601,7 +600,6 @@ config VIDEO_IMX274 config VIDEO_IMX319 tristate "Sony IMX319 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony IMX319 camera. @@ -612,7 +610,6 @@ config VIDEO_IMX319 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. @@ -623,7 +620,6 @@ config VIDEO_IMX355 config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV2640 camera. @@ -634,7 +630,6 @@ config VIDEO_OV2640 config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -646,7 +641,6 @@ config VIDEO_OV2659 config VIDEO_OV2680 tristate "OmniVision OV2680 sensor support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -658,7 +652,6 @@ config VIDEO_OV2680 config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -671,7 +664,6 @@ config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Omnivision @@ -681,7 +673,6 @@ config VIDEO_OV5645 tristate "OmniVision OV5645 sensor support" depends on OF depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -693,7 +684,6 @@ config VIDEO_OV5645 config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -705,7 +695,6 @@ config VIDEO_OV5647 config VIDEO_OV6650 tristate "OmniVision OV6650 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV6650 camera. @@ -716,7 +705,6 @@ config VIDEO_OV6650 config VIDEO_OV5670 tristate "OmniVision OV5670 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT depends on MEDIA_CONTROLLER select V4L2_FWNODE help @@ -729,7 +717,6 @@ config VIDEO_OV5670 config VIDEO_OV5675 tristate "OmniVision OV5675 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT depends on MEDIA_CONTROLLER select V4L2_FWNODE help @@ -742,7 +729,6 @@ config VIDEO_OV5675 config VIDEO_OV5695 tristate "OmniVision OV5695 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. @@ -753,7 +739,6 @@ config VIDEO_OV5695 config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -765,7 +750,6 @@ config VIDEO_OV7251 config VIDEO_OV772X tristate "OmniVision OV772x sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT select REGMAP_SCCB help This is a Video4Linux2 sensor driver for the OmniVision @@ -777,7 +761,6 @@ config VIDEO_OV772X config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV7640 camera. @@ -788,7 +771,6 @@ config VIDEO_OV7640 config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -798,7 +780,6 @@ config VIDEO_OV7670 config VIDEO_OV7740 tristate "OmniVision OV7740 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. @@ -806,7 +787,6 @@ config VIDEO_OV7740 config VIDEO_OV8856 tristate "OmniVision OV8856 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -833,7 +813,6 @@ config VIDEO_OV9650 config VIDEO_OV13858 tristate "OmniVision OV13858 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -842,7 +821,6 @@ config VIDEO_OV13858 config VIDEO_VS6624 tristate "ST VS6624 sensor support" depends on VIDEO_V4L2 && I2C - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the ST VS6624 camera. @@ -853,7 +831,6 @@ config VIDEO_VS6624 config VIDEO_MT9M001 tristate "mt9m001 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This driver supports MT9M001 cameras from Micron, monochrome and colour models. @@ -861,7 +838,6 @@ config VIDEO_MT9M001 config VIDEO_MT9M032 tristate "MT9M032 camera sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL help This driver supports MT9M032 camera sensors from Aptina, monochrome @@ -878,7 +854,6 @@ config VIDEO_MT9M111 config VIDEO_MT9P031 tristate "Aptina MT9P031 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL help This is a Video4Linux2 sensor driver for the Aptina @@ -887,7 +862,6 @@ config VIDEO_MT9P031 config VIDEO_MT9T001 tristate "Aptina MT9T001 support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Aptina (Micron) mt0t001 3 Mpixel camera. @@ -895,7 +869,6 @@ config VIDEO_MT9T001 config VIDEO_MT9T112 tristate "Aptina MT9T111/MT9T112 support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Aptina (Micron) MT9T111 and MT9T112 3 Mpixel camera. @@ -906,7 +879,6 @@ config VIDEO_MT9T112 config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Micron mt0v011 1.3 Mpixel camera. It currently only works with the @@ -915,7 +887,6 @@ config VIDEO_MT9V011 config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C select V4L2_FWNODE help @@ -925,7 +896,6 @@ config VIDEO_MT9V032 config VIDEO_MT9V111 tristate "Aptina MT9V111 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Aptina/Micron MT9V111 sensor. @@ -936,14 +906,12 @@ config VIDEO_MT9V111 config VIDEO_SR030PC30 tristate "Siliconfile SR030PC30 sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This driver supports SR030PC30 VGA camera from Siliconfile config VIDEO_NOON010PC30 tristate "Siliconfile NOON010PC30 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT help This driver supports NOON010PC30 CIF camera from Siliconfile @@ -952,7 +920,6 @@ source "drivers/media/i2c/m5mols/Kconfig" config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" depends on I2C && VIDEO_V4L2 - depends on MEDIA_CAMERA_SUPPORT help This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image sensor. @@ -962,7 +929,6 @@ config VIDEO_RJ54N1 config VIDEO_S5K6AA tristate "Samsung S5K6AAFX sensor support" - depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API help This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M @@ -970,7 +936,6 @@ config VIDEO_S5K6AA config VIDEO_S5K6A3 tristate "Samsung S5K6A3 sensor support" - depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API help This is a V4L2 sensor driver for Samsung S5K6A3 raw @@ -1002,9 +967,12 @@ config VIDEO_S5C73M3 help This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. +endif comment "Lens drivers" +if MEDIA_CAMERA_SUPPORT + config VIDEO_AD5820 tristate "AD5820 lens voice coil support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER @@ -1042,12 +1010,15 @@ config VIDEO_DW9807_VCM capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +endif + comment "Flash devices" +if MEDIA_CAMERA_SUPPORT + config VIDEO_ADP1653 tristate "ADP1653 flash support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT help This is a driver for the ADP1653 flash controller. It is used for example in Nokia N900. @@ -1055,7 +1026,6 @@ config VIDEO_ADP1653 config VIDEO_LM3560 tristate "LM3560 dual flash driver support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C help This is a driver for the lm3560 dual flash controllers. It controls @@ -1064,12 +1034,13 @@ config VIDEO_LM3560 config VIDEO_LM3646 tristate "LM3646 dual flash driver support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER - depends on MEDIA_CAMERA_SUPPORT select REGMAP_I2C help This is a driver for the lm3646 dual flash controllers. It controls flash, torch LEDs. +endif + comment "Video improvement chips" config VIDEO_UPD64031A -- cgit v1.2.3 From 355047f411c0516533b8c9c96eb6cea252bc78f0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Sep 2019 06:09:46 -0300 Subject: media: v4l: fwnode: Make v4l2_fwnode_endpoint_free() safer Assign vep->link_frequencies to NULL after releasing its memory. Without this change, multiple calls to v4l2_fwnode_endpoint_free() would result in double kfree calls. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 3bd1888787eb..192cac076761 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -512,6 +512,7 @@ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) return; kfree(vep->link_frequencies); + vep->link_frequencies = NULL; } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free); -- cgit v1.2.3 From 623df5d710fe222c00328dddb757f5045164771c Mon Sep 17 00:00:00 2001 From: Dongchun Zhu Date: Fri, 27 Sep 2019 04:18:24 -0300 Subject: media: i2c: ov5695: Modify the function of async register subdev related devices This patch adds support for registering a sensor sub-device to the async sub-device framework and parse set up common sensor related devices such as actuator/VCM. [Sakari Ailus: Rewrap commit message] Signed-off-by: Dongchun Zhu Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5695.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 34b7046d9702..d6cd15bb699a 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -1325,7 +1325,7 @@ static int ov5695_probe(struct i2c_client *client, goto err_power_off; #endif - ret = v4l2_async_register_subdev(sd); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); goto err_clean_entity; -- cgit v1.2.3 From 7359fac5a47897e6587ac82896257a60c700ac7d Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 30 Sep 2019 10:06:37 -0300 Subject: media: i2c: ov2659: Fix for image wrap-around in lower resolution Based on recently found sensor configuration examples, it was discovered that when scaling and binning are used for the lower resolutions (i.e. 640x480, 320x240) the read offset has to be increased otherwise the image appears to be wrapped around. Signed-off-by: Benoit Parrot Signed-off-by: Jyri Sarha Acked-by: Lad, Prabhakar Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index f4ded0669ff9..17573257097d 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -661,7 +661,7 @@ static struct sensor_register ov2659_vga[] = { { REG_TIMING_HORIZ_FORMAT, 0x01 }, { 0x370a, 0x52 }, { REG_VFIFO_READ_START_H, 0x00 }, - { REG_VFIFO_READ_START_L, 0x80 }, + { REG_VFIFO_READ_START_L, 0xa0 }, { REG_ISP_CTRL02, 0x10 }, { REG_NULL, 0x00 }, }; @@ -709,7 +709,7 @@ static struct sensor_register ov2659_qvga[] = { { REG_TIMING_HORIZ_FORMAT, 0x01 }, { 0x370a, 0x52 }, { REG_VFIFO_READ_START_H, 0x00 }, - { REG_VFIFO_READ_START_L, 0x80 }, + { REG_VFIFO_READ_START_L, 0xa0 }, { REG_ISP_CTRL02, 0x10 }, { REG_NULL, 0x00 }, }; -- cgit v1.2.3 From 1bc06fdbdf540b99db45b4e32669822a8a1d6870 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 30 Sep 2019 10:06:38 -0300 Subject: media: i2c: ov2659: Fix sensor detection to actually fail when device is not present Make sure that if the expected sensor device id register is not recognized properly the failure is propagated up so devices are not left partially initialized. Signed-off-by: Benoit Parrot Signed-off-by: Jyri Sarha Acked-by: Lad, Prabhakar Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 17573257097d..efbe6dc720e2 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1330,11 +1330,12 @@ static int ov2659_detect(struct v4l2_subdev *sd) unsigned short id; id = OV265X_ID(pid, ver); - if (id != OV2659_ID) + if (id != OV2659_ID) { dev_err(&client->dev, "Sensor detection failed (%04X, %d)\n", id, ret); - else { + ret = -ENODEV; + } else { dev_info(&client->dev, "Found OV%04X sensor\n", id); ret = ov2659_init(sd, 0); } -- cgit v1.2.3 From 72da0419e62e2f42eb5afef8e7f83a79d4553756 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 30 Sep 2019 10:06:39 -0300 Subject: media: i2c: ov2659: Cleanup include file list Several of include files listed are not explicitly needed. If they are need then they are implicitly included. Reduce the list of includes to an easier to manage list. Signed-off-by: Benoit Parrot Acked-by: Lad, Prabhakar Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index efbe6dc720e2..f77320e8a60d 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -22,29 +22,15 @@ #include #include -#include -#include -#include -#include #include -#include -#include #include -#include #include -#include -#include -#include -#include #include -#include #include -#include #include #include #include -#include #include #define DRIVER_NAME "ov2659" -- cgit v1.2.3 From 85c4043f1d403c222d481dfc91846227d66663fb Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 30 Sep 2019 10:06:40 -0300 Subject: media: i2c: ov2659: fix s_stream return value In ov2659_s_stream() return value for invoked function should be checked and propagated. Signed-off-by: Benoit Parrot Acked-by: Lad, Prabhakar Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index f77320e8a60d..da181a13cf44 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1187,11 +1187,15 @@ static int ov2659_s_stream(struct v4l2_subdev *sd, int on) goto unlock; } - ov2659_set_pixel_clock(ov2659); - ov2659_set_frame_size(ov2659); - ov2659_set_format(ov2659); - ov2659_set_streaming(ov2659, 1); - ov2659->streaming = on; + ret = ov2659_set_pixel_clock(ov2659); + if (!ret) + ret = ov2659_set_frame_size(ov2659); + if (!ret) + ret = ov2659_set_format(ov2659); + if (!ret) { + ov2659_set_streaming(ov2659, 1); + ov2659->streaming = on; + } unlock: mutex_unlock(&ov2659->lock); -- cgit v1.2.3 From 2b4a07a0dd336430097c410af33f342fb7931e42 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 30 Sep 2019 10:06:42 -0300 Subject: media: i2c: ov2659: Add powerdown/reset gpio handling On some board it is possible that the sensor 'powerdown' and or 'reset' pin might be controlled by gpio instead of being tied. To implement we add pm_runtime support which will handle the power up/down sequence when it is available otherwise the sensor will be powered on at module insertion/probe and powered off at module removal. Now originally the driver assumed that the sensor would always stay powered and keep its register setting. We cannot assume this anymore, so every time we "power up" we need to re-program the initial registers configuration first. This was previously done only at probe time. [Sakari Ailus: Resolve a conflict in Kconfig] Signed-off-by: Benoit Parrot Acked-by: Lad, Prabhakar Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/ov2659.c | 86 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 3ef778819c5f..78dc64d7b0e7 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -629,7 +629,7 @@ config VIDEO_OV2640 config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_V4L2 && I2C && GPIOLIB select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index da181a13cf44..80de2e35aeca 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -22,9 +22,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -218,6 +220,10 @@ struct ov2659 { struct sensor_register *format_ctrl_regs; struct ov2659_pll_ctrl pll; int streaming; + /* used to control the sensor PWDN pin */ + struct gpio_desc *pwdn_gpio; + /* used to control the sensor RESETB pin */ + struct gpio_desc *resetb_gpio; }; static const struct sensor_register ov2659_init_regs[] = { @@ -1184,10 +1190,19 @@ static int ov2659_s_stream(struct v4l2_subdev *sd, int on) /* Stop Streaming Sequence */ ov2659_set_streaming(ov2659, 0); ov2659->streaming = on; + pm_runtime_put(&client->dev); goto unlock; } - ret = ov2659_set_pixel_clock(ov2659); + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock; + } + + ret = ov2659_init(sd, 0); + if (!ret) + ret = ov2659_set_pixel_clock(ov2659); if (!ret) ret = ov2659_set_frame_size(ov2659); if (!ret) @@ -1229,12 +1244,18 @@ static int ov2659_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov2659 *ov2659 = container_of(ctrl->handler, struct ov2659, ctrls); + struct i2c_client *client = ov2659->client; + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; switch (ctrl->id) { case V4L2_CID_TEST_PATTERN: return ov2659_set_test_pattern(ov2659, ctrl->val); } + pm_runtime_put(&client->dev); return 0; } @@ -1247,6 +1268,39 @@ static const char * const ov2659_test_pattern_menu[] = { "Vertical Color Bars", }; +static int ov2659_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2659 *ov2659 = to_ov2659(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + gpiod_set_value(ov2659->pwdn_gpio, 1); + + return 0; +} + +static int ov2659_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2659 *ov2659 = to_ov2659(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + gpiod_set_value(ov2659->pwdn_gpio, 0); + + if (ov2659->resetb_gpio) { + gpiod_set_value(ov2659->resetb_gpio, 1); + usleep_range(500, 1000); + gpiod_set_value(ov2659->resetb_gpio, 0); + usleep_range(3000, 5000); + } + + return 0; +} + /* ----------------------------------------------------------------------------- * V4L2 subdev internal operations */ @@ -1327,7 +1381,6 @@ static int ov2659_detect(struct v4l2_subdev *sd) ret = -ENODEV; } else { dev_info(&client->dev, "Found OV%04X sensor\n", id); - ret = ov2659_init(sd, 0); } } @@ -1404,6 +1457,18 @@ static int ov2659_probe(struct i2c_client *client) ov2659->xvclk_frequency > 27000000) return -EINVAL; + /* Optional gpio don't fail if not present */ + ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(ov2659->pwdn_gpio)) + return PTR_ERR(ov2659->pwdn_gpio); + + /* Optional gpio don't fail if not present */ + ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov2659->resetb_gpio)) + return PTR_ERR(ov2659->resetb_gpio); + v4l2_ctrl_handler_init(&ov2659->ctrls, 2); ov2659->link_frequency = v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops, @@ -1449,6 +1514,8 @@ static int ov2659_probe(struct i2c_client *client) ov2659->frame_size = &ov2659_framesizes[2]; ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs; + ov2659_power_on(&client->dev); + ret = ov2659_detect(sd); if (ret < 0) goto error; @@ -1462,10 +1529,15 @@ static int ov2659_probe(struct i2c_client *client) dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + return 0; error: v4l2_ctrl_handler_free(&ov2659->ctrls); + ov2659_power_off(&client->dev); media_entity_cleanup(&sd->entity); mutex_destroy(&ov2659->lock); return ret; @@ -1481,9 +1553,18 @@ static int ov2659_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); mutex_destroy(&ov2659->lock); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov2659_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + return 0; } +static const struct dev_pm_ops ov2659_pm_ops = { + SET_RUNTIME_PM_OPS(ov2659_power_off, ov2659_power_on, NULL) +}; + static const struct i2c_device_id ov2659_id[] = { { "ov2659", 0 }, { /* sentinel */ }, @@ -1501,6 +1582,7 @@ MODULE_DEVICE_TABLE(of, ov2659_of_match); static struct i2c_driver ov2659_i2c_driver = { .driver = { .name = DRIVER_NAME, + .pm = &ov2659_pm_ops, .of_match_table = of_match_ptr(ov2659_of_match), }, .probe_new = ov2659_probe, -- cgit v1.2.3 From 9d669fbfca20e6035ead814e55d9ef1a6b500540 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 30 Sep 2019 10:06:43 -0300 Subject: media: i2c: ov2659: Fix missing 720p register config The initial registers sequence is only loaded at probe time. Afterward only the resolution and format specific register are modified. Care must be taken to make sure registers modified by one resolution setting are reverted back when another resolution is programmed. This was not done properly for the 720p case. Signed-off-by: Benoit Parrot Acked-by: Lad, Prabhakar Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 80de2e35aeca..c1e2aa1f952d 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -411,10 +411,14 @@ static struct sensor_register ov2659_720p[] = { { REG_TIMING_YINC, 0x11 }, { REG_TIMING_VERT_FORMAT, 0x80 }, { REG_TIMING_HORIZ_FORMAT, 0x00 }, + { 0x370a, 0x12 }, { 0x3a03, 0xe8 }, { 0x3a09, 0x6f }, { 0x3a0b, 0x5d }, { 0x3a15, 0x9a }, + { REG_VFIFO_READ_START_H, 0x00 }, + { REG_VFIFO_READ_START_L, 0x80 }, + { REG_ISP_CTRL02, 0x00 }, { REG_NULL, 0x00 }, }; -- cgit v1.2.3 From 503e59365dd134b2c63864f14e2de0476284b003 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 30 Sep 2019 10:06:44 -0300 Subject: media: i2c: ov2659: Switch to SPDX Licensing Switch to SPDX licensing and drop the redundant GPL text. Signed-off-by: Benoit Parrot Acked-by: Lad, Prabhakar Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2659.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index c1e2aa1f952d..42f64175a6df 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Omnivision OV2659 CMOS Image Sensor driver * @@ -5,19 +6,6 @@ * * Benoit Parrot * Lad, Prabhakar - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include -- cgit v1.2.3 From c690435ed07901737e5c007a65ec59f53b33eb71 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 11 Sep 2019 11:45:59 -0300 Subject: media: venus: core: Fix msm8996 frequency table In downstream driver, there are two frequency tables defined, one for the encoder and one for the decoder: /* Encoders / <972000 490000000 0x55555555>, / 4k UHD @ 30 / <489600 320000000 0x55555555>, / 1080p @ 60 / <244800 150000000 0x55555555>, / 1080p @ 30 / <108000 75000000 0x55555555>, / 720p @ 30 */ /* Decoders / <1944000 490000000 0xffffffff>, / 4k UHD @ 60 / < 972000 320000000 0xffffffff>, / 4k UHD @ 30 / < 489600 150000000 0xffffffff>, / 1080p @ 60 / < 244800 75000000 0xffffffff>; / 1080p @ 30 */ It shows that encoder always needs a higher clock than decoder. In current venus driver, the unified frequency table is aligned with the downstream decoder table which causes performance issues in encoding scenarios. Fix that by aligning frequency table on worst case (encoding). Signed-off-by: Loic Poulain Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index e6eff512a8a1..84e982f259a0 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -427,10 +427,11 @@ static const struct venus_resources msm8916_res = { }; static const struct freq_tbl msm8996_freq_table[] = { - { 1944000, 490000000 }, /* 4k UHD @ 60 */ - { 972000, 320000000 }, /* 4k UHD @ 30 */ - { 489600, 150000000 }, /* 1080p @ 60 */ - { 244800, 75000000 }, /* 1080p @ 30 */ + { 1944000, 520000000 }, /* 4k UHD @ 60 (decode only) */ + { 972000, 520000000 }, /* 4k UHD @ 30 */ + { 489600, 346666667 }, /* 1080p @ 60 */ + { 244800, 150000000 }, /* 1080p @ 30 */ + { 108000, 75000000 }, /* 720p @ 30 */ }; static const struct reg_val msm8996_reg_preset[] = { -- cgit v1.2.3 From 32f0a6ddc8c98a1aade2bf3d07c79d5d2c6ceb9a Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 13 Aug 2019 12:25:08 -0300 Subject: media: venus: Use on-chip interconnect API This aims to add a requests for bandwidth scaling depending on the resolution and framerate (macroblocks per second). The exact value of the requested bandwidth is get from a pre-calculated tables for encoder and decoder. Acked-by: Georgi Djakov Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 1 + drivers/media/platform/qcom/venus/core.c | 34 +++++++++++++++ drivers/media/platform/qcom/venus/core.h | 14 ++++++ drivers/media/platform/qcom/venus/helpers.c | 67 ++++++++++++++++++++++++++++- 4 files changed, 115 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 1175f122bb1d..997de1a83ff9 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -483,6 +483,7 @@ config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + depends on INTERCONNECT || !INTERCONNECT select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM if ARCH_QCOM select VIDEOBUF2_DMA_SG diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 84e982f259a0..8d662c8f3769 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -5,6 +5,7 @@ */ #include #include +#include #include #include #include @@ -239,6 +240,14 @@ static int venus_probe(struct platform_device *pdev) if (IS_ERR(core->base)) return PTR_ERR(core->base); + core->video_path = of_icc_get(dev, "video-mem"); + if (IS_ERR(core->video_path)) + return PTR_ERR(core->video_path); + + core->cpucfg_path = of_icc_get(dev, "cpu-cfg"); + if (IS_ERR(core->cpucfg_path)) + return PTR_ERR(core->cpucfg_path); + core->irq = platform_get_irq(pdev, 0); if (core->irq < 0) return core->irq; @@ -273,6 +282,10 @@ static int venus_probe(struct platform_device *pdev) if (ret) return ret; + ret = icc_set_bw(core->cpucfg_path, 0, kbps_to_icc(1000)); + if (ret) + return ret; + ret = hfi_create(core, &venus_core_ops); if (ret) return ret; @@ -355,6 +368,9 @@ static int venus_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); + icc_put(core->video_path); + icc_put(core->cpucfg_path); + v4l2_device_unregister(&core->v4l2_dev); return ret; @@ -465,9 +481,27 @@ static const struct freq_tbl sdm845_freq_table[] = { { 244800, 100000000 }, /* 1920x1080@30 */ }; +static const struct bw_tbl sdm845_bw_table_enc[] = { + { 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */ + { 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */ + { 489600, 723000, 0, 973000, 0 }, /* 1920x1080@60 */ + { 244800, 370000, 0, 495000, 0 }, /* 1920x1080@30 */ +}; + +static const struct bw_tbl sdm845_bw_table_dec[] = { + { 2073600, 3929000, 0, 5551000, 0 }, /* 4096x2160@60 */ + { 1036800, 1987000, 0, 2797000, 0 }, /* 4096x2160@30 */ + { 489600, 1040000, 0, 1298000, 0 }, /* 1920x1080@60 */ + { 244800, 530000, 0, 659000, 0 }, /* 1920x1080@30 */ +}; + static const struct venus_resources sdm845_res = { .freq_tbl = sdm845_freq_table, .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), + .bw_tbl_enc = sdm845_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), + .bw_tbl_dec = sdm845_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), .clks = {"core", "iface", "bus" }, .clks_num = 3, .max_load = 3110400, /* 4096x2160@90 */ diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 922cb7e64bfa..661eaa7b81ec 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -26,10 +26,22 @@ struct reg_val { u32 value; }; +struct bw_tbl { + u32 mbs_per_sec; + u32 avg; + u32 peak; + u32 avg_10bit; + u32 peak_10bit; +}; + struct venus_resources { u64 dma_mask; const struct freq_tbl *freq_tbl; unsigned int freq_tbl_size; + const struct bw_tbl *bw_tbl_enc; + unsigned int bw_tbl_enc_size; + const struct bw_tbl *bw_tbl_dec; + unsigned int bw_tbl_dec_size; const struct reg_val *reg_tbl; unsigned int reg_tbl_size; const char * const clks[VIDC_CLKS_NUM_MAX]; @@ -115,6 +127,8 @@ struct venus_core { struct clk *core1_clk; struct clk *core0_bus_clk; struct clk *core1_bus_clk; + struct icc_path *video_path; + struct icc_path *cpucfg_path; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 1ad96c25ab09..f18458921f5d 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -5,6 +5,7 @@ */ #include #include +#include #include #include #include @@ -388,6 +389,65 @@ static u32 load_per_type(struct venus_core *core, u32 session_type) return mbs_per_sec; } +static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak) +{ + const struct venus_resources *res = inst->core->res; + const struct bw_tbl *bw_tbl; + unsigned int num_rows, i; + + *avg = 0; + *peak = 0; + + if (mbs == 0) + return; + + if (inst->session_type == VIDC_SESSION_TYPE_ENC) { + num_rows = res->bw_tbl_enc_size; + bw_tbl = res->bw_tbl_enc; + } else if (inst->session_type == VIDC_SESSION_TYPE_DEC) { + num_rows = res->bw_tbl_dec_size; + bw_tbl = res->bw_tbl_dec; + } else { + return; + } + + if (!bw_tbl || num_rows == 0) + return; + + for (i = 0; i < num_rows; i++) { + if (mbs > bw_tbl[i].mbs_per_sec) + break; + + if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) { + *avg = bw_tbl[i].avg_10bit; + *peak = bw_tbl[i].peak_10bit; + } else { + *avg = bw_tbl[i].avg; + *peak = bw_tbl[i].peak; + } + } +} + +static int load_scale_bw(struct venus_core *core) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec, avg, peak, total_avg = 0, total_peak = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + mbs_per_sec = load_per_instance(inst); + mbs_to_bw(inst, mbs_per_sec, &avg, &peak); + total_avg += avg; + total_peak += peak; + } + mutex_unlock(&core->lock); + + dev_dbg(core->dev, "total: avg_bw: %u, peak_bw: %u\n", + total_avg, total_peak); + + return icc_set_bw(core->video_path, total_avg, total_peak); +} + int venus_helper_load_scale_clocks(struct venus_core *core) { const struct freq_tbl *table = core->res->freq_tbl; @@ -431,10 +491,15 @@ set_freq: if (ret) goto err; + ret = load_scale_bw(core); + if (ret) + goto err; + return 0; err: - dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); + dev_err(dev, "failed to set clock rate %lu or bandwidth (%d)\n", + freq, ret); return ret; } EXPORT_SYMBOL_GPL(venus_helper_load_scale_clocks); -- cgit v1.2.3 From 8dbebb2bd01e6f36e9a215dcde99ace70408f2c8 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 17 Sep 2019 09:02:26 -0300 Subject: media: venus: Fix occasionally failures to suspend Failure to suspend (venus_suspend_3xx) happens when the system is fresh booted and loading venus driver. This happens once and after reload the venus driver modules the problem disrepair. Fix the failure by skipping the check for WFI and IDLE bits if PC_READY is on in control status register. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_venus.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 7129a2aea09a..0d8855014ab3 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1472,6 +1472,7 @@ static int venus_suspend_3xx(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); struct device *dev = core->dev; + u32 ctrl_status; bool val; int ret; @@ -1487,6 +1488,10 @@ static int venus_suspend_3xx(struct venus_core *core) return -EINVAL; } + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY) + goto power_off; + /* * Power collapse sequence for Venus 3xx and 4xx versions: * 1. Check for ARM9 and video core to be idle by checking WFI bit @@ -1511,6 +1516,7 @@ static int venus_suspend_3xx(struct venus_core *core) if (ret) return ret; +power_off: mutex_lock(&hdev->lock); ret = venus_power_off(hdev); -- cgit v1.2.3 From e5b7fabb78f2793146104cba525d24d3236d400b Mon Sep 17 00:00:00 2001 From: Aniket Masule Date: Tue, 24 Sep 2019 02:47:48 -0300 Subject: media: venus: Add codec data table Add vpp cycles for different types of codec. It indicates the cycles required by video hardware to process each macroblock. Add vsp cycles, cycles required by stream processor. Initialize the codec data with core resources. Signed-off-by: Aniket Masule Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 13 +++++++++++++ drivers/media/platform/qcom/venus/core.h | 16 +++++++++++++++ drivers/media/platform/qcom/venus/helpers.c | 30 +++++++++++++++++++++++++++++ drivers/media/platform/qcom/venus/helpers.h | 1 + drivers/media/platform/qcom/venus/vdec.c | 4 ++++ drivers/media/platform/qcom/venus/venc.c | 4 ++++ 6 files changed, 68 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 8d662c8f3769..07312a2fab24 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -481,6 +481,17 @@ static const struct freq_tbl sdm845_freq_table[] = { { 244800, 100000000 }, /* 1920x1080@30 */ }; +static struct codec_freq_data sdm845_codec_freq_data[] = { + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 }, +}; + static const struct bw_tbl sdm845_bw_table_enc[] = { { 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */ { 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */ @@ -502,6 +513,8 @@ static const struct venus_resources sdm845_res = { .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), .bw_tbl_dec = sdm845_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), + .codec_freq_data = sdm845_codec_freq_data, + .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .max_load = 3110400, /* 4096x2160@90 */ diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 661eaa7b81ec..11585fb3cae3 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -26,6 +26,13 @@ struct reg_val { u32 value; }; +struct codec_freq_data { + u32 pixfmt; + u32 session_type; + unsigned long vpp_freq; + unsigned long vsp_freq; +}; + struct bw_tbl { u32 mbs_per_sec; u32 avg; @@ -44,6 +51,8 @@ struct venus_resources { unsigned int bw_tbl_dec_size; const struct reg_val *reg_tbl; unsigned int reg_tbl_size; + const struct codec_freq_data *codec_freq_data; + unsigned int codec_freq_data_size; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; enum hfi_version hfi_version; @@ -222,6 +231,12 @@ struct venus_buffer { struct list_head ref_list; }; +struct clock_data { + u32 core_id; + unsigned long freq; + const struct codec_freq_data *codec_freq_data; +}; + #define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb) enum venus_dec_state { @@ -302,6 +317,7 @@ struct venus_inst { struct list_head list; struct mutex lock; struct venus_core *core; + struct clock_data clk_data; struct list_head dpbbufs; struct list_head internalbufs; struct list_head registeredbufs; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index f18458921f5d..94fd071217c3 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -883,6 +883,36 @@ int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) } EXPORT_SYMBOL_GPL(venus_helper_set_core_usage); +int venus_helper_init_codec_freq_data(struct venus_inst *inst) +{ + const struct codec_freq_data *data; + unsigned int i, data_size; + u32 pixfmt; + int ret = 0; + + if (!IS_V4(inst->core)) + return 0; + + data = inst->core->res->codec_freq_data; + data_size = inst->core->res->codec_freq_data_size; + pixfmt = inst->session_type == VIDC_SESSION_TYPE_DEC ? + inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt; + + for (i = 0; i < data_size; i++) { + if (data[i].pixfmt == pixfmt && + data[i].session_type == inst->session_type) { + inst->clk_data.codec_freq_data = &data[i]; + break; + } + } + + if (!inst->clk_data.codec_freq_data) + ret = -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_init_codec_freq_data); + int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs) diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 01f411b12f81..08abe0293c91 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -33,6 +33,7 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, unsigned int width, unsigned int height, u32 buftype); int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); +int venus_helper_init_codec_freq_data(struct venus_inst *inst); int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 7f4660555ddb..18a861d7f31f 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -685,6 +685,10 @@ static int vdec_session_init(struct venus_inst *inst) if (ret) goto deinit; + ret = venus_helper_init_codec_freq_data(inst); + if (ret) + goto deinit; + return 0; deinit: hfi_session_deinit(inst); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 1b7fb2d5887c..501fb8ca55fb 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -842,6 +842,10 @@ static int venc_init_session(struct venus_inst *inst) if (ret) goto deinit; + ret = venus_helper_init_codec_freq_data(inst); + if (ret) + goto deinit; + ret = venc_set_properties(inst); if (ret) goto deinit; -- cgit v1.2.3 From c0e284ccfedafb646c2af33bdd07985231f71916 Mon Sep 17 00:00:00 2001 From: Aniket Masule Date: Tue, 24 Sep 2019 02:47:49 -0300 Subject: media: venus: Update clock scaling Current clock scaling calculations are same for vpu4 and previous versions. For vpu4, Clock scaling calculations are updated with cycles/mb and bitrate. This helps in getting precise clock required. Signed-off-by: Aniket Masule Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 157 ++++++++++++++++++++++++---- drivers/media/platform/qcom/venus/helpers.h | 2 +- drivers/media/platform/qcom/venus/vdec.c | 4 +- 3 files changed, 140 insertions(+), 23 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 94fd071217c3..5ea5d90f8e5f 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -448,12 +448,32 @@ static int load_scale_bw(struct venus_core *core) return icc_set_bw(core->video_path, total_avg, total_peak); } -int venus_helper_load_scale_clocks(struct venus_core *core) +static int set_clk_freq(struct venus_core *core, unsigned long freq) { + struct clk *clk = core->clks[0]; + int ret; + + ret = clk_set_rate(clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(core->core0_clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(core->core1_clk, freq); + if (ret) + return ret; + + return 0; +} + +static int scale_clocks(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; const struct freq_tbl *table = core->res->freq_tbl; unsigned int num_rows = core->res->freq_tbl_size; unsigned long freq = table[0].freq; - struct clk *clk = core->clks[0]; struct device *dev = core->dev; u32 mbs_per_sec; unsigned int i; @@ -479,28 +499,123 @@ int venus_helper_load_scale_clocks(struct venus_core *core) set_freq: - ret = clk_set_rate(clk, freq); - if (ret) - goto err; + ret = set_clk_freq(core, freq); + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", + freq, ret); + return ret; + } - ret = clk_set_rate(core->core0_clk, freq); - if (ret) - goto err; + ret = load_scale_bw(core); + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } - ret = clk_set_rate(core->core1_clk, freq); - if (ret) - goto err; + return 0; +} + +static unsigned long calculate_inst_freq(struct venus_inst *inst, + unsigned long filled_len) +{ + unsigned long vpp_freq = 0, vsp_freq = 0; + u64 fps = inst->fps; + u32 mbs_per_sec; + + mbs_per_sec = load_per_instance(inst) / inst->fps; + vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; + /* 21 / 20 is overhead factor */ + vpp_freq += vpp_freq / 20; + vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; + + /* 10 / 7 is overhead factor */ + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + vsp_freq += (inst->controls.enc.bitrate * 10) / 7; + else + vsp_freq += ((fps * filled_len * 8) * 10) / 7; + + return max(vpp_freq, vsp_freq); +} + +static int scale_clocks_v4(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct freq_tbl *table = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct device *dev = core->dev; + unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; + unsigned long filled_len = 0; + struct venus_buffer *buf, *n; + struct vb2_buffer *vb; + int i, ret; + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + vb = &buf->vb.vb2_buf; + filled_len = max(filled_len, vb2_get_plane_payload(vb, 0)); + } + + if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len) + return 0; + + freq = calculate_inst_freq(inst, filled_len); + inst->clk_data.freq = freq; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->clk_data.core_id == VIDC_CORE_ID_1) { + freq_core1 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_2) { + freq_core2 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_3) { + freq_core1 += inst->clk_data.freq; + freq_core2 += inst->clk_data.freq; + } + } + mutex_unlock(&core->lock); + + freq = max(freq_core1, freq_core2); + + if (freq >= table[0].freq) { + freq = table[0].freq; + dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n", + freq, table[0].freq); + goto set_freq; + } + + for (i = num_rows - 1 ; i >= 0; i--) { + if (freq <= table[i].freq) { + freq = table[i].freq; + break; + } + } + +set_freq: + + ret = set_clk_freq(core, freq); + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", + freq, ret); + return ret; + } ret = load_scale_bw(core); - if (ret) - goto err; + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } return 0; +} -err: - dev_err(dev, "failed to set clock rate %lu or bandwidth (%d)\n", - freq, ret); - return ret; +int venus_helper_load_scale_clocks(struct venus_inst *inst) +{ + if (IS_V4(inst->core)) + return scale_clocks_v4(inst); + + return scale_clocks(inst); } EXPORT_SYMBOL_GPL(venus_helper_load_scale_clocks); @@ -606,6 +721,8 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) if (inst->session_type == VIDC_SESSION_TYPE_DEC) put_ts_metadata(inst, vbuf); + + venus_helper_load_scale_clocks(inst); } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (inst->session_type == VIDC_SESSION_TYPE_ENC) fdata.buffer_type = HFI_BUFFER_OUTPUT; @@ -874,6 +991,7 @@ int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; struct hfi_videocores_usage_type cu; + inst->clk_data.core_id = usage; if (!IS_V4(inst->core)) return 0; @@ -1235,7 +1353,7 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(core); + venus_helper_load_scale_clocks(inst); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -1288,7 +1406,6 @@ EXPORT_SYMBOL_GPL(venus_helper_process_initial_out_bufs); int venus_helper_vb2_start_streaming(struct venus_inst *inst) { - struct venus_core *core = inst->core; int ret; ret = venus_helper_intbufs_alloc(inst); @@ -1299,7 +1416,7 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_bufs_free; - venus_helper_load_scale_clocks(core); + venus_helper_load_scale_clocks(inst); ret = hfi_session_load_res(inst); if (ret) diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 08abe0293c91..34dcd0c13f06 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -60,7 +60,7 @@ int venus_helper_intbufs_free(struct venus_inst *inst); int venus_helper_intbufs_realloc(struct venus_inst *inst); int venus_helper_queue_dpb_bufs(struct venus_inst *inst); int venus_helper_unregister_bufs(struct venus_inst *inst); -int venus_helper_load_scale_clocks(struct venus_core *core); +int venus_helper_load_scale_clocks(struct venus_inst *inst); int venus_helper_process_initial_cap_bufs(struct venus_inst *inst); int venus_helper_process_initial_out_bufs(struct venus_inst *inst); void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 18a861d7f31f..3bd6d5030598 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -868,7 +868,7 @@ reconfigure: if (ret) goto free_dpb_bufs; - venus_helper_load_scale_clocks(inst->core); + venus_helper_load_scale_clocks(inst); ret = hfi_session_continue(inst); if (ret) @@ -1076,7 +1076,7 @@ static void vdec_session_release(struct venus_inst *inst) hfi_session_abort(inst); venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(core); + venus_helper_load_scale_clocks(inst); INIT_LIST_HEAD(&inst->registeredbufs); mutex_unlock(&inst->lock); -- cgit v1.2.3 From ae5f973759e122e7c98dc06d00d71401fa0d1580 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 25 Sep 2019 10:53:30 -0300 Subject: media: saa7164: use debugfs rather than procfs for debugging file This moves /proc/saa7164 to /sys/kernel/debug/saa7164. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-core.c | 166 +++++++++++++++++++------------ 1 file changed, 105 insertions(+), 61 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 9ae04e18e6c6..126d085be9a7 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -13,12 +13,10 @@ #include #include #include +#include #include #include -#ifdef CONFIG_PROC_FS -#include -#endif #include "saa7164.h" MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards"); @@ -1045,92 +1043,138 @@ static void saa7164_dev_unregister(struct saa7164_dev *dev) return; } -#ifdef CONFIG_PROC_FS -static int saa7164_proc_show(struct seq_file *m, void *v) +#ifdef CONFIG_DEBUG_FS +static void *saa7164_seq_start(struct seq_file *s, loff_t *pos) { struct saa7164_dev *dev; - struct tmComResBusInfo *b; - struct list_head *list; - int i, c; + loff_t index = *pos; - if (saa7164_devcount == 0) - return 0; + mutex_lock(&devlist); + list_for_each_entry(dev, &saa7164_devlist, devlist) { + if (index-- == 0) { + mutex_unlock(&devlist); + return dev; + } + } + mutex_unlock(&devlist); - list_for_each(list, &saa7164_devlist) { - dev = list_entry(list, struct saa7164_dev, devlist); - seq_printf(m, "%s = %p\n", dev->name, dev); + return NULL; +} - /* Lock the bus from any other access */ - b = &dev->bus; - mutex_lock(&b->lock); +static void *saa7164_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct saa7164_dev *dev = v; + void *ret; - seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n", - b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); + mutex_lock(&devlist); + if (list_is_last(&dev->devlist, &saa7164_devlist)) + ret = NULL; + else + ret = list_next_entry(dev, devlist); + mutex_unlock(&devlist); - seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n", - b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); + ++*pos; - seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n", - b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); + return ret; +} - seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n", - b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); - c = 0; - seq_printf(m, "\n Set Ring:\n"); - seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); - for (i = 0; i < b->m_dwSizeSetRing; i++) { - if (c == 0) - seq_printf(m, " %04x:", i); +static void saa7164_seq_stop(struct seq_file *s, void *v) +{ +} - seq_printf(m, " %02x", readb(b->m_pdwSetRing + i)); +static int saa7164_seq_show(struct seq_file *m, void *v) +{ + struct saa7164_dev *dev = v; + struct tmComResBusInfo *b; + int i, c; - if (++c == 16) { - seq_printf(m, "\n"); - c = 0; - } - } + seq_printf(m, "%s = %p\n", dev->name, dev); - c = 0; - seq_printf(m, "\n Get Ring:\n"); - seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); - for (i = 0; i < b->m_dwSizeGetRing; i++) { - if (c == 0) - seq_printf(m, " %04x:", i); + /* Lock the bus from any other access */ + b = &dev->bus; + mutex_lock(&b->lock); - seq_printf(m, " %02x", readb(b->m_pdwGetRing + i)); + seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n", + b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); - if (++c == 16) { - seq_printf(m, "\n"); - c = 0; - } + seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n", + b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); + + seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n", + b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); + + seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n", + b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); + c = 0; + seq_puts(m, "\n Set Ring:\n"); + seq_puts(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + for (i = 0; i < b->m_dwSizeSetRing; i++) { + if (c == 0) + seq_printf(m, " %04x:", i); + + seq_printf(m, " %02x", readb(b->m_pdwSetRing + i)); + + if (++c == 16) { + seq_puts(m, "\n"); + c = 0; } + } - mutex_unlock(&b->lock); + c = 0; + seq_puts(m, "\n Get Ring:\n"); + seq_puts(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + for (i = 0; i < b->m_dwSizeGetRing; i++) { + if (c == 0) + seq_printf(m, " %04x:", i); + seq_printf(m, " %02x", readb(b->m_pdwGetRing + i)); + + if (++c == 16) { + seq_puts(m, "\n"); + c = 0; + } } + mutex_unlock(&b->lock); + return 0; } -static struct proc_dir_entry *saa7164_pe; +static const struct seq_operations saa7164_seq_ops = { + .start = saa7164_seq_start, + .next = saa7164_seq_next, + .stop = saa7164_seq_stop, + .show = saa7164_seq_show, +}; -static int saa7164_proc_create(void) +static int saa7164_open(struct inode *inode, struct file *file) { - saa7164_pe = proc_create_single("saa7164", 0444, NULL, saa7164_proc_show); - if (!saa7164_pe) - return -ENOMEM; + return seq_open(file, &saa7164_seq_ops); +} - return 0; +static const struct file_operations saa7164_operations = { + .owner = THIS_MODULE, + .open = saa7164_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static struct dentry *saa7614_dentry; + +static void __init saa7164_debugfs_create(void) +{ + saa7614_dentry = debugfs_create_file("saa7164", 0444, NULL, NULL, + &saa7164_operations); } -static void saa7164_proc_destroy(void) +static void __exit saa7164_debugfs_remove(void) { - if (saa7164_pe) - remove_proc_entry("saa7164", NULL); + debugfs_remove(saa7614_dentry); } #else -static int saa7164_proc_create(void) { return 0; } -static void saa7164_proc_destroy(void) {} +static void saa7164_debugfs_create(void) { } +static void saa7164_debugfs_remove(void) { } #endif static int saa7164_thread_function(void *data) @@ -1507,7 +1551,7 @@ static int __init saa7164_init(void) if (ret) return ret; - saa7164_proc_create(); + saa7164_debugfs_create(); pr_info("saa7164 driver loaded\n"); @@ -1516,7 +1560,7 @@ static int __init saa7164_init(void) static void __exit saa7164_fini(void) { - saa7164_proc_destroy(); + saa7164_debugfs_remove(); pci_unregister_driver(&saa7164_pci_driver); } -- cgit v1.2.3 From 6bf33ca2a9adfddc53c2146b40cfbed8145d6f54 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 25 Sep 2019 11:59:51 -0300 Subject: media: delete unused proc_fs.h include procfs is no longer used anywhere in media. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/dm1105/dm1105.c | 1 - drivers/media/pci/smipcie/smipcie.h | 1 - drivers/media/usb/cx231xx/cx231xx-audio.c | 1 - drivers/media/usb/em28xx/em28xx-audio.c | 1 - drivers/media/usb/zr364xx/zr364xx.c | 1 - 5 files changed, 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index bb3a8cc9de0c..9dce31d2b525 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index 65bc7e29450b..2b5e0154814c 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index 9ef362e221df..fd6e2df3d1b7 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 49c9b70b632b..79dfbb25714b 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 637962825d7a..aff78d63b869 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From ce819649b03d932dc19b0cb6be513779bf64fad3 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sat, 28 Sep 2019 17:46:14 -0300 Subject: media: rc: mark input device as pointing stick libinput refuses pointer movement from rc-core, since it believes it's not a pointer-type device: libinput error: event17 - Media Center Ed. eHome Infrared Remote Transceiver (1784:0008): libinput bug: REL_X/Y from a non-pointer device Fixes: 158bc148a31e ("media: rc: mce_kbd: input events via rc-core's input device") Fixes: 0ac5a603a732 ("media: rc: imon: report mouse events using rc-core's input device") Cc: stable@vger.kernel.org # 4.20+ Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 13da4c5c7d17..7741151606ef 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1773,6 +1773,7 @@ static int rc_prepare_rx_device(struct rc_dev *dev) set_bit(MSC_SCAN, dev->input_dev->mscbit); /* Pointer/mouse events */ + set_bit(INPUT_PROP_POINTING_STICK, dev->input_dev->propbit); set_bit(EV_REL, dev->input_dev->evbit); set_bit(REL_X, dev->input_dev->relbit); set_bit(REL_Y, dev->input_dev->relbit); -- cgit v1.2.3 From 81bab3fa6ca857b4d3af729f931e57e6bdc60186 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 23 Aug 2019 08:28:02 -0300 Subject: media: rc: increase rc-mm tolerance and add debug message Decoding often fails on e.g. redrat3 devices. The dev_dbg() helps with debugging when decoding does fail. Cc: Patrick Lerda Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-rcmm-decoder.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/ir-rcmm-decoder.c b/drivers/media/rc/ir-rcmm-decoder.c index 64fb65a9a19f..028df5cb1828 100644 --- a/drivers/media/rc/ir-rcmm-decoder.c +++ b/drivers/media/rc/ir-rcmm-decoder.c @@ -79,7 +79,7 @@ static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) if (!ev.pulse) break; - if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT / 2)) + if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT)) break; data->state = STATE_LOW; @@ -91,7 +91,7 @@ static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) if (ev.pulse) break; - if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2)) + if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT)) break; data->state = STATE_BUMP; @@ -164,6 +164,8 @@ static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) break; } + dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n", + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } -- cgit v1.2.3 From e43148645d18efc3072b1ba45afaa3f385299e55 Mon Sep 17 00:00:00 2001 From: A Sun Date: Fri, 6 Sep 2019 09:17:20 -0300 Subject: media: mceusb: fix out of bounds read in MCE receiver buffer Fix multiple cases of out of bounds (OOB) read associated with MCE device receive/input data handling. In reference for the OOB cases below, the incoming/read (byte) data format when the MCE device responds to a command is: { cmd_prefix, subcmd, data0, data1, ... } where cmd_prefix are: MCE_CMD_PORT_SYS MCE_CMD_PORT_IR and subcmd examples are: MCE_RSP_GETPORTSTATUS MCE_RSP_EQIRNUMPORTS ... Response size dynamically depends on cmd_prefix and subcmd. So data0, data1, ... may or may not be present on input. Multiple responses may return in a single receiver buffer. The trigger condition for OOB read is typically random or corrupt input data that fills the mceusb receiver buffer. Case 1: mceusb_handle_command() reads data0 (var hi) and data1 (var lo) regardless of whether the response includes such data. If { cmd_prefix, subcmd } is at the end of the receiver buffer, read past end of buffer occurs. This case was reported by KASAN: slab-out-of-bounds Read in mceusb_dev_recv https://syzkaller.appspot.com/bug?extid=c7fdb6cb36e65f2fe8c9 Fix: In mceusb_handle_command(), change variable hi and lo to pointers, and dereference only when required. Case 2: If response with data is truncated at end of buffer after { cmd_prefix, subcmd }, mceusb_handle_command() reads past end of buffer for data0, data1, ... Fix: In mceusb_process_ir_data(), check response size with remaining buffer size before invoking mceusb_handle_command(). + if (i + ir->rem < buf_len) mceusb_handle_command(ir, &ir->buf_in[i - 1]); Case 3: mceusb_handle_command() handles invalid/bad response such as { 0x??, MCE_RSP_GETPORTSTATUS } of length 2 as a response { MCE_CMD_PORT_SYS, MCE_RSP_GETPORTSTATUS, data0, ... } of length 7. Read OOB occurs for non-existent data0, data1, ... Cause is mceusb_handle_command() does not check cmd_prefix value. Fix: mceusb_handle_command() must test both cmd_prefix and subcmd. Case 4: mceusb_process_ir_data() receiver parser state SUBCMD is possible at start (i=0) of receiver buffer resulting in buffer offset=-1 passed to mceusb_dev_printdata(). Bad offset results in OOB read before start of buffer. [1214218.580308] mceusb 1-1.3:1.0: rx data[0]: 00 80 (length=2) [1214218.580323] mceusb 1-1.3:1.0: Unknown command 0x00 0x80 ... [1214218.580406] mceusb 1-1.3:1.0: rx data[14]: 7f 7f (length=2) [1214218.679311] mceusb 1-1.3:1.0: rx data[-1]: 80 90 (length=2) [1214218.679325] mceusb 1-1.3:1.0: End of raw IR data [1214218.679340] mceusb 1-1.3:1.0: rx data[1]: 7f 7f (length=2) Fix: If parser_state is SUBCMD after processing receiver buffer, reset parser_state to CMD_HEADER. In effect, discard cmd_prefix at end of receiver buffer. In mceusb_dev_printdata(), abort if buffer offset is out of bounds. Case 5: If response with data is truncated at end of buffer after { cmd_prefix, subcmd }, mceusb_dev_printdata() reads past end of buffer for data0, data1, ... while decoding the response to print out. Fix: In mceusb_dev_printdata(), remove unneeded buffer offset adjustments (var start and var skip) associated with MCE gen1 header. Test for truncated MCE cmd response (compare offset+len with buf_len) and skip decoding of incomplete response. Move IR data tracing to execute before the truncation test. Signed-off-by: A Sun Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 141 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 98 insertions(+), 43 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 3fc9829a9233..f9616158bcf4 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -564,7 +564,7 @@ static int mceusb_cmd_datasize(u8 cmd, u8 subcmd) datasize = 4; break; case MCE_CMD_G_REVISION: - datasize = 2; + datasize = 4; break; case MCE_RSP_EQWAKESUPPORT: case MCE_RSP_GETWAKESOURCE: @@ -600,14 +600,9 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, char *inout; u8 cmd, subcmd, *data; struct device *dev = ir->dev; - int start, skip = 0; u32 carrier, period; - /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ - if (ir->flags.microsoft_gen1 && !out && !offset) - skip = 2; - - if (len <= skip) + if (offset < 0 || offset >= buf_len) return; dev_dbg(dev, "%cx data[%d]: %*ph (len=%d sz=%d)", @@ -616,11 +611,32 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, inout = out ? "Request" : "Got"; - start = offset + skip; - cmd = buf[start] & 0xff; - subcmd = buf[start + 1] & 0xff; - data = buf + start + 2; + cmd = buf[offset]; + subcmd = (offset + 1 < buf_len) ? buf[offset + 1] : 0; + data = &buf[offset] + 2; + + /* Trace meaningless 0xb1 0x60 header bytes on original receiver */ + if (ir->flags.microsoft_gen1 && !out && !offset) { + dev_dbg(dev, "MCE gen 1 header"); + return; + } + + /* Trace IR data header or trailer */ + if (cmd != MCE_CMD_PORT_IR && + (cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA) { + if (cmd == MCE_IRDATA_TRAILER) + dev_dbg(dev, "End of raw IR data"); + else + dev_dbg(dev, "Raw IR data, %d pulse/space samples", + cmd & MCE_PACKET_LENGTH_MASK); + return; + } + + /* Unexpected end of buffer? */ + if (offset + len > buf_len) + return; + /* Decode MCE command/response */ switch (cmd) { case MCE_CMD_NULL: if (subcmd == MCE_CMD_NULL) @@ -644,7 +660,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, dev_dbg(dev, "Get hw/sw rev?"); else dev_dbg(dev, "hw/sw rev %*ph", - 4, &buf[start + 2]); + 4, &buf[offset + 2]); break; case MCE_CMD_RESUME: dev_dbg(dev, "Device resume requested"); @@ -746,13 +762,6 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, default: break; } - - if (cmd == MCE_IRDATA_TRAILER) - dev_dbg(dev, "End of raw IR data"); - else if ((cmd != MCE_CMD_PORT_IR) && - ((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA)) - dev_dbg(dev, "Raw IR data, %d pulse/space samples", - cmd & MCE_PACKET_LENGTH_MASK); #endif } @@ -1136,32 +1145,62 @@ static int mceusb_set_rx_carrier_report(struct rc_dev *dev, int enable) } /* + * Handle PORT_SYS/IR command response received from the MCE device. + * + * Assumes single response with all its data (not truncated) + * in buf_in[]. The response itself determines its total length + * (mceusb_cmd_datasize() + 2) and hence the minimum size of buf_in[]. + * * We don't do anything but print debug spew for many of the command bits * we receive from the hardware, but some of them are useful information * we want to store so that we can use them. */ -static void mceusb_handle_command(struct mceusb_dev *ir, int index) +static void mceusb_handle_command(struct mceusb_dev *ir, u8 *buf_in) { + u8 cmd = buf_in[0]; + u8 subcmd = buf_in[1]; + u8 *hi = &buf_in[2]; /* read only when required */ + u8 *lo = &buf_in[3]; /* read only when required */ struct ir_raw_event rawir = {}; - u8 hi = ir->buf_in[index + 1] & 0xff; - u8 lo = ir->buf_in[index + 2] & 0xff; u32 carrier_cycles; u32 cycles_fix; - switch (ir->buf_in[index]) { - /* the one and only 5-byte return value command */ - case MCE_RSP_GETPORTSTATUS: - if ((ir->buf_in[index + 4] & 0xff) == 0x00) - ir->txports_cabled |= 1 << hi; - break; + if (cmd == MCE_CMD_PORT_SYS) { + switch (subcmd) { + /* the one and only 5-byte return value command */ + case MCE_RSP_GETPORTSTATUS: + if (buf_in[5] == 0) + ir->txports_cabled |= 1 << *hi; + break; + + /* 1-byte return value commands */ + case MCE_RSP_EQEMVER: + ir->emver = *hi; + break; + + /* No return value commands */ + case MCE_RSP_CMD_ILLEGAL: + ir->need_reset = true; + break; + + default: + break; + } + + return; + } + if (cmd != MCE_CMD_PORT_IR) + return; + + switch (subcmd) { /* 2-byte return value commands */ case MCE_RSP_EQIRTIMEOUT: - ir->rc->timeout = US_TO_NS((hi << 8 | lo) * MCE_TIME_UNIT); + ir->rc->timeout = US_TO_NS((*hi << 8 | *lo) * MCE_TIME_UNIT); break; case MCE_RSP_EQIRNUMPORTS: - ir->num_txports = hi; - ir->num_rxports = lo; + ir->num_txports = *hi; + ir->num_rxports = *lo; break; case MCE_RSP_EQIRRXCFCNT: /* @@ -1174,7 +1213,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index) */ if (ir->carrier_report_enabled && ir->learning_active && ir->pulse_tunit > 0) { - carrier_cycles = (hi << 8 | lo); + carrier_cycles = (*hi << 8 | *lo); /* * Adjust carrier cycle count by adding * 1 missed count per pulse "on" @@ -1192,24 +1231,24 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index) break; /* 1-byte return value commands */ - case MCE_RSP_EQEMVER: - ir->emver = hi; - break; case MCE_RSP_EQIRTXPORTS: - ir->tx_mask = hi; + ir->tx_mask = *hi; break; case MCE_RSP_EQIRRXPORTEN: - ir->learning_active = ((hi & 0x02) == 0x02); - if (ir->rxports_active != hi) { + ir->learning_active = ((*hi & 0x02) == 0x02); + if (ir->rxports_active != *hi) { dev_info(ir->dev, "%s-range (0x%x) receiver active", - ir->learning_active ? "short" : "long", hi); - ir->rxports_active = hi; + ir->learning_active ? "short" : "long", *hi); + ir->rxports_active = *hi; } break; + + /* No return value commands */ case MCE_RSP_CMD_ILLEGAL: case MCE_RSP_TX_TIMEOUT: ir->need_reset = true; break; + default: break; } @@ -1235,7 +1274,8 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]); mceusb_dev_printdata(ir, ir->buf_in, buf_len, i - 1, ir->rem + 2, false); - mceusb_handle_command(ir, i); + if (i + ir->rem < buf_len) + mceusb_handle_command(ir, &ir->buf_in[i - 1]); ir->parser_state = CMD_DATA; break; case PARSE_IRDATA: @@ -1264,15 +1304,22 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) ir->rem--; break; case CMD_HEADER: - /* decode mce packets of the form (84),AA,BB,CC,DD */ - /* IR data packets can span USB messages - rem */ ir->cmd = ir->buf_in[i]; if ((ir->cmd == MCE_CMD_PORT_IR) || ((ir->cmd & MCE_PORT_MASK) != MCE_COMMAND_IRDATA)) { + /* + * got PORT_SYS, PORT_IR, or unknown + * command response prefix + */ ir->parser_state = SUBCMD; continue; } + /* + * got IR data prefix (0x80 + num_bytes) + * decode MCE packets of the form {0x83, AA, BB, CC} + * IR data packets can span USB messages + */ ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK); mceusb_dev_printdata(ir, ir->buf_in, buf_len, i, ir->rem + 1, false); @@ -1296,6 +1343,14 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) if (ir->parser_state != CMD_HEADER && !ir->rem) ir->parser_state = CMD_HEADER; } + + /* + * Accept IR data spanning multiple rx buffers. + * Reject MCE command response spanning multiple rx buffers. + */ + if (ir->parser_state != PARSE_IRDATA || !ir->rem) + ir->parser_state = CMD_HEADER; + if (event) { dev_dbg(ir->dev, "processed IR data"); ir_raw_event_handle(ir->rc); -- cgit v1.2.3 From cf330691668a3bee37b8ac8212709b3ccdd87997 Mon Sep 17 00:00:00 2001 From: Flavius Georgescu Date: Fri, 20 Sep 2019 13:11:39 -0300 Subject: media: rc: Add support for another iMON 0xffdc device The device it's an iMON UltraBay (0x98 in config byte) with LCD, IR and dual-knobs front panel. To work properly the device also require its own key table, and repeat suppression for all buttons. Signed-off-by: Flavius Georgescu Co-developed-by: Chris Vandomelen Signed-off-by: Chris Vandomelen Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 61 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 37a850421fbb..b8d96c50a804 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -83,6 +83,7 @@ struct imon_usb_dev_descr { __u16 flags; #define IMON_NO_FLAGS 0 #define IMON_NEED_20MS_PKT_DELAY 1 +#define IMON_SUPPRESS_REPEATED_KEYS 2 struct imon_panel_key_table key_table[]; }; @@ -149,8 +150,9 @@ struct imon_context { struct timer_list ttimer; /* touch screen timer */ int touch_x; /* x coordinate on touchscreen */ int touch_y; /* y coordinate on touchscreen */ - struct imon_usb_dev_descr *dev_descr; /* device description with key - table for front panels */ + const struct imon_usb_dev_descr *dev_descr; + /* device description with key */ + /* table for front panels */ }; #define TOUCH_TIMEOUT (HZ/30) @@ -315,6 +317,32 @@ static const struct imon_usb_dev_descr imon_DH102 = { } }; +/* imon ultrabay front panel key table */ +static const struct imon_usb_dev_descr ultrabay_table = { + .flags = IMON_SUPPRESS_REPEATED_KEYS, + .key_table = { + { 0x0000000f0000ffeell, KEY_MEDIA }, /* Go */ + { 0x000000000100ffeell, KEY_UP }, + { 0x000000000001ffeell, KEY_DOWN }, + { 0x000000160000ffeell, KEY_ENTER }, + { 0x0000001f0000ffeell, KEY_AUDIO }, /* Music */ + { 0x000000200000ffeell, KEY_VIDEO }, /* Movie */ + { 0x000000210000ffeell, KEY_CAMERA }, /* Photo */ + { 0x000000270000ffeell, KEY_DVD }, /* DVD */ + { 0x000000230000ffeell, KEY_TV }, /* TV */ + { 0x000000050000ffeell, KEY_PREVIOUS }, /* Previous */ + { 0x000000070000ffeell, KEY_REWIND }, + { 0x000000040000ffeell, KEY_STOP }, + { 0x000000020000ffeell, KEY_PLAYPAUSE }, + { 0x000000080000ffeell, KEY_FASTFORWARD }, + { 0x000000060000ffeell, KEY_NEXT }, /* Next */ + { 0x000100000000ffeell, KEY_VOLUMEUP }, + { 0x010000000000ffeell, KEY_VOLUMEDOWN }, + { 0x000000010000ffeell, KEY_MUTE }, + { 0, KEY_RESERVED }, + } +}; + /* * USB Device ID for iMON USB Control Boards * @@ -1264,9 +1292,11 @@ static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 scancode) static u32 imon_panel_key_lookup(struct imon_context *ictx, u64 code) { - int i; + const struct imon_panel_key_table *key_table; u32 keycode = KEY_RESERVED; - struct imon_panel_key_table *key_table = ictx->dev_descr->key_table; + int i; + + key_table = ictx->dev_descr->key_table; for (i = 0; key_table[i].hw_code != 0; i++) { if (key_table[i].hw_code == (code | 0xffee)) { @@ -1550,7 +1580,6 @@ static void imon_incoming_packet(struct imon_context *ictx, u32 kc; u64 scancode; int press_type = 0; - long msec; ktime_t t; static ktime_t prev_time; u8 ktype; @@ -1653,14 +1682,16 @@ static void imon_incoming_packet(struct imon_context *ictx, spin_lock_irqsave(&ictx->kc_lock, flags); t = ktime_get(); - /* KEY_MUTE repeats from knob need to be suppressed */ - if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) { - msec = ktime_ms_delta(t, prev_time); - if (msec < ictx->idev->rep[REP_DELAY]) { + /* KEY repeats from knob and panel that need to be suppressed */ + if (ictx->kc == KEY_MUTE || + ictx->dev_descr->flags & IMON_SUPPRESS_REPEATED_KEYS) { + if (ictx->kc == ictx->last_keycode && + ktime_ms_delta(t, prev_time) < ictx->idev->rep[REP_DELAY]) { spin_unlock_irqrestore(&ictx->kc_lock, flags); return; } } + prev_time = t; kc = ictx->kc; @@ -1848,6 +1879,14 @@ static void imon_get_ffdc_type(struct imon_context *ictx) dev_info(ictx->dev, "0xffdc iMON Inside, iMON IR"); ictx->display_supported = false; break; + /* Soundgraph iMON UltraBay */ + case 0x98: + dev_info(ictx->dev, "0xffdc iMON UltraBay, LCD + IR"); + detected_display_type = IMON_DISPLAY_TYPE_LCD; + allowed_protos = RC_PROTO_BIT_IMON | RC_PROTO_BIT_RC6_MCE; + ictx->dev_descr = &ultrabay_table; + break; + default: dev_info(ictx->dev, "Unknown 0xffdc device, defaulting to VFD and iMON IR"); detected_display_type = IMON_DISPLAY_TYPE_VFD; @@ -1979,10 +2018,12 @@ out: static struct input_dev *imon_init_idev(struct imon_context *ictx) { - struct imon_panel_key_table *key_table = ictx->dev_descr->key_table; + const struct imon_panel_key_table *key_table; struct input_dev *idev; int ret, i; + key_table = ictx->dev_descr->key_table; + idev = input_allocate_device(); if (!idev) goto out; -- cgit v1.2.3 From 64659c81dfccc4f8733289d59904b7c536c00683 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sat, 31 Aug 2019 05:37:38 -0300 Subject: media: rtl28xxu: set keymap for Astrometa DVB-T2 Thanks to Jan Pieter van Woerkom for providing the hardware. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 1a36bda28542..78ad9adfbeac 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1957,7 +1957,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { /* RTL2832P devices: */ { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131, - &rtl28xxu_props, "Astrometa DVB-T2", NULL) }, + &rtl28xxu_props, "Astrometa DVB-T2", + RC_MAP_ASTROMETA_T2HYBRID) }, { DVB_USB_DEVICE(0x5654, 0xca42, &rtl28xxu_props, "GoTView MasterHD 3", NULL) }, { } -- cgit v1.2.3 From 6f5129e251aeec71f8928ae0668d508454fa8aff Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sat, 31 Aug 2019 05:37:39 -0300 Subject: media: rtl28xxu: fix idle handling Currently the IR receiver is put into idle mode after each interrupt. However longer IR like NEC can be distributed over multiple interrupts, so putting the IR into idle in the middle of such a message will break decoding. Every IR message has a trailing space of 0xbf (one less than IR_IDE_LEN{0,1} which is programmed to 0xc0). So, set the timeout to that value and rc-core will put the IR receiver into idle mode automatically. Thanks to Jan Pieter van Woerkom for providing the hardware. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 2 ++ drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 1 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index b874a49ececf..52bcc2d2efe5 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -121,6 +121,7 @@ struct dvb_usb_driver_info { * @interval: time in ms between two queries * @driver_type: used to point if a device supports raw mode * @bulk_mode: device supports bulk mode for rc (disable polling mode) + * @timeout: set to length of last space before raw IR goes idle */ struct dvb_usb_rc { const char *map_name; @@ -130,6 +131,7 @@ struct dvb_usb_rc { unsigned int interval; enum rc_driver_type driver_type; bool bulk_mode; + int timeout; }; /** diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index e5e056bf9dfa..f1c79f351ec8 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -150,6 +150,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d) dev->map_name = d->rc.map_name; dev->allowed_protocols = d->rc.allowed_protos; dev->change_protocol = d->rc.change_protocol; + dev->timeout = d->rc.timeout; dev->priv = d; ret = rc_register_device(dev); diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 78ad9adfbeac..5016ede7b35f 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1781,7 +1781,6 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) } /* 'flush' ir_raw_event_store_with_filter() */ - ir_raw_event_set_idle(d->rc_dev, true); ir_raw_event_handle(d->rc_dev); exit: return ret; @@ -1804,6 +1803,8 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d, rc->driver_type = RC_DRIVER_IR_RAW; rc->query = rtl2832u_rc_query; rc->interval = 200; + /* we program idle len to 0xc0, set timeout to one less */ + rc->timeout = 0xbf * 50800; return 0; } -- cgit v1.2.3 From ed4e15697b5fda94e65af1ecb272cd1388192bd6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 5 Sep 2019 14:57:26 -0300 Subject: media: cxd2820r: make arrays tab static const, makes object smaller Don't populate the array tab on the stack but instead make it static const. Makes the object size smaller by 170 bytes in total. Before: text data bss dec hex filename 7045 1736 0 8781 224d media/dvb-frontends/cxd2820r_c.o 8800 2216 0 11016 2b08 media/dvb-frontends/cxd2820r_t.o 8981 2120 0 11101 2b5d media/dvb-frontends/cxd2820r_t2.o After: text data bss dec hex filename 6896 1832 0 8728 2218 media/dvb-frontends/cxd2820r_c.o 8651 2312 0 10963 2ad3 media/dvb-frontends/cxd2820r_t.o 8853 2184 0 11037 2b1d media/dvb-frontends/cxd2820r_t2.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cxd2820r_c.c | 2 +- drivers/media/dvb-frontends/cxd2820r_t.c | 2 +- drivers/media/dvb-frontends/cxd2820r_t2.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c index 6f7eedb4c00e..0ba382948c51 100644 --- a/drivers/media/dvb-frontends/cxd2820r_c.c +++ b/drivers/media/dvb-frontends/cxd2820r_c.c @@ -298,7 +298,7 @@ int cxd2820r_sleep_c(struct dvb_frontend *fe) struct cxd2820r_priv *priv = fe->demodulator_priv; struct i2c_client *client = priv->client[0]; int ret; - struct reg_val_mask tab[] = { + static const struct reg_val_mask tab[] = { { 0x000ff, 0x1f, 0xff }, { 0x00085, 0x00, 0xff }, { 0x00088, 0x01, 0xff }, diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c index d56c6f788196..fbdfa6bf38dc 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -392,7 +392,7 @@ int cxd2820r_sleep_t(struct dvb_frontend *fe) struct cxd2820r_priv *priv = fe->demodulator_priv; struct i2c_client *client = priv->client[0]; int ret; - struct reg_val_mask tab[] = { + static struct reg_val_mask tab[] = { { 0x000ff, 0x1f, 0xff }, { 0x00085, 0x00, 0xff }, { 0x00088, 0x01, 0xff }, diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c index f924a80b968a..34ef2bb2de34 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -386,7 +386,7 @@ int cxd2820r_sleep_t2(struct dvb_frontend *fe) struct cxd2820r_priv *priv = fe->demodulator_priv; struct i2c_client *client = priv->client[0]; int ret; - struct reg_val_mask tab[] = { + static const struct reg_val_mask tab[] = { { 0x000ff, 0x1f, 0xff }, { 0x00085, 0x00, 0xff }, { 0x00088, 0x01, 0xff }, -- cgit v1.2.3 From 3eab054614cd50ab4254785e7346a82190d78b82 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Sun, 1 Sep 2019 18:27:46 -0300 Subject: media: em28xx: Add support for Magix Wideowandler 2 Add support for Magix Wideowandler 2 - analog USB capture card Signed-off-by: Dominik Danelski Co-author: Cornelius Porosanu Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 20 ++++++++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 2 files changed, 21 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 5983e72a0622..def9cdd931a9 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2487,6 +2487,24 @@ const struct em28xx_board em28xx_boards[] = { .ir_codes = RC_MAP_HAUPPAUGE, .leds = hauppauge_dualhd_leds, }, + /* + * 1b80:e349 Magix USB Videowandler-2 + * (same chips as Honestech VIDBOX NW03) + * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner + */ + [EM2861_BOARD_MAGIX_VIDEOWANDLER2] = { + .name = "Magix USB Videowandler-2", + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2696,6 +2714,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28178_BOARD_PLEX_PX_BCUD }, { USB_DEVICE(0xeb1a, 0x5051), /* Ion Video 2 PC MKII / Startech svid2usb23 / Raygo R12-41373 */ .driver_info = EM2860_BOARD_TVP5150_REFERENCE_DESIGN }, + { USB_DEVICE(0x1b80, 0xe349), /* Magix USB Videowandler-2 */ + .driver_info = EM2861_BOARD_MAGIX_VIDEOWANDLER2 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index c8bc59059a19..4ecadd57dac7 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -149,6 +149,7 @@ #define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100 #define EM2884_BOARD_TERRATEC_H6 101 #define EM2882_BOARD_ZOLID_HYBRID_TV_STICK 102 +#define EM2861_BOARD_MAGIX_VIDEOWANDLER2 103 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3 From a7b2df76b42bdd026e3106cf2ba97db41345a177 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Wed, 25 Sep 2019 12:02:41 -0300 Subject: media: rc: prevent memory leak in cx23888_ir_probe In cx23888_ir_probe if kfifo_alloc fails the allocated memory for state should be released. Signed-off-by: Navid Emamdoost Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23888-ir.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index e880afe37f15..d59ca3601785 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -1167,8 +1167,11 @@ int cx23888_ir_probe(struct cx23885_dev *dev) return -ENOMEM; spin_lock_init(&state->rx_kfifo_lock); - if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL)) + if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, + GFP_KERNEL)) { + kfree(state); return -ENOMEM; + } state->dev = dev; sd = &state->sd; -- cgit v1.2.3 From 6d7cc95311f70fa726966c9f834563f8ce05a5c7 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Sun, 15 Sep 2019 08:00:46 -0300 Subject: media: rc: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header file related to Remote Controller Driver for Linux. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-core-priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 9f21b3e8b377..5f36244cc34f 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * Remote Controller core raw events header * * Copyright (C) 2010 by Mauro Carvalho Chehab -- cgit v1.2.3 From 1983c43509d7ab4c147305c1c668f4e23a47ef7f Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Wed, 18 Sep 2019 07:30:18 -0300 Subject: media: rc: Use devm_platform_ioremap_resource() in tango_ir_probe() Simplify this function implementation by using a known wrapper function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Mans Rullgard Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/tango-ir.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/tango-ir.c b/drivers/media/rc/tango-ir.c index 451ec4e9dcfa..b8eb5bc4d9be 100644 --- a/drivers/media/rc/tango-ir.c +++ b/drivers/media/rc/tango-ir.c @@ -157,20 +157,10 @@ static int tango_ir_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rc_dev *rc; struct tango_ir *ir; - struct resource *rc5_res; - struct resource *rc6_res; u64 clkrate, clkdiv; int irq, err; u32 val; - rc5_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!rc5_res) - return -EINVAL; - - rc6_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!rc6_res) - return -EINVAL; - irq = platform_get_irq(pdev, 0); if (irq <= 0) return -EINVAL; @@ -179,11 +169,11 @@ static int tango_ir_probe(struct platform_device *pdev) if (!ir) return -ENOMEM; - ir->rc5_base = devm_ioremap_resource(dev, rc5_res); + ir->rc5_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ir->rc5_base)) return PTR_ERR(ir->rc5_base); - ir->rc6_base = devm_ioremap_resource(dev, rc6_res); + ir->rc6_base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(ir->rc6_base)) return PTR_ERR(ir->rc6_base); -- cgit v1.2.3 From 94ddd60d9c39bf06e63a808e784bc4eaafc852d7 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Mon, 16 Sep 2019 12:09:00 -0300 Subject: media: usb: tm6000: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files for TV Master Media USB Adapter drivers. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-regs.h | 2 +- drivers/media/usb/tm6000/tm6000-usb-isoc.h | 2 +- drivers/media/usb/tm6000/tm6000.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/tm6000/tm6000-regs.h b/drivers/media/usb/tm6000/tm6000-regs.h index d10424673db9..6a181f2e7ef2 100644 --- a/drivers/media/usb/tm6000/tm6000-regs.h +++ b/drivers/media/usb/tm6000/tm6000-regs.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices * * Copyright (c) 2006-2007 Mauro Carvalho Chehab diff --git a/drivers/media/usb/tm6000/tm6000-usb-isoc.h b/drivers/media/usb/tm6000/tm6000-usb-isoc.h index b275dbce3a1b..e3c6933f854d 100644 --- a/drivers/media/usb/tm6000/tm6000-usb-isoc.h +++ b/drivers/media/usb/tm6000/tm6000-usb-isoc.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices * * Copyright (c) 2006-2007 Mauro Carvalho Chehab diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index bf396544da9a..c08c95312739 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices * * Copyright (c) 2006-2007 Mauro Carvalho Chehab -- cgit v1.2.3 From 767f22ac54b696b2b40f87cad08f877fe3ec2264 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 1 Aug 2019 13:09:58 -0300 Subject: media: drxj: remove redundant assignment to variable rc The variable rc is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drx39xyj/drxj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 2f5af4813a74..ac7be872f460 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -4201,7 +4201,7 @@ int drxj_dap_scu_atomic_read_reg16(struct i2c_device_addr *dev_addr, u16 *data, u32 flags) { u8 buf[2] = { 0 }; - int rc = -EIO; + int rc; u16 word = 0; if (!data) -- cgit v1.2.3 From cecf0bbbcb6f035a5ca2197f3e11ec2b7fb3da83 Mon Sep 17 00:00:00 2001 From: Andrei Koshkosh Date: Sun, 29 Sep 2019 05:04:05 -0300 Subject: media: dvbsky: use a single mutex and state buffers for all R/W ops Re-use usb_mutex from dvb_usb_device for this. Tested-by: Jan Pieter van Woerkom Signed-off-by: Andrei Koshkosh Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvbsky.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 617a306f6815..b562ee090c85 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -22,7 +22,6 @@ MODULE_PARM_DESC(disable_rc, "Disable inbuilt IR receiver."); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct dvbsky_state { - struct mutex stream_mutex; u8 ibuf[DVBSKY_BUF_LEN]; u8 obuf[DVBSKY_BUF_LEN]; u8 last_lock; @@ -60,17 +59,19 @@ static int dvbsky_usb_generic_rw(struct dvb_usb_device *d, static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff) { struct dvbsky_state *state = d_to_priv(d); + static const u8 obuf_pre[3] = { 0x37, 0, 0 }; + static const u8 obuf_post[3] = { 0x36, 3, 0 }; int ret; - u8 obuf_pre[3] = { 0x37, 0, 0 }; - u8 obuf_post[3] = { 0x36, 3, 0 }; - mutex_lock(&state->stream_mutex); - ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0); + mutex_lock(&d->usb_mutex); + memcpy(state->obuf, obuf_pre, 3); + ret = dvb_usbv2_generic_write_locked(d, state->obuf, 3); if (!ret && onoff) { msleep(20); - ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0); + memcpy(state->obuf, obuf_post, 3); + ret = dvb_usbv2_generic_write_locked(d, state->obuf, 3); } - mutex_unlock(&state->stream_mutex); + mutex_unlock(&d->usb_mutex); return ret; } @@ -598,7 +599,6 @@ static int dvbsky_init(struct dvb_usb_device *d) if (ret) return ret; */ - mutex_init(&state->stream_mutex); state->last_lock = 0; -- cgit v1.2.3 From 1ea76d16569b7fc242b860c7e19549be028b13d1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 23 Aug 2019 20:53:37 -0300 Subject: media: gl861: re-implement I2C adapter logic Device I2C adapter is capable of writing and reading large messages. For I2C writes there is 2 methods: simple for max 2 byte messages and usb_control_msg() with payload data for larger I2C messages. Add I2C adapter logic which selects suitable method according to message size. Add also support for plain I2C read. Cc: Akihiro TSUKADA Signed-off-by: Antti Palosaari Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/gl861.c | 216 ++++++++++++++++++++++++++--------- 1 file changed, 159 insertions(+), 57 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index c7197e534c02..f4003e3d4b18 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -14,6 +14,154 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +struct gl861 { + /* USB control message buffer */ + u8 buf[16]; + + struct i2c_adapter *demod_sub_i2c; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; + struct i2c_adapter tuner_adap; +}; + +#define CMD_WRITE_SHORT 0x01 +#define CMD_READ 0x02 +#define CMD_WRITE 0x03 + +static int gl861_ctrl_msg(struct dvb_usb_device *d, u8 request, u16 value, + u16 index, void *data, u16 size) +{ + struct gl861 *ctx = d_to_priv(d); + struct usb_interface *intf = d->intf; + int ret; + unsigned int pipe; + u8 requesttype; + + mutex_lock(&d->usb_mutex); + + switch (request) { + case CMD_WRITE: + memcpy(ctx->buf, data, size); + /* Fall through */ + case CMD_WRITE_SHORT: + pipe = usb_sndctrlpipe(d->udev, 0); + requesttype = USB_TYPE_VENDOR | USB_DIR_OUT; + break; + case CMD_READ: + pipe = usb_rcvctrlpipe(d->udev, 0); + requesttype = USB_TYPE_VENDOR | USB_DIR_IN; + break; + default: + ret = -EINVAL; + goto err_mutex_unlock; + } + + ret = usb_control_msg(d->udev, pipe, request, requesttype, value, + index, ctx->buf, size, 200); + dev_dbg(&intf->dev, "%d | %02x %02x %*ph %*ph %*ph %s %*ph\n", + ret, requesttype, request, 2, &value, 2, &index, 2, &size, + (requesttype & USB_DIR_IN) ? "<<<" : ">>>", size, ctx->buf); + if (ret < 0) + goto err_mutex_unlock; + + if (request == CMD_READ) + memcpy(data, ctx->buf, size); + + usleep_range(1000, 2000); /* Avoid I2C errors */ + + mutex_unlock(&d->usb_mutex); + + return 0; + +err_mutex_unlock: + mutex_unlock(&d->usb_mutex); + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; +} + +static int gl861_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct usb_interface *intf = d->intf; + struct gl861 *ctx = d_to_priv(d); + int ret; + u8 request, *data; + u16 value, index, size; + + /* XXX: I2C adapter maximum data lengths are not tested */ + if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + /* I2C write */ + if (msg[0].len < 2 || msg[0].len > sizeof(ctx->buf)) { + ret = -EOPNOTSUPP; + goto err; + } + + value = (msg[0].addr << 1) << 8; + index = msg[0].buf[0]; + + if (msg[0].len == 2) { + request = CMD_WRITE_SHORT; + value |= msg[0].buf[1]; + size = 0; + data = NULL; + } else { + request = CMD_WRITE; + size = msg[0].len - 1; + data = &msg[0].buf[1]; + } + + ret = gl861_ctrl_msg(d, request, value, index, data, size); + } else if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + /* I2C write + read */ + if (msg[0].len > 1 || msg[1].len > sizeof(ctx->buf)) { + ret = -EOPNOTSUPP; + goto err; + } + + value = (msg[0].addr << 1) << 8; + index = msg[0].buf[0]; + request = CMD_READ; + + ret = gl861_ctrl_msg(d, request, value, index, + msg[1].buf, msg[1].len); + } else if (num == 1 && (msg[0].flags & I2C_M_RD)) { + /* I2C read */ + if (msg[0].len > sizeof(ctx->buf)) { + ret = -EOPNOTSUPP; + goto err; + } + value = (msg[0].addr << 1) << 8; + index = 0x0100; + request = CMD_READ; + + ret = gl861_ctrl_msg(d, request, value, index, + msg[0].buf, msg[0].len); + } else { + /* Unsupported I2C message */ + dev_dbg(&intf->dev, "unknown i2c msg, num %u\n", num); + ret = -EOPNOTSUPP; + } + if (ret) + goto err; + + return num; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; +} + +static u32 gl861_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm gl861_i2c_algo = { + .master_xfer = gl861_i2c_master_xfer, + .functionality = gl861_i2c_functionality, +}; + static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) { @@ -63,46 +211,6 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, return ret; } -/* I2C */ -static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], - int num) -{ - struct dvb_usb_device *d = i2c_get_adapdata(adap); - int i; - - if (num > 2) - return -EINVAL; - - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; - - for (i = 0; i < num; i++) { - /* write/read request */ - if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { - if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, - msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) - break; - i++; - } else - if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, - msg[i].len, NULL, 0) < 0) - break; - } - - mutex_unlock(&d->i2c_mutex); - return i; -} - -static u32 gl861_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm gl861_i2c_algo = { - .master_xfer = gl861_i2c_xfer, - .functionality = gl861_i2c_func, -}; - /* Callbacks for DVB USB */ static struct zl10353_config gl861_zl10353_config = { .demod_address = 0x0f, @@ -149,6 +257,8 @@ static struct dvb_usb_device_properties gl861_props = { .owner = THIS_MODULE, .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct gl861), + .i2c_algo = &gl861_i2c_algo, .frontend_attach = gl861_frontend_attach, .tuner_attach = gl861_tuner_attach, @@ -166,14 +276,6 @@ static struct dvb_usb_device_properties gl861_props = { /* * For Friio */ - -struct friio_priv { - struct i2c_adapter *demod_sub_i2c; - struct i2c_client *i2c_client_demod; - struct i2c_client *i2c_client_tuner; - struct i2c_adapter tuner_adap; -}; - struct friio_config { struct i2c_board_info demod_info; struct tc90522_config demod_cfg; @@ -242,7 +344,7 @@ gl861_i2c_read_ex(struct dvb_usb_device *d, u8 addr, u8 *rbuf, u16 rlen) static int friio_i2c_tuner_read(struct dvb_usb_device *d, struct i2c_msg *msg) { - struct friio_priv *priv; + struct gl861 *priv; u8 addr; priv = d_to_priv(d); @@ -255,7 +357,7 @@ friio_i2c_tuner_write(struct dvb_usb_device *d, struct i2c_msg *msg) { u8 *buf; int ret; - struct friio_priv *priv; + struct gl861 *priv; priv = d_to_priv(d); @@ -308,7 +410,7 @@ static int friio_tuner_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], static struct i2c_algorithm friio_tuner_i2c_algo = { .master_xfer = friio_tuner_i2c_xfer, - .functionality = gl861_i2c_func, + .functionality = gl861_i2c_functionality, }; /* GPIO control in Friio */ @@ -488,7 +590,7 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d; struct tc90522_config cfg; struct i2c_client *cl; - struct friio_priv *priv; + struct gl861 *priv; info = &friio_config.demod_info; d = adap_to_d(adap); @@ -513,7 +615,7 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap) static int friio_frontend_detach(struct dvb_usb_adapter *adap) { - struct friio_priv *priv; + struct gl861 *priv; priv = adap_to_priv(adap); i2c_del_adapter(&priv->tuner_adap); @@ -526,7 +628,7 @@ static int friio_tuner_attach(struct dvb_usb_adapter *adap) const struct i2c_board_info *info; struct dvb_pll_config cfg; struct i2c_client *cl; - struct friio_priv *priv; + struct gl861 *priv; priv = adap_to_priv(adap); info = &friio_config.tuner_info; @@ -543,7 +645,7 @@ static int friio_tuner_attach(struct dvb_usb_adapter *adap) static int friio_tuner_detach(struct dvb_usb_adapter *adap) { - struct friio_priv *priv; + struct gl861 *priv; priv = adap_to_priv(adap); dvb_module_release(priv->i2c_client_tuner); @@ -554,7 +656,7 @@ static int friio_init(struct dvb_usb_device *d) { int i; int ret; - struct friio_priv *priv; + struct gl861 *priv; static const u8 demod_init[][2] = { {0x01, 0x40}, {0x04, 0x38}, {0x05, 0x40}, {0x07, 0x40}, @@ -606,7 +708,7 @@ static struct dvb_usb_device_properties friio_props = { .owner = THIS_MODULE, .adapter_nr = adapter_nr, - .size_of_priv = sizeof(struct friio_priv), + .size_of_priv = sizeof(struct gl861), .i2c_algo = &gl861_i2c_algo, .power_ctrl = friio_power_ctrl, -- cgit v1.2.3 From 8139bb3e1d45275f87dc3bfe296c38e59575528d Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Thu, 29 Aug 2019 05:38:19 -0300 Subject: media: dvb-usb-v2/gl861: remove device-specific i2c algo For Friio dvb cards, a distinct I2C algo was provided to support some "long" I2C messages used in relaying tuner I2C via demod. Since the other (generic) i2c algo in this module has been patched to support those messages in a more generic way, this patch replaces and integrates the device-specific i2c algo with the generic one and simplifies the code. Signed-off-by: Akihiro Tsukada Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/gl861.c | 234 ++++------------------------------- 1 file changed, 26 insertions(+), 208 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index f4003e3d4b18..f125264d8df1 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -21,7 +21,6 @@ struct gl861 { struct i2c_adapter *demod_sub_i2c; struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; - struct i2c_adapter tuner_adap; }; #define CMD_WRITE_SHORT 0x01 @@ -79,6 +78,12 @@ err_mutex_unlock: return ret; } +static int gl861_short_write(struct dvb_usb_device *d, u8 addr, u8 reg, u8 val) +{ + return gl861_ctrl_msg(d, CMD_WRITE_SHORT, + (addr << 9) | val, reg, NULL, 0); +} + static int gl861_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { @@ -162,55 +167,6 @@ static struct i2c_algorithm gl861_i2c_algo = { .functionality = gl861_i2c_functionality, }; -static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, - u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) -{ - u16 index; - u16 value = addr << (8 + 1); - int wo = (rbuf == NULL || rlen == 0); /* write-only */ - u8 req, type; - u8 *buf; - int ret; - - if (wo) { - req = GL861_REQ_I2C_WRITE; - type = GL861_WRITE; - buf = kmemdup(wbuf, wlen, GFP_KERNEL); - } else { /* rw */ - req = GL861_REQ_I2C_READ; - type = GL861_READ; - buf = kmalloc(rlen, GFP_KERNEL); - } - if (!buf) - return -ENOMEM; - - switch (wlen) { - case 1: - index = wbuf[0]; - break; - case 2: - index = wbuf[0]; - value = value + wbuf[1]; - break; - default: - dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n", - KBUILD_MODNAME, wlen); - kfree(buf); - return -EINVAL; - } - - usleep_range(1000, 2000); /* avoid I2C errors */ - - ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, - value, index, buf, rlen, 2000); - - if (!wo && ret > 0) - memcpy(rbuf, buf, rlen); - - kfree(buf); - return ret; -} - /* Callbacks for DVB USB */ static struct zl10353_config gl861_zl10353_config = { .demod_address = 0x0f, @@ -289,129 +245,6 @@ static const struct friio_config friio_config = { .tuner_info = { I2C_BOARD_INFO("tua6034_friio", 0x60), }, }; -/* For another type of I2C: - * message sent by a USB control-read/write transaction with data stage. - * Used in init/config of Friio. - */ -static int -gl861_i2c_write_ex(struct dvb_usb_device *d, u8 addr, u8 *wbuf, u16 wlen) -{ - u8 *buf; - int ret; - - buf = kmemdup(wbuf, wlen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), - GL861_REQ_I2C_RAW, GL861_WRITE, - addr << (8 + 1), 0x0100, buf, wlen, 2000); - kfree(buf); - return ret; -} - -static int -gl861_i2c_read_ex(struct dvb_usb_device *d, u8 addr, u8 *rbuf, u16 rlen) -{ - u8 *buf; - int ret; - - buf = kmalloc(rlen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), - GL861_REQ_I2C_READ, GL861_READ, - addr << (8 + 1), 0x0100, buf, rlen, 2000); - if (ret > 0 && rlen > 0) - memcpy(buf, rbuf, rlen); - kfree(buf); - return ret; -} - -/* For I2C transactions to the tuner of Friio (dvb_pll). - * - * Friio uses irregular USB encapsulation for tuner i2c transactions: - * write transacions are encapsulated with a different USB 'request' value. - * - * Although all transactions are sent via the demod(tc90522) - * and the demod provides an i2c adapter for them, it cannot be used in Friio - * since it assumes using the same parent adapter with the demod, - * which does not use the request value and uses same one for both read/write. - * So we define a dedicated i2c adapter here. - */ - -static int -friio_i2c_tuner_read(struct dvb_usb_device *d, struct i2c_msg *msg) -{ - struct gl861 *priv; - u8 addr; - - priv = d_to_priv(d); - addr = priv->i2c_client_demod->addr; - return gl861_i2c_read_ex(d, addr, msg->buf, msg->len); -} - -static int -friio_i2c_tuner_write(struct dvb_usb_device *d, struct i2c_msg *msg) -{ - u8 *buf; - int ret; - struct gl861 *priv; - - priv = d_to_priv(d); - - if (msg->len < 1) - return -EINVAL; - - buf = kmalloc(msg->len + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - buf[0] = msg->addr << 1; - memcpy(buf + 1, msg->buf, msg->len); - - ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), - GL861_REQ_I2C_RAW, GL861_WRITE, - priv->i2c_client_demod->addr << (8 + 1), - 0xFE, buf, msg->len + 1, 2000); - kfree(buf); - return ret; -} - -static int friio_tuner_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], - int num) -{ - struct dvb_usb_device *d = i2c_get_adapdata(adap); - int i; - - if (num > 2) - return -EINVAL; - - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; - - for (i = 0; i < num; i++) { - int ret; - - if (msg[i].flags & I2C_M_RD) - ret = friio_i2c_tuner_read(d, &msg[i]); - else - ret = friio_i2c_tuner_write(d, &msg[i]); - - if (ret < 0) - break; - - usleep_range(1000, 2000); /* avoid I2C errors */ - } - - mutex_unlock(&d->i2c_mutex); - return i; -} - -static struct i2c_algorithm friio_tuner_i2c_algo = { - .master_xfer = friio_tuner_i2c_xfer, - .functionality = gl861_i2c_functionality, -}; /* GPIO control in Friio */ @@ -479,9 +312,11 @@ static int friio_ext_ctl(struct dvb_usb_device *d, /* init/config of gl861 for Friio */ /* NOTE: * This function cannot be moved to friio_init()/dvb_usbv2_init(), - * because the init defined here must be done before any activities like I2C, + * because the init defined here includes a whole device reset, + * it must be run early before any activities like I2C, * but friio_init() is called by dvb-usbv2 after {_frontend, _tuner}_attach(), * where I2C communication is used. + * In addition, this reset is required in reset_resume() as well. * Thus this function is set to be called from _power_ctl(). * * Since it will be called on the early init stage @@ -491,7 +326,7 @@ static int friio_ext_ctl(struct dvb_usb_device *d, static int friio_reset(struct dvb_usb_device *d) { int i, ret; - u8 wbuf[2], rbuf[2]; + u8 wbuf[1], rbuf[2]; static const u8 friio_init_cmds[][2] = { {0x33, 0x08}, {0x37, 0x40}, {0x3a, 0x1f}, {0x3b, 0xff}, @@ -503,16 +338,12 @@ static int friio_reset(struct dvb_usb_device *d) if (ret < 0) return ret; - wbuf[0] = 0x11; - wbuf[1] = 0x02; - ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + ret = gl861_short_write(d, 0x00, 0x11, 0x02); if (ret < 0) return ret; usleep_range(2000, 3000); - wbuf[0] = 0x11; - wbuf[1] = 0x00; - ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + ret = gl861_short_write(d, 0x00, 0x11, 0x00); if (ret < 0) return ret; @@ -522,14 +353,13 @@ static int friio_reset(struct dvb_usb_device *d) */ usleep_range(1000, 2000); - wbuf[0] = 0x03; - wbuf[1] = 0x80; - ret = gl861_i2c_write_ex(d, 0x09, wbuf, 2); + wbuf[0] = 0x80; + ret = gl861_ctrl_msg(d, CMD_WRITE, 0x09 << 9, 0x03, wbuf, 1); if (ret < 0) return ret; usleep_range(2000, 3000); - ret = gl861_i2c_read_ex(d, 0x09, rbuf, 2); + ret = gl861_ctrl_msg(d, CMD_READ, 0x09 << 9, 0x0100, rbuf, 2); if (ret < 0) return ret; if (rbuf[0] != 0xff || rbuf[1] != 0xff) @@ -537,38 +367,33 @@ static int friio_reset(struct dvb_usb_device *d) usleep_range(1000, 2000); - ret = gl861_i2c_write_ex(d, 0x48, wbuf, 2); + wbuf[0] = 0x80; + ret = gl861_ctrl_msg(d, CMD_WRITE, 0x48 << 9, 0x03, wbuf, 1); if (ret < 0) return ret; usleep_range(2000, 3000); - ret = gl861_i2c_read_ex(d, 0x48, rbuf, 2); + ret = gl861_ctrl_msg(d, CMD_READ, 0x48 << 9, 0x0100, rbuf, 2); if (ret < 0) return ret; if (rbuf[0] != 0xff || rbuf[1] != 0xff) return -ENODEV; - wbuf[0] = 0x30; - wbuf[1] = 0x04; - ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + ret = gl861_short_write(d, 0x00, 0x30, 0x04); if (ret < 0) return ret; - wbuf[0] = 0x00; - wbuf[1] = 0x01; - ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + ret = gl861_short_write(d, 0x00, 0x00, 0x01); if (ret < 0) return ret; - wbuf[0] = 0x06; - wbuf[1] = 0x0f; - ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + ret = gl861_short_write(d, 0x00, 0x06, 0x0f); if (ret < 0) return ret; for (i = 0; i < ARRAY_SIZE(friio_init_cmds); i++) { - ret = gl861_i2c_msg(d, 0x00, (u8 *)friio_init_cmds[i], 2, - NULL, 0); + ret = gl861_short_write(d, 0x00, friio_init_cmds[i][0], + friio_init_cmds[i][1]); if (ret < 0) return ret; } @@ -593,6 +418,7 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap) struct gl861 *priv; info = &friio_config.demod_info; + cfg = friio_config.demod_cfg; d = adap_to_d(adap); cl = dvb_module_probe("tc90522", info->type, &d->i2c_adap, info->addr, &cfg); @@ -600,17 +426,10 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; adap->fe[0] = cfg.fe; - /* ignore cfg.tuner_i2c and create new one */ priv = adap_to_priv(adap); priv->i2c_client_demod = cl; - priv->tuner_adap.algo = &friio_tuner_i2c_algo; - priv->tuner_adap.dev.parent = &d->udev->dev; - strscpy(priv->tuner_adap.name, d->name, sizeof(priv->tuner_adap.name)); - strlcat(priv->tuner_adap.name, "-tuner", sizeof(priv->tuner_adap.name)); - priv->demod_sub_i2c = &priv->tuner_adap; - i2c_set_adapdata(&priv->tuner_adap, d); - - return i2c_add_adapter(&priv->tuner_adap); + priv->demod_sub_i2c = cfg.tuner_i2c; + return 0; } static int friio_frontend_detach(struct dvb_usb_adapter *adap) @@ -618,7 +437,6 @@ static int friio_frontend_detach(struct dvb_usb_adapter *adap) struct gl861 *priv; priv = adap_to_priv(adap); - i2c_del_adapter(&priv->tuner_adap); dvb_module_release(priv->i2c_client_demod); return 0; } -- cgit v1.2.3 From 25877478c444f0f97ef78af0e7399333bb36b496 Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Thu, 29 Aug 2019 05:38:20 -0300 Subject: media: dvb-usb-v2/gl861: remove an un-used header file The header contained just internal definitions and they are not used anymore. Signed-off-by: Akihiro Tsukada Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/gl861.c | 2 +- drivers/media/usb/dvb-usb-v2/gl861.h | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 drivers/media/usb/dvb-usb-v2/gl861.h (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index f125264d8df1..f5f774ed05b8 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -5,7 +5,7 @@ */ #include -#include "gl861.h" +#include "dvb_usb.h" #include "zl10353.h" #include "qt1010.h" diff --git a/drivers/media/usb/dvb-usb-v2/gl861.h b/drivers/media/usb/dvb-usb-v2/gl861.h deleted file mode 100644 index 02c00e10748a..000000000000 --- a/drivers/media/usb/dvb-usb-v2/gl861.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _DVB_USB_GL861_H_ -#define _DVB_USB_GL861_H_ - -#include "dvb_usb.h" - -#define GL861_WRITE 0x40 -#define GL861_READ 0xc0 - -#define GL861_REQ_I2C_WRITE 0x01 -#define GL861_REQ_I2C_READ 0x02 -#define GL861_REQ_I2C_RAW 0x03 - -#endif -- cgit v1.2.3 From ecf20d28ff7730d8d446c989eb1cf641ec6e28ce Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Thu, 29 Aug 2019 05:38:21 -0300 Subject: media: dvb-frontends/tc90522: extend i2c algo to support some devices This demod implements an i2c adapter for attached tuner and relays i2c messages from users (dvb adapters / bridge chips). Some of them, such as Friio dvb card using gl861, require each pair of i2c messages for one read to be issued as two separate transactions. This patch adds a configuration option to enable this split. Signed-off-by: Akihiro Tsukada Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tc90522.c | 27 +++++++++++++++++++++++++-- drivers/media/dvb-frontends/tc90522.h | 3 +++ 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index 849d63dbc279..e83836b29715 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -685,10 +685,33 @@ tc90522_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) p += new_msgs[j].len; } - if (i < num) + if (i < num) { ret = -ENOMEM; - else + } else if (!state->cfg.split_tuner_read_i2c || rd_num == 0) { ret = i2c_transfer(state->i2c_client->adapter, new_msgs, j); + } else { + /* + * Split transactions at each I2C_M_RD message. + * Some of the parent device require this, + * such as Friio (see. dvb-usb-gl861). + */ + int from, to; + + ret = 0; + from = 0; + do { + int r; + + to = from + 1; + while (to < j && !(new_msgs[to].flags & I2C_M_RD)) + to++; + r = i2c_transfer(state->i2c_client->adapter, + &new_msgs[from], to - from); + ret = (r <= 0) ? r : ret + r; + from = to; + } while (from < j && ret > 0); + } + if (ret >= 0 && ret < j) ret = -EIO; kfree(new_msgs); diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h index ac0e2ab51924..07e3813bf590 100644 --- a/drivers/media/dvb-frontends/tc90522.h +++ b/drivers/media/dvb-frontends/tc90522.h @@ -28,6 +28,9 @@ struct tc90522_config { /* [OUT] tuner I2C adapter returned by driver */ struct i2c_adapter *tuner_i2c; + + /* [IN] use two separate I2C transactions for one tuner read */ + bool split_tuner_read_i2c; }; #endif /* TC90522_H */ -- cgit v1.2.3 From 43be77fa6ce6907d285269bf98e654ad21a36880 Mon Sep 17 00:00:00 2001 From: Akihiro Tsukada Date: Thu, 29 Aug 2019 05:38:22 -0300 Subject: media: dvb-usb-gl861: support I2C read from tuner via demod Support Friio devices to read from tuner via demod tc90522, by enabling the config option. Signed-off-by: Akihiro Tsukada Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/gl861.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index f5f774ed05b8..19217dcf20f1 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -242,6 +242,7 @@ struct friio_config { static const struct friio_config friio_config = { .demod_info = { I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x18), }, + .demod_cfg = { .split_tuner_read_i2c = true, }, .tuner_info = { I2C_BOARD_INFO("tua6034_friio", 0x60), }, }; -- cgit v1.2.3 From 649cd16c438f51d4cd777e71ca1f47f6e0c5e65d Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 24 Sep 2019 06:49:04 -0300 Subject: media: flexcop-usb: fix NULL-ptr deref in flexcop_usb_transfer_init() If usb_set_interface() failed, iface->cur_altsetting will not be assigned and it will be used in flexcop_usb_transfer_init() It may lead a NULL pointer dereference. Check usb_set_interface() return value in flexcop_usb_init() and return failed to avoid using this NULL pointer. Signed-off-by: Yang Yingliang Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/b2c2/flexcop-usb.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index 1826ff825c2e..4bf85e9b78b8 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -504,7 +504,13 @@ urb_error: static int flexcop_usb_init(struct flexcop_usb *fc_usb) { /* use the alternate setting with the larges buffer */ - usb_set_interface(fc_usb->udev,0,1); + int ret = usb_set_interface(fc_usb->udev, 0, 1); + + if (ret) { + err("set interface failed."); + return ret; + } + switch (fc_usb->udev->speed) { case USB_SPEED_LOW: err("cannot handle USB speed because it is too slow."); -- cgit v1.2.3 From 219031a6e7dff52a066e8b074adc0697f501e3d3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 Oct 2019 10:35:05 -0300 Subject: media: venus: fix build on 32bit environments As reported by jenkins@linuxtv.org, the build with i386 fails with: ld: drivers/media/platform/qcom/venus/helpers.o: in function `venus_helper_load_scale_clocks': (.text+0x1d77): undefined reference to `__udivdi3' ld: (.text+0x1dce): undefined reference to `__udivdi3' make: *** [Makefile:1094: vmlinux] Error 1 That's because it divides an u32 bit integer by a u64 one. Reviewed-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/helpers.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 5ea5d90f8e5f..a172f1ac0b35 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -520,10 +520,11 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst, unsigned long filled_len) { unsigned long vpp_freq = 0, vsp_freq = 0; - u64 fps = inst->fps; + u32 fps = (u32)inst->fps; u32 mbs_per_sec; - mbs_per_sec = load_per_instance(inst) / inst->fps; + mbs_per_sec = load_per_instance(inst) / fps; + vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; /* 21 / 20 is overhead factor */ vpp_freq += vpp_freq / 20; -- cgit v1.2.3 From 1c9b943cdcbd7646c71b51d830234c4b6529b0e7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Oct 2019 09:15:36 -0300 Subject: media: si2168: use bits instead of bool for flags Using bool on struct is not recommended, as it wastes lots of space. So, instead, let's use bits. While here, convert the comments to kernel-doc format. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/si2168.h | 47 +++++++++++++++++-------------- drivers/media/dvb-frontends/si2168_priv.h | 10 +++---- 2 files changed, 31 insertions(+), 26 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h index 50dccb394efa..ecd21adf8950 100644 --- a/drivers/media/dvb-frontends/si2168.h +++ b/drivers/media/dvb-frontends/si2168.h @@ -9,38 +9,43 @@ #define SI2168_H #include -/* - * I2C address - * 0x64 +/** + * struct si2168_config - configuration parameters for si2168 + * + * @fe: + * frontend returned by driver + * @i2c_adapter: + * tuner I2C adapter returned by driver + * @ts_mode: + * Transport Stream mode. Can be: + * - %SI2168_TS_PARALLEL + * - %SI2168_TS_SERIAL + * - %SI2168_TS_TRISTATE + * - %SI2168_TS_CLK_MANUAL + * @ts_clock_inv: + * TS clock inverted + * @ts_clock_gapped: + * TS clock gapped + * @spectral_inversion: + * Inverted spectrum + * + * Note: + * The I2C address of this demod is 0x64. */ struct si2168_config { - /* - * frontend - * returned by driver - */ struct dvb_frontend **fe; - - /* - * tuner I2C adapter - * returned by driver - */ struct i2c_adapter **i2c_adapter; - /* TS mode */ #define SI2168_TS_PARALLEL 0x06 #define SI2168_TS_SERIAL 0x03 #define SI2168_TS_TRISTATE 0x00 #define SI2168_TS_CLK_MANUAL 0x20 u8 ts_mode; - /* TS clock inverted */ - bool ts_clock_inv; - - /* TS clock gapped */ - bool ts_clock_gapped; - - /* Inverted spectrum */ - bool spectral_inversion; + /* Flags */ + unsigned int ts_clock_inv:1; + unsigned int ts_clock_gapped:1; + unsigned int spectral_inversion:1; }; #endif diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 804d5b30c697..18bea5222082 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -34,12 +34,12 @@ struct si2168_dev { unsigned int chip_id; unsigned int version; const char *firmware_name; - bool active; - bool warm; u8 ts_mode; - bool ts_clock_inv; - bool ts_clock_gapped; - bool spectral_inversion; + unsigned int active:1; + unsigned int warm:1; + unsigned int ts_clock_inv:1; + unsigned int ts_clock_gapped:1; + unsigned int spectral_inversion:1; }; /* firmware command struct */ -- cgit v1.2.3 From d7f9e85082f6ed47731fc5fb591eb65fed9547c6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Oct 2019 10:08:28 -0300 Subject: media: af9035: add the formula used for the I2C speed A very old patch sent to the media ML used to contain the I2C speed formula: https://lore.kernel.org/linux-media/1312539895.2763.33.camel@Jason-Linux/ When the ite9135 code was merged with af9035, the formula was lost. As we might need to slow down the speed for some devices, add the formula again. No functional changes. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 3afd18733614..51e607ea3add 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1197,6 +1197,15 @@ err: return ret; } +/* + * The I2C speed register is calculated with: + * I2C speed register = (1000000000 / (24.4 * 16 * I2C_speed)) + * + * The default speed register for it930x is 7, with means a + * speed of ~366 kbps + */ +#define I2C_SPEED_366K 7 + static int it930x_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); @@ -1208,13 +1217,13 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap) dev_dbg(&intf->dev, "adap->id=%d\n", adap->id); - /* I2C master bus 2 clock speed 300k */ - ret = af9035_wr_reg(d, 0x00f6a7, 0x07); + /* I2C master bus 2 clock speed 366k */ + ret = af9035_wr_reg(d, 0x00f6a7, I2C_SPEED_366K); if (ret < 0) goto err; - /* I2C master bus 1,3 clock speed 300k */ - ret = af9035_wr_reg(d, 0x00f103, 0x07); + /* I2C master bus 1,3 clock speed 366k */ + ret = af9035_wr_reg(d, 0x00f103, I2C_SPEED_366K); if (ret < 0) goto err; -- cgit v1.2.3 From 57df53738a8134955beee6ab1b3b006669c0e82e Mon Sep 17 00:00:00 2001 From: Gon Solo Date: Thu, 10 Oct 2019 06:51:01 -0300 Subject: media: si2157: Add option for not downloading firmware. While at it, convert to kernel-doc format and use bits instead of bools. Signed-off-by: Gon Solo Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 6 ++++++ drivers/media/tuners/si2157.h | 33 +++++++++++++++++++-------------- drivers/media/tuners/si2157_priv.h | 5 +++-- 3 files changed, 28 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index e87040d6eca7..898e0f9f8b70 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -118,6 +118,11 @@ static int si2157_init(struct dvb_frontend *fe) goto err; } + if (dev->dont_load_firmware) { + dev_info(&client->dev, "device is buggy, skipping firmware download\n"); + goto skip_fw_download; + } + /* query chip revision */ memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; @@ -440,6 +445,7 @@ static int si2157_probe(struct i2c_client *client, i2c_set_clientdata(client, dev); dev->fe = cfg->fe; dev->inversion = cfg->inversion; + dev->dont_load_firmware = cfg->dont_load_firmware; dev->if_port = cfg->if_port; dev->chiptype = (u8)id->driver_data; dev->if_frequency = 5000000; /* default value of property 0x0706 */ diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h index c22ca784f43f..ffdece3c2eaa 100644 --- a/drivers/media/tuners/si2157.h +++ b/drivers/media/tuners/si2157.h @@ -11,29 +11,34 @@ #include #include -/* - * I2C address - * 0x60 +/** + * struct si2157_config - configuration parameters for si2157 + * + * @fe: + * frontend returned by driver + * @mdev: + * media device returned by driver + * @inversion: + * spectral inversion + * @dont_load_firmware: + * Instead of uploading a new firmware, use the existing one + * @if_port: + * Port selection + * Select the RF interface to use (pins 9+11 or 12+13) + * + * Note: + * The I2C address of this demod is 0x60. */ struct si2157_config { - /* - * frontend - */ struct dvb_frontend *fe; #if defined(CONFIG_MEDIA_CONTROLLER) struct media_device *mdev; #endif - /* - * Spectral Inversion - */ - bool inversion; + unsigned int inversion:1; + unsigned int dont_load_firmware:1; - /* - * Port selection - * Select the RF interface to use (pins 9+11 or 12+13) - */ u8 if_port; }; diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 2bda903358da..778f81b39996 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -23,8 +23,9 @@ enum si2157_pads { struct si2157_dev { struct mutex i2c_mutex; struct dvb_frontend *fe; - bool active; - bool inversion; + unsigned int active:1; + unsigned int inversion:1; + unsigned int dont_load_firmware:1; u8 chiptype; u8 if_port; u32 if_frequency; -- cgit v1.2.3 From 6bf05f759932b813afa289203c8dc63050a0da82 Mon Sep 17 00:00:00 2001 From: Gon Solo Date: Thu, 10 Oct 2019 06:51:03 -0300 Subject: media: af9035: add support for Logilink VG0022A. This includes a hack for the device as it returns only 0xff after a new firmware is loaded. To quote Mauro: "When the [...] firmware that came with the device is replaced by a new one, any I2C data received from the tuner will be replaced by 0xff. Probably, the vendor firmware has some patch specifically designed for this device. So, we can't replace by the generic firmware. The right solution would be to extract the [...] firmware from the original driver and ask the driver to load the specifically designed firmware, but, while we don't have that, the next best solution is to just keep the original firmware at the device." Signed-off-by: Gon Solo Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 51e607ea3add..792667ee5ebc 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1619,6 +1619,24 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap) memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe[0]; + + /* + * HACK: The Logilink VG0022A has a bug: when the si2157 + * firmware that came with the device is replaced by a new + * one, the I2C transfers to the tuner will return just 0xff. + * + * Probably, the vendor firmware has some patch specifically + * designed for this device. So, we can't replace by the + * generic firmware. The right solution would be to extract + * the si2157 firmware from the original driver and ask the + * driver to load the specifically designed firmware, but, + * while we don't have that, the next best solution is to just + * keep the original firmware at the device. + */ + if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_DEXATEK && + le16_to_cpu(d->udev->descriptor.idProduct) == 0x0100) + si2157_config.dont_load_firmware = true; + si2157_config.if_port = it930x_addresses_table[state->it930x_addresses].tuner_if_port; ret = af9035_add_i2c_dev(d, "si2157", it930x_addresses_table[state->it930x_addresses].tuner_i2c_addr, @@ -2130,6 +2148,8 @@ static const struct usb_device_id af9035_id_table[] = { &it930x_props, "ITE 9303 Generic", NULL) }, { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TD310, &it930x_props, "AVerMedia TD310 DVB-T2", NULL) }, + { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x0100, + &it930x_props, "Logilink VG0022A", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, af9035_id_table); -- cgit v1.2.3 From 31218df77e7f1b9b0a136990a500a7f980e6a573 Mon Sep 17 00:00:00 2001 From: Yizhuo Date: Thu, 29 Aug 2019 21:43:59 -0300 Subject: media: media/pci/ivtv: Variable vbi.type could be uninitialized if macro v4l2_subdev_call set __result an error code Inside function compress_sliced_buf(), variable vbi.type is uninitialized if macro v4l2_subdev_call set __result an error code. However, vbi.type is used in the if statement without any check, which is potentially unsafe. Signed-off-by: Yizhuo Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-vbi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ivtv/ivtv-vbi.c b/drivers/media/pci/ivtv/ivtv-vbi.c index 6d22c0107d33..80478b026d75 100644 --- a/drivers/media/pci/ivtv/ivtv-vbi.c +++ b/drivers/media/pci/ivtv/ivtv-vbi.c @@ -325,7 +325,7 @@ static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size) static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav) { u32 line_size = itv->vbi.sliced_decoder_line_size; - struct v4l2_decode_vbi_line vbi; + struct v4l2_decode_vbi_line vbi = {}; int i; unsigned lines = 0; -- cgit v1.2.3 From 672c29b91670ab34de288bb7015e9a12c63d007e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 30 Aug 2019 10:48:27 -0300 Subject: media: i2c: Use div64_ul() for u64-by-unsigned-long divide div_u64() does a 64-by-32 division, while the divisor max2175.xtal_freq is unsigned long, thus 64-bit on 64-bit platforms. Hence the proper function to call is div64_ul(). Note that this change does not have any functional impact, as the crystal frequency must be much lower than the 32-bit limit anyway. On 32-bit platforms, the generated code is the same. But at least on arm64, this saves an AND-instruction to truncate xtal_freq to 32-bit. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max2175.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index 19a3ceea3bc2..506a30e69ced 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -591,8 +591,8 @@ static int max2175_set_lo_freq(struct max2175 *ctx, u32 lo_freq) lo_freq *= lo_mult; int_desired = lo_freq / ctx->xtal_freq; - frac_desired = div_u64((u64)(lo_freq % ctx->xtal_freq) << 20, - ctx->xtal_freq); + frac_desired = div64_ul((u64)(lo_freq % ctx->xtal_freq) << 20, + ctx->xtal_freq); /* Check CSM is not busy */ ret = max2175_poll_csm_ready(ctx); -- cgit v1.2.3 From e6f45ea2e7211850d2301a726d195a61fde0ccda Mon Sep 17 00:00:00 2001 From: Daniel Gonzalez Cabanelas Date: Wed, 11 Sep 2019 14:57:06 -0300 Subject: media: cx88: Add support for NotOnlyTV LV3H card Add support for the PCI hybrid card NotOnlyTV LV3H. This card consists of: - A/V Controller: Conexant CX-23883 - Tuner: Xceive XC3028L - DVB-T Decoder: Zarlink ZL10353 This patch adds support for DVB-T, Analog TV, FM radio, composite and S-video inputs. The IR input isn't supported. Since the PCI subsystem ID (14f1:8852) is the same as the Geniatech X8000-MT, but they are different boards, the card is only supported via insmod option: options cx88xx card=91 For the record this is the eeprom dump, useful if someone wanted to implement the card auto detection: 07 ff ff ff f1 14 52 88 04 04 32 55 f8 00 a2 02 a1 00 40 63 06 11 44 30 03 df 40 80 00 20 00 73 3c 10 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff a0 00 Signed-off-by: Daniel Gonzalez Cabanelas Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-cards.c | 43 +++++++++++++++++++++++++++++++++++++ drivers/media/pci/cx88/cx88-dvb.c | 1 + drivers/media/pci/cx88/cx88.h | 1 + 3 files changed, 45 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 3cd87626cd79..9fa388626bae 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -1781,6 +1781,41 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_NOTONLYTV_LV3H] = { + .name = "NotOnlyTV LV3H", + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + /* if gpio1:bit9 is enabled, DVB-T won't work */ + + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0000, + .gpio1 = 0xa141, + .gpio2 = 0x0000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0000, + .gpio1 = 0xa161, + .gpio2 = 0x0000, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x0000, + .gpio1 = 0xa161, + .gpio2 = 0x0000, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x0000, + .gpio1 = 0xa141, + .gpio2 = 0x0000, + }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = { .name = "DViCO FusionHDTV DVB-T PRO", .tuner_type = TUNER_XC2028, @@ -2654,6 +2689,7 @@ static const struct cx88_subid cx88_subids[] = { .subdevice = 0x6f18, .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, }, { + /* Also NotOnlyTV LV3H (version 1.11 is silkscreened on the board) */ .subvendor = 0x14f1, .subdevice = 0x8852, .card = CX88_BOARD_GENIATECH_X8000_MT, @@ -3121,6 +3157,7 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: return cx88_dvico_xc2028_callback(core, command, arg); + case CX88_BOARD_NOTONLYTV_LV3H: case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_WINFAST_DTV1800H: return cx88_xc3028_winfast1800h_callback(core, command, arg); @@ -3322,6 +3359,7 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) udelay(1000); break; + case CX88_BOARD_NOTONLYTV_LV3H: case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_WINFAST_DTV1800H: cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0); @@ -3378,6 +3416,11 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) */ ctl->disable_power_mgmt = 1; break; + case CX88_BOARD_NOTONLYTV_LV3H: + ctl->demod = XC3028_FE_ZARLINK456; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + ctl->read_not_reliable = 1; + break; case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 0292d0947cc7..202ff9e8c257 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -1378,6 +1378,7 @@ static int dvb_register(struct cx8802_dev *dev) fe->ops.tuner_ops.set_config(fe, &ctl); } break; + case CX88_BOARD_NOTONLYTV_LV3H: case CX88_BOARD_PINNACLE_HYBRID_PCTV: case CX88_BOARD_WINFAST_DTV1800H: fe0->dvb.frontend = dvb_attach(zl10353_attach, diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h index 744a22328ebc..ce4acf6de6aa 100644 --- a/drivers/media/pci/cx88/cx88.h +++ b/drivers/media/pci/cx88/cx88.h @@ -228,6 +228,7 @@ extern const struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_WINFAST_DTV1800H_XC4000 88 #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89 #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90 +#define CX88_BOARD_NOTONLYTV_LV3H 91 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3 From 68085f314d6429ddfb09756623e2bfeec2966909 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Wed, 11 Sep 2019 17:20:12 -0300 Subject: media: gspca: null check create_singlethread_workqueue In sd_start the return value of create_singlethread_workqueue needs null check. Signed-off-by: Navid Emamdoost Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sq905c.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c index 3d7f6dcdd7a8..6ca947aef298 100644 --- a/drivers/media/usb/gspca/sq905c.c +++ b/drivers/media/usb/gspca/sq905c.c @@ -276,6 +276,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* Start the workqueue function to do the streaming */ dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + if (!dev->work_thread) + return -ENOMEM; + queue_work(dev->work_thread, &dev->work_struct); return 0; -- cgit v1.2.3 From 8dbdee8e8acc4f56febb8f5bf226f1ebfdd36219 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Wed, 11 Sep 2019 17:26:00 -0300 Subject: media: usb: null check create_singlethread_workqueue In sd_start return value of create_singlethread_workqueue needs null check. Signed-off-by: Navid Emamdoost Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sq905.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c index 863c485f4275..97799cfb832e 100644 --- a/drivers/media/usb/gspca/sq905.c +++ b/drivers/media/usb/gspca/sq905.c @@ -378,6 +378,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* Start the workqueue function to do the streaming */ dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + if (!dev->work_thread) + return -ENOMEM; + queue_work(dev->work_thread, &dev->work_struct); return 0; -- cgit v1.2.3 From 2eca8e4c1df4864b937752c3aa2f7925114f4806 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 13 Sep 2019 16:06:47 -0300 Subject: media: v4l: cadence: Fix how unsued lanes are handled in 'csi2rx_start()' The 2nd parameter of 'find_first_zero_bit()' is a number of bits, not of bytes. So use 'csi2rx->max_lanes' instead of 'sizeof(lanes_used)'. Fixes: 1fc3b37f34f6 ("media: v4l: cadence: Add Cadence MIPI-CSI2 RX driver") Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/cdns-csi2rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 31ace114eda1..be9ec59774d6 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -129,7 +129,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) */ for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) { unsigned int idx = find_first_zero_bit(&lanes_used, - sizeof(lanes_used)); + csi2rx->max_lanes); set_bit(idx, &lanes_used); reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1); } -- cgit v1.2.3 From e1444e9b0424c70def6352580762d660af50e03f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 22 Sep 2019 04:41:23 -0300 Subject: media: cx88: Fix some error handling path in 'cx8800_initdev()' A call to 'pci_disable_device()' is missing in the error handling path. In some cases, a call to 'free_irq()' may also be missing. Reorder the error handling path, add some new labels and fix the 2 issues mentionned above. This way, the error handling path in more in line with 'cx8800_finidev()' (i.e. the remove function) Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-video.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index dcc0f02aeb70..b8abcd550604 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1277,7 +1277,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, core = cx88_core_get(dev->pci); if (!core) { err = -EINVAL; - goto fail_free; + goto fail_disable; } dev->core = core; @@ -1323,7 +1323,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, cc->step, cc->default_value); if (!vc) { err = core->audio_hdl.error; - goto fail_core; + goto fail_irq; } vc->priv = (void *)cc; } @@ -1337,7 +1337,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, cc->step, cc->default_value); if (!vc) { err = core->video_hdl.error; - goto fail_core; + goto fail_irq; } vc->priv = (void *)cc; if (vc->id == V4L2_CID_CHROMA_AGC) @@ -1509,11 +1509,14 @@ static int cx8800_initdev(struct pci_dev *pci_dev, fail_unreg: cx8800_unregister_video(dev); - free_irq(pci_dev->irq, dev); mutex_unlock(&core->lock); +fail_irq: + free_irq(pci_dev->irq, dev); fail_core: core->v4ldev = NULL; cx88_core_put(core, dev->pci); +fail_disable: + pci_disable_device(pci_dev); fail_free: kfree(dev); return err; -- cgit v1.2.3 From 69bb7eb88346ec19d024276205ddabc72d21dc83 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sun, 6 Oct 2019 04:43:24 -0300 Subject: media: cx231xx: remove duplicated include from cx231xx-417.c Remove duplicated include. Signed-off-by: YueHaibing Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-417.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 46d0215deee6..1aec4459f50a 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 27dbc2e63b9aff694a22c69bba18375750cb6643 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 7 Oct 2019 06:52:40 -0300 Subject: media: cx18: make array mapping static, makes object smaller Don't populate the array mapping on the stack but instead make it static. Makes the object code smaller by 79 bytes. Before: text data bss dec hex filename 27572 2096 0 29668 73e4 drivers/media/pci/cx18/cx18-ioctl.o After: text data bss dec hex filename 27429 2160 0 29589 7395 drivers/media/pci/cx18/cx18-ioctl.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index 85f3e7307538..fa57e12f2ac8 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -664,7 +664,7 @@ static int _cx18_process_idx_data(struct cx18_buffer *buf, struct cx18_enc_idx_entry *e_buf; /* Frame type lookup: 1=I, 2=P, 4=B */ - const int mapping[8] = { + static const int mapping[8] = { -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1 }; -- cgit v1.2.3 From 59251a8be3c258c6ce59dcce703ce440a4e03b82 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 7 Oct 2019 07:18:48 -0300 Subject: media: gspca: make array st6422_bridge_init static, makes object smaller Don't populate the array st6422_bridge_init on the stack but instead make it static. Makes the object code smaller by 231 bytes. Before: text data bss dec hex filename 3419 752 64 4235 108b gspca/stv06xx/stv06xx_st6422.o After: text data bss dec hex filename 3124 816 64 4004 fa4 gspca/stv06xx/stv06xx_st6422.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c index 7104a88b1e43..aac19d449be2 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c @@ -117,7 +117,7 @@ static int st6422_init(struct sd *sd) { int err = 0, i; - const u16 st6422_bridge_init[][2] = { + static const u16 st6422_bridge_init[][2] = { { STV_ISO_ENABLE, 0x00 }, /* disable capture */ { 0x1436, 0x00 }, { 0x1432, 0x03 }, /* 0x00-0x1F brightness */ -- cgit v1.2.3 From c7a191464078262bf799136317c95824e26a222b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 7 Oct 2019 12:09:04 -0300 Subject: media: usbvision: Fix invalid accesses after device disconnect The syzbot fuzzer found two invalid-access bugs in the usbvision driver. These bugs occur when userspace keeps the device file open after the device has been disconnected and usbvision_disconnect() has set usbvision->dev to NULL: When the device file is closed, usbvision_radio_close() tries to issue a usb_set_interface() call, passing the NULL pointer as its first argument. If userspace performs a querycap ioctl call, vidioc_querycap() calls usb_make_path() with the same NULL pointer. This patch fixes the problems by making the appropriate tests beforehand. Note that vidioc_querycap() is protected by usbvision->v4l2_lock, acquired in a higher layer of the V4L2 subsystem. Reported-and-tested-by: syzbot+7fa38a608b1075dfd634@syzkaller.appspotmail.com Signed-off-by: Alan Stern CC: Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index cdc66adda755..62dec73aec6e 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -453,6 +453,9 @@ static int vidioc_querycap(struct file *file, void *priv, { struct usb_usbvision *usbvision = video_drvdata(file); + if (!usbvision->dev) + return -ENODEV; + strscpy(vc->driver, "USBVision", sizeof(vc->driver)); strscpy(vc->card, usbvision_device_data[usbvision->dev_model].model_string, @@ -1099,8 +1102,9 @@ static int usbvision_radio_close(struct file *file) mutex_lock(&usbvision->v4l2_lock); /* Set packet size to 0 */ usbvision->iface_alt = 0; - usb_set_interface(usbvision->dev, usbvision->iface, - usbvision->iface_alt); + if (usbvision->dev) + usb_set_interface(usbvision->dev, usbvision->iface, + usbvision->iface_alt); usbvision_audio_off(usbvision); usbvision->radio = 0; -- cgit v1.2.3 From 9e08117c9d4efc1e1bc6fce83dab856d9fd284b6 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 7 Oct 2019 12:09:53 -0300 Subject: media: usbvision: Fix races among open, close, and disconnect Visual inspection of the usbvision driver shows that it suffers from three races between its open, close, and disconnect handlers. In particular, the driver is careful to update its usbvision->user and usbvision->remove_pending flags while holding the private mutex, but: usbvision_v4l2_close() and usbvision_radio_close() don't hold the mutex while they check the value of usbvision->remove_pending; usbvision_disconnect() doesn't hold the mutex while checking the value of usbvision->user; and also, usbvision_v4l2_open() and usbvision_radio_open() don't check whether the device has been unplugged before allowing the user to open the device files. Each of these can potentially lead to usbvision_release() being called twice and use-after-free errors. This patch fixes the races by reading the flags while the mutex is still held and checking for pending removes before allowing an open to succeed. Signed-off-by: Alan Stern CC: Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-video.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 62dec73aec6e..93d36aab824f 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -314,6 +314,10 @@ static int usbvision_v4l2_open(struct file *file) if (mutex_lock_interruptible(&usbvision->v4l2_lock)) return -ERESTARTSYS; + if (usbvision->remove_pending) { + err_code = -ENODEV; + goto unlock; + } if (usbvision->user) { err_code = -EBUSY; } else { @@ -377,6 +381,7 @@ unlock: static int usbvision_v4l2_close(struct file *file) { struct usb_usbvision *usbvision = video_drvdata(file); + int r; PDEBUG(DBG_IO, "close"); @@ -391,9 +396,10 @@ static int usbvision_v4l2_close(struct file *file) usbvision_scratch_free(usbvision); usbvision->user--; + r = usbvision->remove_pending; mutex_unlock(&usbvision->v4l2_lock); - if (usbvision->remove_pending) { + if (r) { printk(KERN_INFO "%s: Final disconnect\n", __func__); usbvision_release(usbvision); return 0; @@ -1064,6 +1070,11 @@ static int usbvision_radio_open(struct file *file) if (mutex_lock_interruptible(&usbvision->v4l2_lock)) return -ERESTARTSYS; + + if (usbvision->remove_pending) { + err_code = -ENODEV; + goto out; + } err_code = v4l2_fh_open(file); if (err_code) goto out; @@ -1096,6 +1107,7 @@ out: static int usbvision_radio_close(struct file *file) { struct usb_usbvision *usbvision = video_drvdata(file); + int r; PDEBUG(DBG_IO, ""); @@ -1109,9 +1121,10 @@ static int usbvision_radio_close(struct file *file) usbvision_audio_off(usbvision); usbvision->radio = 0; usbvision->user--; + r = usbvision->remove_pending; mutex_unlock(&usbvision->v4l2_lock); - if (usbvision->remove_pending) { + if (r) { printk(KERN_INFO "%s: Final disconnect\n", __func__); v4l2_fh_release(file); usbvision_release(usbvision); @@ -1543,6 +1556,7 @@ err_usb: static void usbvision_disconnect(struct usb_interface *intf) { struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf)); + int u; PDEBUG(DBG_PROBE, ""); @@ -1559,13 +1573,14 @@ static void usbvision_disconnect(struct usb_interface *intf) v4l2_device_disconnect(&usbvision->v4l2_dev); usbvision_i2c_unregister(usbvision); usbvision->remove_pending = 1; /* Now all ISO data will be ignored */ + u = usbvision->user; usb_put_dev(usbvision->dev); usbvision->dev = NULL; /* USB device is no more */ mutex_unlock(&usbvision->v4l2_lock); - if (usbvision->user) { + if (u) { printk(KERN_INFO "%s: In use, disconnect pending\n", __func__); wake_up_interruptible(&usbvision->wait_frame); -- cgit v1.2.3 From 1edfa9b1687ed8aac39a0423345f8a76a1cff58c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 8 Oct 2019 06:01:33 -0300 Subject: media: vimc: Fix error return code in vimc_register_devices() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: f13d5f361959 ("media: vimc: Collapse component structure into a single monolithic driver") Signed-off-by: Wei Yongjun Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index 6e3e5c91ae39..2d20a7c10398 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -202,8 +202,10 @@ static int vimc_register_devices(struct vimc_device *vimc) vimc->ent_devs = kmalloc_array(vimc->pipe_cfg->num_ents, sizeof(*vimc->ent_devs), GFP_KERNEL); - if (!vimc->ent_devs) + if (!vimc->ent_devs) { + ret = -ENOMEM; goto err_v4l2_unregister; + } /* Invoke entity config hooks to initialize and register subdevs */ ret = vimc_add_subdevs(vimc); -- cgit v1.2.3 From aacbd4ff3a8b2fb10576497571a51ff85d24bbb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Tue, 8 Oct 2019 20:22:00 -0300 Subject: media: rcar-vin: Rename wrongly named rectangle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After recent refactoring the rectangle named crop no longer reflects it usage, to contain the source rectangle. Fix this by renaming it. There is no functional change. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index a7ee44dd248e..f809350c514c 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -181,7 +181,8 @@ static int rvin_reset_format(struct rvin_dev *vin) static int rvin_try_format(struct rvin_dev *vin, u32 which, struct v4l2_pix_format *pix, - struct v4l2_rect *crop, struct v4l2_rect *compose) + struct v4l2_rect *src_rect, + struct v4l2_rect *compose) { struct v4l2_subdev *sd = vin_to_source(vin); struct v4l2_subdev_pad_config *pad_cfg; @@ -214,11 +215,11 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, v4l2_fill_pix_format(pix, &format.format); - if (crop) { - crop->top = 0; - crop->left = 0; - crop->width = pix->width; - crop->height = pix->height; + if (src_rect) { + src_rect->top = 0; + src_rect->left = 0; + src_rect->width = pix->width; + src_rect->height = pix->height; } if (field != V4L2_FIELD_ANY) @@ -266,21 +267,21 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct rvin_dev *vin = video_drvdata(file); - struct v4l2_rect crop, compose; + struct v4l2_rect src_rect, compose; int ret; if (vb2_is_busy(&vin->queue)) return -EBUSY; ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, - &crop, &compose); + &src_rect, &compose); if (ret) return ret; vin->format = f->fmt.pix; - v4l2_rect_map_inside(&vin->crop, &crop); + v4l2_rect_map_inside(&vin->crop, &src_rect); v4l2_rect_map_inside(&vin->compose, &compose); - vin->src_rect = crop; + vin->src_rect = src_rect; return 0; } -- cgit v1.2.3 From dbb8d05a9d11651c47035653b5b813f2fa55ba80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Tue, 8 Oct 2019 20:22:01 -0300 Subject: media: rcar-vin: Create compose rectangle where it is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rectangle used to correct the compose settings when changing the format was created inside a helper function and not where it was used. This is confusing and makes the code harder to read, fix this. This cleanup is made possible due to refactoring elsewhere and there is no functional change. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index f809350c514c..9a9b89c0dc0b 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -181,8 +181,7 @@ static int rvin_reset_format(struct rvin_dev *vin) static int rvin_try_format(struct rvin_dev *vin, u32 which, struct v4l2_pix_format *pix, - struct v4l2_rect *src_rect, - struct v4l2_rect *compose) + struct v4l2_rect *src_rect) { struct v4l2_subdev *sd = vin_to_source(vin); struct v4l2_subdev_pad_config *pad_cfg; @@ -229,13 +228,6 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, pix->height = height; rvin_format_align(vin, pix); - - if (compose) { - compose->top = 0; - compose->left = 0; - compose->width = pix->width; - compose->height = pix->height; - } done: v4l2_subdev_free_pad_config(pad_cfg); @@ -259,28 +251,33 @@ static int rvin_try_fmt_vid_cap(struct file *file, void *priv, { struct rvin_dev *vin = video_drvdata(file); - return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL, - NULL); + return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL); } static int rvin_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct rvin_dev *vin = video_drvdata(file); - struct v4l2_rect src_rect, compose; + struct v4l2_rect fmt_rect, src_rect; int ret; if (vb2_is_busy(&vin->queue)) return -EBUSY; ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, - &src_rect, &compose); + &src_rect); if (ret) return ret; vin->format = f->fmt.pix; + + fmt_rect.top = 0; + fmt_rect.left = 0; + fmt_rect.width = vin->format.width; + fmt_rect.height = vin->format.height; + v4l2_rect_map_inside(&vin->crop, &src_rect); - v4l2_rect_map_inside(&vin->compose, &compose); + v4l2_rect_map_inside(&vin->compose, &fmt_rect); vin->src_rect = src_rect; return 0; -- cgit v1.2.3 From 5c9de1fa03e9bb67c8d82240fcb8f550036090b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Thu, 5 Sep 2019 18:25:17 -0300 Subject: media: rcar-vin: Use bytes per line instead of width for UV offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The image size is doubled for NV16 and is calculated as bytesperline * height * 2 to accommodate the split of UV data. When writing the offset to hardware, the width is used instead of bytesperline, fix this. Signed-off-by: Niklas Söderlund Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index c7859b329dd4..af4f774149f0 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -703,8 +703,8 @@ static int rvin_setup(struct rvin_dev *vin) switch (vin->format.pixelformat) { case V4L2_PIX_FMT_NV16: rvin_write(vin, - ALIGN(vin->format.width * vin->format.height, 0x80), - VNUVAOF_REG); + ALIGN(vin->format.bytesperline * vin->format.height, + 0x80), VNUVAOF_REG); dmr = VNDMR_DTMD_YCSEP; output_is_yuv = true; break; -- cgit v1.2.3 From d8b1ad7ce76513efaa463c25d261dab67f7f1f67 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Wed, 18 Sep 2019 06:20:48 -0300 Subject: media: platform: Use devm_platform_ioremap_resource() in two functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify these function implementations by using a known wrapper function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Reviewed-by: Geert Uytterhoeven Tested-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c | 8 +------- drivers/media/platform/rcar-vin/rcar-core.c | 7 +------ 2 files changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c index 00d090df11bb..944771ee5f5c 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -253,13 +253,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) } for (i = 0; i < NUM_MAX_VDEC_REG_BASE; i++) { - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (res == NULL) { - dev_err(&pdev->dev, "get memory resource failed."); - ret = -ENXIO; - goto err_res; - } - dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res); + dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); if (IS_ERR((__force void *)dev->reg_base[i])) { ret = PTR_ERR((__force void *)dev->reg_base[i]); goto err_res; diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 6993484ff0f3..334c62805959 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1282,7 +1282,6 @@ static int rcar_vin_probe(struct platform_device *pdev) { const struct soc_device_attribute *attr; struct rvin_dev *vin; - struct resource *mem; int irq, ret; vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL); @@ -1301,11 +1300,7 @@ static int rcar_vin_probe(struct platform_device *pdev) if (attr) vin->info = attr->data; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mem == NULL) - return -EINVAL; - - vin->base = devm_ioremap_resource(vin->dev, mem); + vin->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(vin->base)) return PTR_ERR(vin->base); -- cgit v1.2.3 From b35d6c02aa3ca88f80c8da099ebabfe426fab759 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 12:06:29 -0300 Subject: media: v4l2-core: Implement v4l2_ctrl_new_std_compound Currently compound controls do not have a simple way of initializing its values. This results in ofuscated code with type_ops init. This patch introduces a new field on the control with the default value for the compound control that can be set with the brand new v4l2_ctrl_new_std_compound function Suggested-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado [hverkuil@xs4all.nl: fix checkpatch warning] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 50 +++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1d8f38824631..219d8aeefa20 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -29,6 +29,8 @@ #define call_op(master, op) \ (has_op(master, op) ? master->ops->op(master) : 0) +static const union v4l2_ctrl_ptr ptr_null; + /* Internal temporary helper struct, one for each v4l2_ext_control */ struct v4l2_ctrl_helper { /* Pointer to the control reference of the master control */ @@ -1530,7 +1532,10 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; void *p = ptr.p + idx * ctrl->elem_size; - memset(p, 0, ctrl->elem_size); + if (ctrl->p_def.p) + memcpy(p, ctrl->p_def.p, ctrl->elem_size); + else + memset(p, 0, ctrl->elem_size); /* * The cast is needed to get rid of a gcc warning complaining that @@ -2354,7 +2359,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, s64 min, s64 max, u64 step, s64 def, const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size, u32 flags, const char * const *qmenu, - const s64 *qmenu_int, void *priv) + const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def, + void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra; @@ -2460,6 +2466,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, is_array) sz_extra += 2 * tot_ctrl_size; + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p) + sz_extra += elem_size; + ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); if (ctrl == NULL) { handler_set_err(hdl, -ENOMEM); @@ -2503,6 +2512,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->p_new.p = &ctrl->val; ctrl->p_cur.p = &ctrl->cur.val; } + + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p) { + ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size; + memcpy(ctrl->p_def.p, p_def.p, elem_size); + } + for (idx = 0; idx < elems; idx++) { ctrl->type_ops->init(ctrl, idx, ctrl->p_cur); ctrl->type_ops->init(ctrl, idx, ctrl->p_new); @@ -2554,7 +2569,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, type, min, max, is_menu ? cfg->menu_skip_mask : step, def, cfg->dims, cfg->elem_size, - flags, qmenu, qmenu_int, priv); + flags, qmenu, qmenu_int, ptr_null, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -2579,7 +2594,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, min, max, step, def, NULL, 0, - flags, NULL, NULL, NULL); + flags, NULL, NULL, ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -2612,7 +2627,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, mask, def, NULL, 0, - flags, qmenu, qmenu_int, NULL); + flags, qmenu, qmenu_int, ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -2644,11 +2659,32 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, mask, def, NULL, 0, - flags, qmenu, NULL, NULL); + flags, qmenu, NULL, ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); +/* Helper function for standard compound controls */ +struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, u32 id, + const union v4l2_ctrl_ptr p_def) +{ + const char *name; + enum v4l2_ctrl_type type; + u32 flags; + s64 min, max, step, def; + + v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); + if (type < V4L2_CTRL_COMPOUND_TYPES) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, + min, max, step, def, NULL, 0, + flags, NULL, NULL, p_def, NULL); +} +EXPORT_SYMBOL(v4l2_ctrl_new_std_compound); + /* Helper function for standard integer menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, @@ -2669,7 +2705,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, 0, def, NULL, 0, - flags, NULL, qmenu_int, NULL); + flags, NULL, qmenu_int, ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); -- cgit v1.2.3 From d1dc49370f8371b00e682ac409aa1987ce641e93 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 12:06:31 -0300 Subject: media: add V4L2_CTRL_TYPE_AREA control type This type contains the width and the height of a rectangular area. Reviewed-by: Jacopo Mondi Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 219d8aeefa20..96cab2e173d3 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1677,6 +1677,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, { struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; + struct v4l2_area *area; void *p = ptr.p + idx * ctrl->elem_size; switch ((u32)ctrl->type) { @@ -1753,6 +1754,11 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, zero_padding(p_vp8_frame_header->entropy_header); zero_padding(p_vp8_frame_header->coder_state); break; + case V4L2_CTRL_TYPE_AREA: + area = p; + if (!area->width || !area->height) + return -EINVAL; + break; default: return -EINVAL; } @@ -2427,6 +2433,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header); break; + case V4L2_CTRL_TYPE_AREA: + elem_size = sizeof(struct v4l2_area); + break; default: if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); @@ -4116,6 +4125,18 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); +int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl, + const struct v4l2_area *area) +{ + lockdep_assert_held(ctrl->handler->lock); + + /* It's a driver bug if this happens. */ + WARN_ON(ctrl->type != V4L2_CTRL_TYPE_AREA); + *ctrl->p_new.p_area = *area; + return set_ctrl(NULL, ctrl, 0); +} +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_area); + void v4l2_ctrl_request_complete(struct media_request *req, struct v4l2_ctrl_handler *main_hdl) { -- cgit v1.2.3 From 61fd036d01111679b01e4b92e6bd0cdd33809aea Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 12:06:33 -0300 Subject: media: add V4L2_CID_UNIT_CELL_SIZE control This control returns the unit cell size in nanometres. The struct provides the width and the height in separated fields to take into consideration asymmetric pixels and/or hardware binning. This control is required for automatic calibration of sensors/cameras. Reviewed-by: Philipp Zabel Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 96cab2e173d3..bf50d37ef6c1 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -996,6 +996,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; case V4L2_CID_PAN_SPEED: return "Pan, Speed"; case V4L2_CID_TILT_SPEED: return "Tilt, Speed"; + case V4L2_CID_UNIT_CELL_SIZE: return "Unit Cell Size"; /* FM Radio Modulator controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -1377,6 +1378,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER: *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER; break; + case V4L2_CID_UNIT_CELL_SIZE: + *type = V4L2_CTRL_TYPE_AREA; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; -- cgit v1.2.3 From d30f4e3d4fbdaa1ccc650965fe4f97b37a31c334 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 12:06:36 -0300 Subject: media: imx214: Add new control with V4L2_CID_UNIT_CELL_SIZE According to the product brief, the unit cell size is 1120 nanometers^2. https://www.sony-semicon.co.jp/products_en/IS/sensor1/img/products/ProductBrief_IMX214_20150428.pdf Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx214.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 159a3a604f0e..adcaaa8c86d1 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -47,6 +47,7 @@ struct imx214 { struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *link_freq; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *unit_size; struct regulator_bulk_data supplies[IMX214_NUM_SUPPLIES]; @@ -948,6 +949,10 @@ static int imx214_probe(struct i2c_client *client) static const s64 link_freq[] = { IMX214_DEFAULT_LINK_FREQ, }; + static const struct v4l2_area unit_size = { + .width = 1120, + .height = 1120, + }; int ret; ret = imx214_parse_fwnode(dev); @@ -1029,6 +1034,10 @@ static int imx214_probe(struct i2c_client *client) V4L2_CID_EXPOSURE, 0, 3184, 1, 0x0c70); + imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls, + NULL, + V4L2_CID_UNIT_CELL_SIZE, + v4l2_ctrl_ptr_create((void *)&unit_size)); ret = imx214->ctrls.error; if (ret) { dev_err(&client->dev, "%s control init failed (%d)\n", -- cgit v1.2.3 From 5139de5d5e60aeb38146a8bf5bfaa09c54a8eeb7 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:49 -0300 Subject: media: ti-vpe: Fix a parallel build issue When TI CAL was introduce as another driver under platform/ti-vpe adding a second entry into the ti-vpe directory in the platform Makefile caused issues during parallel build. Signed-off-by: Benoit Parrot Signed-off-by: Jyri Sarha Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6ee7eb0d36f4..d13db96e3015 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -19,9 +19,7 @@ obj-$(CONFIG_VIDEO_VIVID) += vivid/ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ -obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/ - -obj-$(CONFIG_VIDEO_TI_CAL) += ti-vpe/ +obj-y += ti-vpe/ obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o obj-$(CONFIG_VIDEO_CODA) += coda/ -- cgit v1.2.3 From 102af9b9922f658f705a4b0deaccabac409131bf Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:50 -0300 Subject: media: ti-vpe: vpe: Fix Motion Vector vpdma stride commit 3dc2046ca78b ("[media] media: ti-vpe: vpe: allow use of user specified stride") and commit da4414eaed15 ("[media] media: ti-vpe: vpdma: add support for user specified stride") resulted in the Motion Vector stride to be the same as the image stride. This caused memory corruption in the output image as mentioned in commit 00db969964c8 ("[media] media: ti-vpe: vpe: Fix line stride for output motion vector"). Fixes: 3dc2046ca78b ("[media] media: ti-vpe: vpe: allow use of user specified stride") Fixes: da4414eaed15 ("[media] media: ti-vpe: vpdma: add support for user specified stride") Signed-off-by: Benoit Parrot Acked-by: Nikhil Devshatwar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 60b575bb44c4..5ba72445584d 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1013,11 +1013,14 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) dma_addr_t dma_addr; u32 flags = 0; u32 offset = 0; + u32 stride; if (port == VPE_PORT_MV_OUT) { vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; dma_addr = ctx->mv_buf_dma[mv_buf_selector]; q_data = &ctx->q_data[Q_DATA_SRC]; + stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3, + VPDMA_STRIDE_ALIGN); } else { /* to incorporate interleaved formats */ int plane = fmt->coplanar ? p_data->vb_part : 0; @@ -1044,6 +1047,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) } /* Apply the offset */ dma_addr += offset; + stride = q_data->bytesperline[VPE_LUMA]; } if (q_data->flags & Q_DATA_FRAME_1D) @@ -1055,7 +1059,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) MAX_W, MAX_H); vpdma_add_out_dtd(&ctx->desc_list, q_data->width, - q_data->bytesperline[VPE_LUMA], &q_data->c_rect, + stride, &q_data->c_rect, vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1, MAX_OUT_HEIGHT_REG1, p_data->channel, flags); } @@ -1074,10 +1078,13 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) dma_addr_t dma_addr; u32 flags = 0; u32 offset = 0; + u32 stride; if (port == VPE_PORT_MV_IN) { vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; dma_addr = ctx->mv_buf_dma[mv_buf_selector]; + stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3, + VPDMA_STRIDE_ALIGN); } else { /* to incorporate interleaved formats */ int plane = fmt->coplanar ? p_data->vb_part : 0; @@ -1104,6 +1111,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) } /* Apply the offset */ dma_addr += offset; + stride = q_data->bytesperline[VPE_LUMA]; if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) { /* @@ -1139,10 +1147,10 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12) frame_height /= 2; - vpdma_add_in_dtd(&ctx->desc_list, q_data->width, - q_data->bytesperline[VPE_LUMA], &q_data->c_rect, - vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width, - frame_height, 0, 0); + vpdma_add_in_dtd(&ctx->desc_list, q_data->width, stride, + &q_data->c_rect, vpdma_fmt, dma_addr, + p_data->channel, field, flags, frame_width, + frame_height, 0, 0); } /* -- cgit v1.2.3 From 4d59c7d455853edca990848b1cb7c482b4628f32 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:51 -0300 Subject: media: ti-vpe: vpe: Add missing null pointer checks A few NULL pointer checks were missing. Add check with appropriate return code. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 5ba72445584d..56f60dbea15c 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1537,6 +1537,8 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) return -EINVAL; q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; pix->width = q_data->width; pix->height = q_data->height; @@ -2001,6 +2003,8 @@ static int vpe_queue_setup(struct vb2_queue *vq, struct vpe_q_data *q_data; q_data = get_q_data(ctx, vq->type); + if (!q_data) + return -EINVAL; *nplanes = q_data->nplanes; @@ -2025,6 +2029,8 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); q_data = get_q_data(ctx, vb->vb2_queue->type); + if (!q_data) + return -EINVAL; num_planes = q_data->nplanes; if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { @@ -2481,7 +2487,12 @@ static int vpe_probe(struct platform_device *pdev) mutex_init(&dev->dev_mutex); dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "vpe_top"); + "vpe_top"); + if (!dev->res) { + dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); + return -ENODEV; + } + /* * HACK: we get resource info from device tree in the form of a list of * VPE sub blocks, the driver currently uses only the base of vpe_top -- cgit v1.2.3 From 55c686ff93f1287c4f7278c78926c11611cf7e00 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:52 -0300 Subject: media: ti-vpe: vpe: Remove unnecessary use of container_of Instead of saving a pointer to the 'fh' member of struct vpe_ctx to later have to use container_of to retrieve the actual pointer to the context structure, which seems to confuse static code analysis tool anyways, just save the pointer to the actual structure and then retrieve it directly. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 56f60dbea15c..0e9cb0319a92 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -900,14 +900,6 @@ static int set_srcdst_params(struct vpe_ctx *ctx) return 0; } -/* - * Return the vpe_ctx structure for a given struct file - */ -static struct vpe_ctx *file2ctx(struct file *file) -{ - return container_of(file->private_data, struct vpe_ctx, fh); -} - /* * mem2mem callbacks */ @@ -1527,7 +1519,7 @@ static int vpe_enum_fmt(struct file *file, void *priv, static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct vpe_ctx *ctx = file2ctx(file); + struct vpe_ctx *ctx = file->private_data; struct vb2_queue *vq; struct vpe_q_data *q_data; int i; @@ -1689,7 +1681,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct vpe_ctx *ctx = file2ctx(file); + struct vpe_ctx *ctx = file->private_data; struct vpe_fmt *fmt = find_format(f); if (V4L2_TYPE_IS_OUTPUT(f->type)) @@ -1762,7 +1754,7 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { int ret; - struct vpe_ctx *ctx = file2ctx(file); + struct vpe_ctx *ctx = file->private_data; ret = vpe_try_fmt(file, priv, f); if (ret) @@ -1847,7 +1839,7 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) static int vpe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) { - struct vpe_ctx *ctx = file2ctx(file); + struct vpe_ctx *ctx = file->private_data; struct vpe_q_data *q_data; bool use_c_rect = false; @@ -1908,7 +1900,7 @@ static int vpe_g_selection(struct file *file, void *fh, static int vpe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { - struct vpe_ctx *ctx = file2ctx(file); + struct vpe_ctx *ctx = file->private_data; struct vpe_q_data *q_data; struct v4l2_selection sel = *s; int ret; @@ -2275,7 +2267,7 @@ static int vpe_open(struct file *file) init_adb_hdrs(ctx); v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; + file->private_data = ctx; hdl = &ctx->hdl; v4l2_ctrl_handler_init(hdl, 1); @@ -2360,7 +2352,7 @@ free_ctx: static int vpe_release(struct file *file) { struct vpe_dev *dev = video_drvdata(file); - struct vpe_ctx *ctx = file2ctx(file); + struct vpe_ctx *ctx = file->private_data; vpe_dbg(dev, "releasing instance %p\n", ctx); -- cgit v1.2.3 From 95959793d20d0c12e1a81309dbd1a652f3ee197b Mon Sep 17 00:00:00 2001 From: Nikhil Devshatwar Date: Mon, 7 Oct 2019 12:09:53 -0300 Subject: media: ti-vpe: Add support for SEQ_BT SEQ_BT indicates the buffer for bottom field needs to be processed before the top field. Simplify the field selection logic to support SEQ_BT as well. Modify the interlace flags to include any of alternate, SEQ_TB, SEQ_BT. Update other format error checking to consider SEQ_BT. Replace SEQ_TB with SEQ_XX wherever applicable. Signed-off-by: Nikhil Devshatwar Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 73 +++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 27 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 0e9cb0319a92..5d0ec5f7ca25 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -328,9 +328,14 @@ struct vpe_q_data { #define Q_DATA_MODE_TILED BIT(1) #define Q_DATA_INTERLACED_ALTERNATE BIT(2) #define Q_DATA_INTERLACED_SEQ_TB BIT(3) +#define Q_DATA_INTERLACED_SEQ_BT BIT(4) + +#define Q_IS_SEQ_XX (Q_DATA_INTERLACED_SEQ_TB | \ + Q_DATA_INTERLACED_SEQ_BT) #define Q_IS_INTERLACED (Q_DATA_INTERLACED_ALTERNATE | \ - Q_DATA_INTERLACED_SEQ_TB) + Q_DATA_INTERLACED_SEQ_TB | \ + Q_DATA_INTERLACED_SEQ_BT) enum { Q_DATA_SRC = 0, @@ -1105,24 +1110,31 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) dma_addr += offset; stride = q_data->bytesperline[VPE_LUMA]; - if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) { - /* - * Use top or bottom field from same vb alternately - * f,f-1,f-2 = TBT when seq is even - * f,f-1,f-2 = BTB when seq is odd - */ - field = (p_data->vb_index + (ctx->sequence % 2)) % 2; + /* + * field used in VPDMA desc = 0 (top) / 1 (bottom) + * Use top or bottom field from same vb alternately + * For each de-interlacing operation, f,f-1,f-2 should be one + * of TBT or BTB + */ + if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB || + q_data->flags & Q_DATA_INTERLACED_SEQ_BT) { + /* Select initial value based on format */ + if (q_data->flags & Q_DATA_INTERLACED_SEQ_BT) + field = 1; + else + field = 0; + + /* Toggle for each vb_index and each operation */ + field = (field + p_data->vb_index + ctx->sequence) % 2; if (field) { - /* - * bottom field of a SEQ_TB buffer - * Skip the top field data by - */ int height = q_data->height / 2; int bpp = fmt->fourcc == V4L2_PIX_FMT_NV12 ? 1 : (vpdma_fmt->depth >> 3); + if (plane) height /= 2; + dma_addr += q_data->width * height * bpp; } } @@ -1177,12 +1189,14 @@ static void device_run(void *priv) struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; - if (ctx->deinterlacing && s_q_data->flags & Q_DATA_INTERLACED_SEQ_TB && - ctx->sequence % 2 == 0) { - /* When using SEQ_TB buffers, When using it first time, - * No need to remove the buffer as the next field is present - * in the same buffer. (so that job_ready won't fail) - * It will be removed when using bottom field + if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX && + ctx->sequence % 2 == 0) { + /* When using SEQ_XX type buffers, each buffer has two fields + * each buffer has two fields (top & bottom) + * Removing one buffer is actually getting two fields + * Alternate between two operations:- + * Even : consume one field but DO NOT REMOVE from queue + * Odd : consume other field and REMOVE from queue */ ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); WARN_ON(ctx->src_vbs[0] == NULL); @@ -1573,8 +1587,10 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, return -EINVAL; } - if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE - && pix->field != V4L2_FIELD_SEQ_TB) + if (pix->field != V4L2_FIELD_NONE && + pix->field != V4L2_FIELD_ALTERNATE && + pix->field != V4L2_FIELD_SEQ_TB && + pix->field != V4L2_FIELD_SEQ_BT) pix->field = V4L2_FIELD_NONE; depth = fmt->vpdma_fmt[VPE_LUMA]->depth; @@ -1626,9 +1642,9 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, /* * For the actual image parameters, we need to consider the field - * height of the image for SEQ_TB buffers. + * height of the image for SEQ_XX buffers. */ - if (pix->field == V4L2_FIELD_SEQ_TB) + if (pix->field == V4L2_FIELD_SEQ_TB || pix->field == V4L2_FIELD_SEQ_BT) height = pix->height / 2; else height = pix->height; @@ -1734,11 +1750,13 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) q_data->flags |= Q_DATA_INTERLACED_ALTERNATE; else if (q_data->field == V4L2_FIELD_SEQ_TB) q_data->flags |= Q_DATA_INTERLACED_SEQ_TB; + else if (q_data->field == V4L2_FIELD_SEQ_BT) + q_data->flags |= Q_DATA_INTERLACED_SEQ_BT; else q_data->flags &= ~Q_IS_INTERLACED; - /* the crop height is halved for the case of SEQ_TB buffers */ - if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) + /* the crop height is halved for the case of SEQ_XX buffers */ + if (q_data->flags & Q_IS_SEQ_XX) q_data->c_rect.height /= 2; vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d", @@ -1811,10 +1829,10 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) } /* - * For SEQ_TB buffers, crop height should be less than the height of + * For SEQ_XX buffers, crop height should be less than the height of * the field height, not the buffer height */ - if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) + if (q_data->flags & Q_IS_SEQ_XX) height = q_data->height / 2; else height = q_data->height; @@ -2031,7 +2049,8 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) } else { if (vbuf->field != V4L2_FIELD_TOP && vbuf->field != V4L2_FIELD_BOTTOM && - vbuf->field != V4L2_FIELD_SEQ_TB) + vbuf->field != V4L2_FIELD_SEQ_TB && + vbuf->field != V4L2_FIELD_SEQ_BT) return -EINVAL; } } -- cgit v1.2.3 From b2bb3d822f2c9e27236ea32bc5ef18a9f22c8a80 Mon Sep 17 00:00:00 2001 From: Nikhil Devshatwar Date: Mon, 7 Oct 2019 12:09:54 -0300 Subject: media: ti-vpe: Add support for NV21 format In NV21 format, the chroma plane is written to memory such that the U and V components are swapped for NV12. Create a new entry in the VPDMA formats to describe the correct data types used in the data descriptors. Update all checks for NV12 and add NV21 there as well. Add support for V4L2_PIX_FMT_NV21 format for both capture and output streams. Signed-off-by: Nikhil Devshatwar Signed-off-by: Benoit Parrot Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpdma.c | 11 +++++++++-- drivers/media/platform/ti-vpe/vpdma.h | 1 + drivers/media/platform/ti-vpe/vpdma_priv.h | 1 + drivers/media/platform/ti-vpe/vpe.c | 29 +++++++++++++++++++++++------ 4 files changed, 34 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index 53d27cd6e10a..817d287c8138 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -56,6 +56,11 @@ const struct vpdma_data_format vpdma_yuv_fmts[] = { .data_type = DATA_TYPE_C420, .depth = 4, }, + [VPDMA_DATA_FMT_CB420] = { + .type = VPDMA_DATA_FMT_TYPE_YUV, + .data_type = DATA_TYPE_CB420, + .depth = 4, + }, [VPDMA_DATA_FMT_YCR422] = { .type = VPDMA_DATA_FMT_TYPE_YUV, .data_type = DATA_TYPE_YCR422, @@ -825,7 +830,8 @@ void vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width, channel = next_chan = raw_vpdma_chan; if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && - fmt->data_type == DATA_TYPE_C420) { + (fmt->data_type == DATA_TYPE_C420 || + fmt->data_type == DATA_TYPE_CB420)) { rect.height >>= 1; rect.top >>= 1; depth = 8; @@ -893,7 +899,8 @@ void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, channel = next_chan = chan_info[chan].num; if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && - fmt->data_type == DATA_TYPE_C420) { + (fmt->data_type == DATA_TYPE_C420 || + fmt->data_type == DATA_TYPE_CB420)) { rect.height >>= 1; rect.top >>= 1; depth = 8; diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h index 28bc94129348..bce17329c4c9 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -71,6 +71,7 @@ enum vpdma_yuv_formats { VPDMA_DATA_FMT_C444, VPDMA_DATA_FMT_C422, VPDMA_DATA_FMT_C420, + VPDMA_DATA_FMT_CB420, VPDMA_DATA_FMT_YCR422, VPDMA_DATA_FMT_YC444, VPDMA_DATA_FMT_CRY422, diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h index c488609bc162..d8ae3e1cd54d 100644 --- a/drivers/media/platform/ti-vpe/vpdma_priv.h +++ b/drivers/media/platform/ti-vpe/vpdma_priv.h @@ -92,6 +92,7 @@ #define DATA_TYPE_C444 0x4 #define DATA_TYPE_C422 0x5 #define DATA_TYPE_C420 0x6 +#define DATA_TYPE_CB420 0x16 #define DATA_TYPE_YC444 0x8 #define DATA_TYPE_YCB422 0x7 #define DATA_TYPE_YCR422 0x17 diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 5d0ec5f7ca25..f3ee9ff87927 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -248,6 +248,14 @@ static struct vpe_fmt vpe_formats[] = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420], }, }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, + .coplanar = 1, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], + &vpdma_yuv_fmts[VPDMA_DATA_FMT_CB420], + }, + }, { .fourcc = V4L2_PIX_FMT_YUYV, .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, @@ -686,7 +694,8 @@ static void set_cfg_modes(struct vpe_ctx *ctx) * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing. */ - if (fmt->fourcc == V4L2_PIX_FMT_NV12) + if (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21) cfg_mode = 0; write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); @@ -701,7 +710,8 @@ static void set_line_modes(struct vpe_ctx *ctx) struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; int line_mode = 1; - if (fmt->fourcc == V4L2_PIX_FMT_NV12) + if (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21) line_mode = 0; /* double lines to line buffer */ /* regs for now */ @@ -763,7 +773,8 @@ static void set_dst_registers(struct vpe_ctx *ctx) */ val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER; - if (fmt->fourcc != V4L2_PIX_FMT_NV12) + if (fmt->fourcc != V4L2_PIX_FMT_NV12 && + fmt->fourcc != V4L2_PIX_FMT_NV21) val |= VPE_DS_BYPASS; mmr_adb->out_fmt_reg[0] = val; @@ -1129,8 +1140,13 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) if (field) { int height = q_data->height / 2; - int bpp = fmt->fourcc == V4L2_PIX_FMT_NV12 ? - 1 : (vpdma_fmt->depth >> 3); + int bpp; + + if (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21) + bpp = 1; + else + bpp = vpdma_fmt->depth >> 3; if (plane) height /= 2; @@ -1148,7 +1164,8 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) frame_width = q_data->c_rect.width; frame_height = q_data->c_rect.height; - if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12) + if (p_data->vb_part && (fmt->fourcc == V4L2_PIX_FMT_NV12 || + fmt->fourcc == V4L2_PIX_FMT_NV21)) frame_height /= 2; vpdma_add_in_dtd(&ctx->desc_list, q_data->width, stride, -- cgit v1.2.3 From 661eaa3c4bca4b8d3233b60ca75fb5c5340cb970 Mon Sep 17 00:00:00 2001 From: Ram Prasad Date: Mon, 7 Oct 2019 12:09:55 -0300 Subject: media: ti-vpe: Set MAX height supported to 2048 pixels VPE's max height supported MAX_H is set to 1184 which is the padded height from VC1 decoder output. In case of 90, 270 degree rotated video processing, input to VPE will be 1080x1920, 720x1280 etc and MAX_H needs to be set correct value. Setting MAX_H to 2048 as worst case height. Signed-off-by: Ram Prasad Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index f3ee9ff87927..bbbf11174e16 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -52,7 +52,7 @@ #define MIN_W 32 #define MIN_H 32 #define MAX_W 2048 -#define MAX_H 1184 +#define MAX_H 2048 /* required alignments */ #define S_ALIGN 0 /* multiple of 1 */ -- cgit v1.2.3 From a37980ac5be29b83da67bf7d571c6bd9f90f8e45 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:56 -0300 Subject: media: ti-vpe: vpe: fix a v4l2-compliance failure causing a kernel panic v4l2-compliance fails with this message: warn: v4l2-test-formats.cpp(717): \ TRY_FMT cannot handle an invalid pixelformat. test VIDIOC_TRY_FMT: FAIL This causes the following kernel panic: Unable to handle kernel paging request at virtual address 56595561 pgd = ecd80e00 *pgd=00000000 Internal error: Oops: 205 [#1] PREEMPT SMP ARM ... CPU: 0 PID: 930 Comm: v4l2-compliance Not tainted \ 4.14.62-01715-gc8cd67f49a19 #1 Hardware name: Generic DRA72X (Flattened Device Tree) task: ece44d80 task.stack: ecc6e000 PC is at __vpe_try_fmt+0x18c/0x2a8 [ti_vpe] LR is at 0x8 Because the driver fails to properly check the 'num_planes' values for proper ranges it ends up accessing out of bound data causing the kernel panic. Since this driver only handle single or dual plane pixel format, make sure the provided value does not exceed 2 planes. Signed-off-by: Benoit Parrot Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index bbbf11174e16..1278d457f753 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1650,7 +1650,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, &pix->height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - if (!pix->num_planes) + if (!pix->num_planes || pix->num_planes > 2) pix->num_planes = fmt->coplanar ? 2 : 1; else if (pix->num_planes > 1 && !fmt->coplanar) pix->num_planes = 1; -- cgit v1.2.3 From 06bec72b250b2cb3ba96fa45c2b8e0fb83745517 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:57 -0300 Subject: media: ti-vpe: vpe: fix a v4l2-compliance warning about invalid pixel format v4l2-compliance warns with this message: warn: v4l2-test-formats.cpp(717): \ TRY_FMT cannot handle an invalid pixelformat. warn: v4l2-test-formats.cpp(718): \ This may or may not be a problem. For more information see: warn: v4l2-test-formats.cpp(719): \ http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html ... test VIDIOC_TRY_FMT: FAIL We need to make sure that the returns a valid pixel format in all instance. Based on the v4l2 framework convention drivers must return a valid pixel format when the requested pixel format is either invalid or not supported. Signed-off-by: Benoit Parrot Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 1278d457f753..d3f1ae8b72fa 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -351,20 +351,25 @@ enum { }; /* find our format description corresponding to the passed v4l2_format */ -static struct vpe_fmt *find_format(struct v4l2_format *f) +static struct vpe_fmt *__find_format(u32 fourcc) { struct vpe_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) { fmt = &vpe_formats[k]; - if (fmt->fourcc == f->fmt.pix.pixelformat) + if (fmt->fourcc == fourcc) return fmt; } return NULL; } +static struct vpe_fmt *find_format(struct v4l2_format *f) +{ + return __find_format(f->fmt.pix.pixelformat); +} + /* * there is one vpe_dev structure in the driver, it is shared by * all instances. @@ -1599,9 +1604,9 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, unsigned int stride = 0; if (!fmt || !(fmt->types & type)) { - vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n", + vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n", pix->pixelformat); - return -EINVAL; + fmt = __find_format(V4L2_PIX_FMT_YUYV); } if (pix->field != V4L2_FIELD_NONE && -- cgit v1.2.3 From e20b248051ca0f90d84b4d9378e4780bc31f16c6 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:58 -0300 Subject: media: ti-vpe: vpe: Make sure YUYV is set as default format v4l2-compliance fails with this message: fail: v4l2-test-formats.cpp(672): \ Video Capture Multiplanar: TRY_FMT(G_FMT) != G_FMT fail: v4l2-test-formats.cpp(672): \ Video Output Multiplanar: TRY_FMT(G_FMT) != G_FMT ... test VIDIOC_TRY_FMT: FAIL The default pixel format was setup as pointing to a specific offset in the vpe_formats table assuming it was pointing to the V4L2_PIX_FMT_YUYV entry. This became false after the addition on the NV21 format (see above commid-id) So instead of hard-coding an offset which might change over time we need to use a lookup helper instead so we know the default will always be what we intended. Signed-off-by: Benoit Parrot Fixes: 40cc823f7005 ("media: ti-vpe: Add support for NV21 format") Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index d3f1ae8b72fa..7aa83026fb6c 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2321,7 +2321,7 @@ static int vpe_open(struct file *file) v4l2_ctrl_handler_setup(hdl); s_q_data = &ctx->q_data[Q_DATA_SRC]; - s_q_data->fmt = &vpe_formats[2]; + s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV); s_q_data->width = 1920; s_q_data->height = 1080; s_q_data->nplanes = 1; -- cgit v1.2.3 From 0bac73adea4df8d34048b38f6ff24dc3e73e90b6 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:09:59 -0300 Subject: media: ti-vpe: vpe: fix a v4l2-compliance failure about invalid sizeimage v4l2-compliance fails with this message: fail: v4l2-test-formats.cpp(463): !pfmt.sizeimage fail: v4l2-test-formats.cpp(736): \ Video Capture Multiplanar is valid, \ but TRY_FMT failed to return a format test VIDIOC_TRY_FMT: FAIL This failure is causd by the driver failing to handle out range 'bytesperline' values from user space applications. VPDMA hardware is limited to 64k line stride (16 bytes aligned, so 65520 bytes). So make sure the provided or calculated 'bytesperline' is smaller than the maximum value. Signed-off-by: Benoit Parrot Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpdma.h | 1 + drivers/media/platform/ti-vpe/vpe.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h index bce17329c4c9..393fcbb3cb40 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -57,6 +57,7 @@ struct vpdma_data_format { * line stride of source and dest * buffers should be 16 byte aligned */ +#define VPDMA_MAX_STRIDE 65520 /* Max line stride 16 byte aligned */ #define VPDMA_DTD_DESC_SIZE 32 /* 8 words */ #define VPDMA_CFD_CTD_DESC_SIZE 16 /* 4 words */ diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 7aa83026fb6c..0a7cf9c820c6 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1694,6 +1694,10 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, if (stride > plane_fmt->bytesperline) plane_fmt->bytesperline = stride; + plane_fmt->bytesperline = clamp_t(u32, plane_fmt->bytesperline, + stride, + VPDMA_MAX_STRIDE); + plane_fmt->bytesperline = ALIGN(plane_fmt->bytesperline, VPDMA_STRIDE_ALIGN); -- cgit v1.2.3 From 2444846c0dbfa4ead21b621e4300ec32c90fbf38 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:00 -0300 Subject: media: ti-vpe: vpe: fix a v4l2-compliance failure about frame sequence number v4l2-compliance fails with this message: fail: v4l2-test-buffers.cpp(294): \ (int)g_sequence() < seq.last_seq + 1 fail: v4l2-test-buffers.cpp(740): \ buf.check(m2m_q, last_m2m_seq) fail: v4l2-test-buffers.cpp(974): \ captureBufs(node, q, m2m_q, frame_count, true) test MMAP: FAIL The driver is failing to update the source frame sequence number in the vb2 buffer object. Only the destination frame sequence was being updated. This is only a reporting issue if the user space app actually cares about the frame sequence number. But it is fixed nonetheless. Signed-off-by: Benoit Parrot Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 0a7cf9c820c6..8ab1c3241b74 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1440,6 +1440,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) d_vb->timecode = s_vb->timecode; d_vb->sequence = ctx->sequence; + s_vb->sequence = ctx->sequence; d_q_data = &ctx->q_data[Q_DATA_DST]; if (d_q_data->flags & Q_IS_INTERLACED) { -- cgit v1.2.3 From cf6acb73b050e98b5cc435fae0e8ae0157520410 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:01 -0300 Subject: media: ti-vpe: vpe: ensure buffers are cleaned up properly in abort cases v4l2-compliance fails with this message: fail: v4l2-test-buffers.cpp(691): ret == 0 fail: v4l2-test-buffers.cpp(974): captureBufs(node, q, m2m_q, frame_count, true) test MMAP: FAIL This caused the following Kernel Warning: WARNING: CPU: 0 PID: 961 at drivers/media/v4l2-core/videobuf2-core.c:1658 __vb2_queue_cancel+0x174/0x1d8 ... CPU: 0 PID: 961 Comm: v4l2-compliance Not tainted 4.14.62-01720-g20ecd717e87a #6 Hardware name: Generic DRA72X (Flattened Device Tree) Backtrace: [] (dump_backtrace) from [] (show_stack+0x18/0x1c) r7:00000009 r6:60070013 r5:00000000 r4:c1053824 [] (show_stack) from [] (dump_stack+0x90/0xa4) [] (dump_stack) from [] (__warn+0xec/0x104) r7:00000009 r6:c0c0ad50 r5:00000000 r4:00000000 [] (__warn) from [] (warn_slowpath_null+0x28/0x30) r9:00000008 r8:00000000 r7:eced4808 r6:edbc9bac r5:eced4844 r4:eced4808 [] (warn_slowpath_null) from [] (__vb2_queue_cancel+0x174/0x1d8) [] (__vb2_queue_cancel) from [] (vb2_core_queue_release+0x20/0x40) r10:ecc7bd70 r9:00000008 r8:00000000 r7:edb73010 r6:edbc9bac r5:eced4844 r4:eced4808 r3:00000004 [] (vb2_core_queue_release) from [] (vb2_queue_release+0x10/0x14) r5:edbc9810 r4:eced4800 [] (vb2_queue_release) from [] (v4l2_m2m_ctx_release+0x1c/0x30) [] (v4l2_m2m_ctx_release) from [] (vpe_release+0x74/0xb0 [ti_vpe]) r5:edbc9810 r4:ed67a400 [] (vpe_release [ti_vpe]) from [] (v4l2_release+0x3c/0x80) r7:edb73010 r6:ed176aa0 r5:edbc9868 r4:ed5119c0 [] (v4l2_release) from [] (__fput+0x8c/0x1dc) r5:ecc7bd70 r4:ed5119c0 [] (__fput) from [] (____fput+0x10/0x14) r10:00000000 r9:ed5119c0 r8:ece392d0 r7:c1059544 r6:ece38d80 r5:ece392b4 r4:00000000 [] (____fput) from [] (task_work_run+0x98/0xb8) [] (task_work_run) from [] (do_exit+0x170/0xa80) r9:ece351fc r8:00000000 r7:ecde3f58 r6:ffffe000 r5:ece351c0 r4:ece38d80 [] (do_exit) from [] (do_group_exit+0x48/0xc4) r7:000000f8 [] (do_group_exit) from [] (__wake_up_parent+0x0/0x28) r7:000000f8 r6:b6c6a798 r5:00000001 r4:00000001 [] (SyS_exit_group) from [] (ret_fast_syscall+0x0/0x4c) These warnings are caused by buffers which not properly cleaned up/release during an abort use case. In the abort cases the VPDMA desc buffers would still be mapped and the in-flight VB2 buffers would not be released properly causing a kernel warning from being generated by the videobuf2-core level. Signed-off-by: Benoit Parrot Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 8ab1c3241b74..ad9d8b559cad 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1427,9 +1427,6 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) /* the previous dst mv buffer becomes the next src mv buffer */ ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector; - if (ctx->aborting) - goto finished; - s_vb = ctx->src_vbs[0]; d_vb = ctx->dst_vb; @@ -1494,6 +1491,9 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) ctx->src_vbs[0] = NULL; ctx->dst_vb = NULL; + if (ctx->aborting) + goto finished; + ctx->bufs_completed++; if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) { device_run(ctx); @@ -2404,6 +2404,12 @@ static int vpe_release(struct file *file) mutex_lock(&dev->dev_mutex); free_mv_buffers(ctx); + + vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); + vpdma_free_desc_list(&ctx->desc_list); vpdma_free_desc_buf(&ctx->mmr_adb); -- cgit v1.2.3 From 63728b1cab288da65234e9c9fac4e294cc95cdd9 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:02 -0300 Subject: media: ti-vpe: vpdma: Use fixed type for address in descriptor Using dma_addr_t as the type to hold address inside of a fix sized descriptor used by the vpdma firmware is prone to fail when the expected width is 32 bits and suddenly when CONFIG_LPAE is enabled the data size is now 64 bits shifted the remaining members of the descriptor in memory which confuses the firmware. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpdma_priv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h index d8ae3e1cd54d..0bbee45338bd 100644 --- a/drivers/media/platform/ti-vpe/vpdma_priv.h +++ b/drivers/media/platform/ti-vpe/vpdma_priv.h @@ -166,11 +166,11 @@ struct vpdma_dtd { u32 xfer_length_height; u32 w1; }; - dma_addr_t start_addr; + u32 start_addr; u32 pkt_ctl; union { u32 frame_width_height; /* inbound */ - dma_addr_t desc_write_addr; /* outbound */ + u32 desc_write_addr; /* outbound */ }; union { u32 start_h_v; /* inbound */ -- cgit v1.2.3 From f3320447a22a37bc9b9d2769d14aa39860fd5698 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:03 -0300 Subject: media: ti-vpe: Set the DMA mask and coherent mask VPE uses VPDMA (built-in dma engine) to transfer data to and from the IP and memory. VPDMA expect 32 bits addresses. To make sure that is always the case set the DMA mask and coherent mask for the device. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index ad9d8b559cad..d7f8eb901475 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2517,6 +2517,13 @@ static int vpe_probe(struct platform_device *pdev) struct vpe_dev *dev; int ret, irq, func; + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, + "32-bit consistent DMA enable failed\n"); + return ret; + } + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; -- cgit v1.2.3 From 34efd808dbf4c824b5a354a58418e0d8e98df08f Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:04 -0300 Subject: media: ti-vpe: vpe: use standard struct instead of duplicating fields For each queue we need to maintain resolutions, pixel format, bytesperline, sizeimage, colorspace, etc. Instead of manually adding more entries in the vpe_q_data struct, it is better to just add a "struct v4l2_format" member and use that to store all needed information. Signed-off-by: Benoit Parrot [hverkuil@xs4all.nl: fix checkpatch warning] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 196 +++++++++++++++++++----------------- 1 file changed, 101 insertions(+), 95 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index d7f8eb901475..23ba6232d739 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -319,14 +319,9 @@ static struct vpe_fmt vpe_formats[] = { * there is one source queue and one destination queue for each m2m context. */ struct vpe_q_data { - unsigned int width; /* frame width */ - unsigned int height; /* frame height */ - unsigned int nplanes; /* Current number of planes */ - unsigned int bytesperline[VPE_MAX_PLANES]; /* bytes per line in memory */ - enum v4l2_colorspace colorspace; - enum v4l2_field field; /* supported field value */ + /* current v4l2 format info */ + struct v4l2_format format; unsigned int flags; - unsigned int sizeimage[VPE_MAX_PLANES]; /* image size in memory */ struct v4l2_rect c_rect; /* crop/compose rectangle */ struct vpe_fmt *fmt; /* format info */ }; @@ -761,11 +756,12 @@ static void set_src_registers(struct vpe_ctx *ctx) static void set_dst_registers(struct vpe_ctx *ctx) { struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - enum v4l2_colorspace clrspc = ctx->q_data[Q_DATA_DST].colorspace; + struct v4l2_pix_format_mplane *pix; struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; u32 val = 0; - if (clrspc == V4L2_COLORSPACE_SRGB) { + pix = &ctx->q_data[Q_DATA_DST].format.fmt.pix_mp; + if (pix->colorspace == V4L2_COLORSPACE_SRGB) { val |= VPE_RGB_OUT_SELECT; vpdma_set_bg_color(ctx->dev->vpdma, (struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff); @@ -868,11 +864,15 @@ static int set_srcdst_params(struct vpe_ctx *ctx) unsigned int src_h = s_q_data->c_rect.height; unsigned int dst_w = d_q_data->c_rect.width; unsigned int dst_h = d_q_data->c_rect.height; + struct v4l2_pix_format_mplane *spix; + struct v4l2_pix_format_mplane *dpix; size_t mv_buf_size; int ret; ctx->sequence = 0; ctx->field = V4L2_FIELD_TOP; + spix = &s_q_data->format.fmt.pix_mp; + dpix = &d_q_data->format.fmt.pix_mp; if ((s_q_data->flags & Q_IS_INTERLACED) && !(d_q_data->flags & Q_IS_INTERLACED)) { @@ -887,9 +887,9 @@ static int set_srcdst_params(struct vpe_ctx *ctx) * extra space will not be used by the de-interlacer, but will * ensure that vpdma operates correctly */ - bytes_per_line = ALIGN((s_q_data->width * mv->depth) >> 3, - VPDMA_STRIDE_ALIGN); - mv_buf_size = bytes_per_line * s_q_data->height; + bytes_per_line = ALIGN((spix->width * mv->depth) >> 3, + VPDMA_STRIDE_ALIGN); + mv_buf_size = bytes_per_line * spix->height; ctx->deinterlacing = true; src_h <<= 1; @@ -909,7 +909,7 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_dei_regs(ctx); csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], - s_q_data->colorspace, d_q_data->colorspace); + spix->colorspace, dpix->colorspace); sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); @@ -1023,6 +1023,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; int mv_buf_selector = !ctx->src_mv_buf_selector; + struct v4l2_pix_format_mplane *pix; dma_addr_t dma_addr; u32 flags = 0; u32 offset = 0; @@ -1032,21 +1033,23 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; dma_addr = ctx->mv_buf_dma[mv_buf_selector]; q_data = &ctx->q_data[Q_DATA_SRC]; - stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3, + pix = &q_data->format.fmt.pix_mp; + stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, VPDMA_STRIDE_ALIGN); } else { /* to incorporate interleaved formats */ int plane = fmt->coplanar ? p_data->vb_part : 0; + pix = &q_data->format.fmt.pix_mp; vpdma_fmt = fmt->vpdma_fmt[plane]; /* * If we are using a single plane buffer and * we need to set a separate vpdma chroma channel. */ - if (q_data->nplanes == 1 && plane) { + if (pix->num_planes == 1 && plane) { dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); /* Compute required offset */ - offset = q_data->bytesperline[0] * q_data->height; + offset = pix->plane_fmt[0].bytesperline * pix->height; } else { dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); /* Use address as is, no offset */ @@ -1060,7 +1063,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) } /* Apply the offset */ dma_addr += offset; - stride = q_data->bytesperline[VPE_LUMA]; + stride = pix->plane_fmt[VPE_LUMA].bytesperline; } if (q_data->flags & Q_DATA_FRAME_1D) @@ -1071,7 +1074,7 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) vpdma_set_max_size(ctx->dev->vpdma, VPDMA_MAX_SIZE1, MAX_W, MAX_H); - vpdma_add_out_dtd(&ctx->desc_list, q_data->width, + vpdma_add_out_dtd(&ctx->desc_list, pix->width, stride, &q_data->c_rect, vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1, MAX_OUT_HEIGHT_REG1, p_data->channel, flags); @@ -1084,6 +1087,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) struct vb2_buffer *vb = &ctx->src_vbs[p_data->vb_index]->vb2_buf; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpe_fmt *fmt = q_data->fmt; + struct v4l2_pix_format_mplane *pix; const struct vpdma_data_format *vpdma_fmt; int mv_buf_selector = ctx->src_mv_buf_selector; int field = vbuf->field == V4L2_FIELD_BOTTOM; @@ -1093,10 +1097,11 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) u32 offset = 0; u32 stride; + pix = &q_data->format.fmt.pix_mp; if (port == VPE_PORT_MV_IN) { vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; dma_addr = ctx->mv_buf_dma[mv_buf_selector]; - stride = ALIGN((q_data->width * vpdma_fmt->depth) >> 3, + stride = ALIGN((pix->width * vpdma_fmt->depth) >> 3, VPDMA_STRIDE_ALIGN); } else { /* to incorporate interleaved formats */ @@ -1107,10 +1112,10 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) * If we are using a single plane buffer and * we need to set a separate vpdma chroma channel. */ - if (q_data->nplanes == 1 && plane) { + if (pix->num_planes == 1 && plane) { dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); /* Compute required offset */ - offset = q_data->bytesperline[0] * q_data->height; + offset = pix->plane_fmt[0].bytesperline * pix->height; } else { dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); /* Use address as is, no offset */ @@ -1124,7 +1129,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) } /* Apply the offset */ dma_addr += offset; - stride = q_data->bytesperline[VPE_LUMA]; + stride = pix->plane_fmt[VPE_LUMA].bytesperline; /* * field used in VPDMA desc = 0 (top) / 1 (bottom) @@ -1144,7 +1149,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) field = (field + p_data->vb_index + ctx->sequence) % 2; if (field) { - int height = q_data->height / 2; + int height = pix->height / 2; int bpp; if (fmt->fourcc == V4L2_PIX_FMT_NV12 || @@ -1156,7 +1161,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) if (plane) height /= 2; - dma_addr += q_data->width * height * bpp; + dma_addr += pix->width * height * bpp; } } } @@ -1173,7 +1178,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) fmt->fourcc == V4L2_PIX_FMT_NV21)) frame_height /= 2; - vpdma_add_in_dtd(&ctx->desc_list, q_data->width, stride, + vpdma_add_in_dtd(&ctx->desc_list, pix->width, stride, &q_data->c_rect, vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width, frame_height, 0, 0); @@ -1210,6 +1215,9 @@ static void device_run(void *priv) struct sc_data *sc = ctx->dev->sc; struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + struct v4l2_pix_format_mplane *dpix; + + dpix = &d_q_data->format.fmt.pix_mp; if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX && ctx->sequence % 2 == 0) { @@ -1282,7 +1290,7 @@ static void device_run(void *priv) if (ctx->deinterlacing) add_out_dtd(ctx, VPE_PORT_MV_OUT); - if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { + if (dpix->colorspace == V4L2_COLORSPACE_SRGB) { add_out_dtd(ctx, VPE_PORT_RGB_OUT); } else { add_out_dtd(ctx, VPE_PORT_LUMA_OUT); @@ -1324,7 +1332,7 @@ static void device_run(void *priv) } /* sync on channel control descriptors for output ports */ - if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { + if (dpix->colorspace == V4L2_COLORSPACE_SRGB) { vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_RGB_OUT); } else { @@ -1559,7 +1567,6 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct vpe_ctx *ctx = file->private_data; struct vb2_queue *vq; struct vpe_q_data *q_data; - int i; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -1569,27 +1576,17 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) if (!q_data) return -EINVAL; - pix->width = q_data->width; - pix->height = q_data->height; - pix->pixelformat = q_data->fmt->fourcc; - pix->field = q_data->field; + *f = q_data->format; - if (V4L2_TYPE_IS_OUTPUT(f->type)) { - pix->colorspace = q_data->colorspace; - } else { + if (!V4L2_TYPE_IS_OUTPUT(f->type)) { struct vpe_q_data *s_q_data; + struct v4l2_pix_format_mplane *spix; /* get colorspace from the source queue */ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + spix = &s_q_data->format.fmt.pix_mp; - pix->colorspace = s_q_data->colorspace; - } - - pix->num_planes = q_data->nplanes; - - for (i = 0; i < pix->num_planes; i++) { - pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; - pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; + pix->colorspace = spix->colorspace; } return 0; @@ -1736,10 +1733,9 @@ static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f) static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct v4l2_plane_pix_format *plane_fmt; + struct v4l2_pix_format_mplane *qpix; struct vpe_q_data *q_data; struct vb2_queue *vq; - int i; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -1754,30 +1750,20 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) if (!q_data) return -EINVAL; + qpix = &q_data->format.fmt.pix_mp; q_data->fmt = find_format(f); - q_data->width = pix->width; - q_data->height = pix->height; - q_data->colorspace = pix->colorspace; - q_data->field = pix->field; - q_data->nplanes = pix->num_planes; - - for (i = 0; i < pix->num_planes; i++) { - plane_fmt = &pix->plane_fmt[i]; - - q_data->bytesperline[i] = plane_fmt->bytesperline; - q_data->sizeimage[i] = plane_fmt->sizeimage; - } + q_data->format = *f; q_data->c_rect.left = 0; q_data->c_rect.top = 0; - q_data->c_rect.width = q_data->width; - q_data->c_rect.height = q_data->height; + q_data->c_rect.width = pix->width; + q_data->c_rect.height = pix->height; - if (q_data->field == V4L2_FIELD_ALTERNATE) + if (qpix->field == V4L2_FIELD_ALTERNATE) q_data->flags |= Q_DATA_INTERLACED_ALTERNATE; - else if (q_data->field == V4L2_FIELD_SEQ_TB) + else if (qpix->field == V4L2_FIELD_SEQ_TB) q_data->flags |= Q_DATA_INTERLACED_SEQ_TB; - else if (q_data->field == V4L2_FIELD_SEQ_BT) + else if (qpix->field == V4L2_FIELD_SEQ_BT) q_data->flags |= Q_DATA_INTERLACED_SEQ_BT; else q_data->flags &= ~Q_IS_INTERLACED; @@ -1787,11 +1773,11 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) q_data->c_rect.height /= 2; vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d", - f->type, q_data->width, q_data->height, q_data->fmt->fourcc, - q_data->bytesperline[VPE_LUMA]); - if (q_data->nplanes == 2) + f->type, pix->width, pix->height, pix->pixelformat, + pix->plane_fmt[0].bytesperline); + if (pix->num_planes == 2) vpe_dbg(ctx->dev, " bpl_uv %d\n", - q_data->bytesperline[VPE_CHROMA]); + pix->plane_fmt[1].bytesperline); return 0; } @@ -1820,6 +1806,7 @@ static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) { struct vpe_q_data *q_data; + struct v4l2_pix_format_mplane *pix; int height; if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && @@ -1830,6 +1817,8 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) if (!q_data) return -EINVAL; + pix = &q_data->format.fmt.pix_mp; + switch (s->target) { case V4L2_SEL_TGT_COMPOSE: /* @@ -1860,23 +1849,23 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) * the field height, not the buffer height */ if (q_data->flags & Q_IS_SEQ_XX) - height = q_data->height / 2; + height = pix->height / 2; else - height = q_data->height; + height = pix->height; if (s->r.top < 0 || s->r.left < 0) { vpe_err(ctx->dev, "negative values for top and left\n"); s->r.top = s->r.left = 0; } - v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1, + v4l_bound_align_image(&s->r.width, MIN_W, pix->width, 1, &s->r.height, MIN_H, height, H_ALIGN, S_ALIGN); /* adjust left/top if cropping rectangle is out of bounds */ - if (s->r.left + s->r.width > q_data->width) - s->r.left = q_data->width - s->r.width; - if (s->r.top + s->r.height > q_data->height) - s->r.top = q_data->height - s->r.height; + if (s->r.left + s->r.width > pix->width) + s->r.left = pix->width - s->r.width; + if (s->r.top + s->r.height > pix->height) + s->r.top = pix->height - s->r.height; return 0; } @@ -1886,6 +1875,7 @@ static int vpe_g_selection(struct file *file, void *fh, { struct vpe_ctx *ctx = file->private_data; struct vpe_q_data *q_data; + struct v4l2_pix_format_mplane *pix; bool use_c_rect = false; if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && @@ -1896,6 +1886,8 @@ static int vpe_g_selection(struct file *file, void *fh, if (!q_data) return -EINVAL; + pix = &q_data->format.fmt.pix_mp; + switch (s->target) { case V4L2_SEL_TGT_COMPOSE_DEFAULT: case V4L2_SEL_TGT_COMPOSE_BOUNDS: @@ -1934,8 +1926,8 @@ static int vpe_g_selection(struct file *file, void *fh, */ s->r.left = 0; s->r.top = 0; - s->r.width = q_data->width; - s->r.height = q_data->height; + s->r.width = pix->width; + s->r.height = pix->height; } return 0; @@ -2038,19 +2030,21 @@ static int vpe_queue_setup(struct vb2_queue *vq, int i; struct vpe_ctx *ctx = vb2_get_drv_priv(vq); struct vpe_q_data *q_data; + struct v4l2_pix_format_mplane *pix; q_data = get_q_data(ctx, vq->type); if (!q_data) return -EINVAL; - *nplanes = q_data->nplanes; + pix = &q_data->format.fmt.pix_mp; + *nplanes = pix->num_planes; for (i = 0; i < *nplanes; i++) - sizes[i] = q_data->sizeimage[i]; + sizes[i] = pix->plane_fmt[i].sizeimage; vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers, sizes[VPE_LUMA]); - if (q_data->nplanes == 2) + if (*nplanes == 2) vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]); return 0; @@ -2061,14 +2055,16 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vpe_q_data *q_data; - int i, num_planes; + struct v4l2_pix_format_mplane *pix; + int i; vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); q_data = get_q_data(ctx, vb->vb2_queue->type); if (!q_data) return -EINVAL; - num_planes = q_data->nplanes; + + pix = &q_data->format.fmt.pix_mp; if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (!(q_data->flags & Q_IS_INTERLACED)) { @@ -2082,18 +2078,18 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) } } - for (i = 0; i < num_planes; i++) { - if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { + for (i = 0; i < pix->num_planes; i++) { + if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) { vpe_err(ctx->dev, "data will not fit into plane (%lu < %lu)\n", vb2_plane_size(vb, i), - (long) q_data->sizeimage[i]); + (long)pix->plane_fmt[i].sizeimage); return -EINVAL; } } - for (i = 0; i < num_planes; i++) - vb2_set_plane_payload(vb, i, q_data->sizeimage[i]); + for (i = 0; i < pix->num_planes; i++) + vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage); return 0; } @@ -2278,6 +2274,7 @@ static int vpe_open(struct file *file) struct vpe_q_data *s_q_data; struct v4l2_ctrl_handler *hdl; struct vpe_ctx *ctx; + struct v4l2_pix_format_mplane *pix; int ret; vpe_dbg(dev, "vpe_open\n"); @@ -2326,23 +2323,32 @@ static int vpe_open(struct file *file) v4l2_ctrl_handler_setup(hdl); s_q_data = &ctx->q_data[Q_DATA_SRC]; + pix = &s_q_data->format.fmt.pix_mp; s_q_data->fmt = __find_format(V4L2_PIX_FMT_YUYV); - s_q_data->width = 1920; - s_q_data->height = 1080; - s_q_data->nplanes = 1; - s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width * + pix->pixelformat = s_q_data->fmt->fourcc; + s_q_data->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + pix->width = 1920; + pix->height = 1080; + pix->num_planes = 1; + pix->plane_fmt[VPE_LUMA].bytesperline = (pix->width * s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; - s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] * - s_q_data->height); - s_q_data->colorspace = V4L2_COLORSPACE_REC709; - s_q_data->field = V4L2_FIELD_NONE; + pix->plane_fmt[VPE_LUMA].sizeimage = + pix->plane_fmt[VPE_LUMA].bytesperline * + pix->height; + pix->colorspace = V4L2_COLORSPACE_REC709; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + pix->field = V4L2_FIELD_NONE; s_q_data->c_rect.left = 0; s_q_data->c_rect.top = 0; - s_q_data->c_rect.width = s_q_data->width; - s_q_data->c_rect.height = s_q_data->height; + s_q_data->c_rect.width = pix->width; + s_q_data->c_rect.height = pix->height; s_q_data->flags = 0; ctx->q_data[Q_DATA_DST] = *s_q_data; + ctx->q_data[Q_DATA_DST].format.type = + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; set_dei_shadow_registers(ctx); set_src_registers(ctx); -- cgit v1.2.3 From 98ca241d239a51cdd67321f3a73711f42e80eea5 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:05 -0300 Subject: media: ti-vpe: vpe: fix v4l2_compliance issue related to xfer_func All 4 of the "colorspace" components were not originally handled. Causing issue related to xfer_func not being initialized properly. This was found with v4l2-compliance test. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 23ba6232d739..a50511f1e1c3 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1582,11 +1582,14 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct vpe_q_data *s_q_data; struct v4l2_pix_format_mplane *spix; - /* get colorspace from the source queue */ + /* get colorimetry from the source queue */ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); spix = &s_q_data->format.fmt.pix_mp; pix->colorspace = spix->colorspace; + pix->xfer_func = spix->xfer_func; + pix->ycbcr_enc = spix->ycbcr_enc; + pix->quantization = spix->quantization; } return 0; -- cgit v1.2.3 From 9152dc9ec940136ca915d3caeab13a7b97bd15e0 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:06 -0300 Subject: media: ti-vpe: csc: rgb-to-yuv HD full range coeff are wrong The RGB to YUV HD full range coefficients did not match the TRM values and appeared to be a cut-n-paste from the YUV to RGB section. Replace the entries with the values from the TRM. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/csc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c index eda2a5985da7..f0c45d187b5f 100644 --- a/drivers/media/platform/ti-vpe/csc.c +++ b/drivers/media/platform/ti-vpe/csc.c @@ -81,8 +81,8 @@ static struct colorspace_coeffs colorspace_coeffs[4] = { }, { /* HDTV */ - 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, - 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, + 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2, + 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200, }, }, }; -- cgit v1.2.3 From d5a897c8428b38053df4b427a4277b1a0722bfa0 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:07 -0300 Subject: media: v4l2-common: add pixel encoding support It is often useful to figure out if a pixel_format is either YUV or RGB especially for driver who can perform the pixel encoding conversion. Instead of having each driver implement its own "is_this_yuv/rgb" function based on a restricted set of pixel value, it is better to do this in centralized manner. We therefore add a pixel_enc member to the v4l2_format_info structure to quickly identify the related pixel encoding. And add helper functions to check pixel encoding. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 126 +++++++++++++++++----------------- 1 file changed, 63 insertions(+), 63 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 62f7aa92ac29..09a3915b98a3 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -236,77 +236,77 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { static const struct v4l2_format_info formats[] = { /* RGB formats */ - { .format = V4L2_PIX_FMT_BGR24, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB24, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV24, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XBGR32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRX32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XRGB32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBX32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_GREY, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, /* YUV packed formats */ - { .format = V4L2_PIX_FMT_YUYV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_YVYU, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_UYVY, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_VYUY, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_UYVY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, /* YUV planar formats */ - { .format = V4L2_PIX_FMT_NV12, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_NV21, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_NV16, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_NV61, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_NV24, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_NV42, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - - { .format = V4L2_PIX_FMT_YUV410, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, - { .format = V4L2_PIX_FMT_YVU410, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, - { .format = V4L2_PIX_FMT_YUV411P, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_YUV420, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_YVU420, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_YUV422P, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV21, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV61, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + + { .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, + { .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, + { .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YVU420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 }, /* YUV planar formats, non contiguous variant */ - { .format = V4L2_PIX_FMT_YUV420M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_YVU420M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_YUV422M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_YVU422M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_YUV444M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_YVU444M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 }, - - { .format = V4L2_PIX_FMT_NV12M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_NV21M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, - { .format = V4L2_PIX_FMT_NV16M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_NV61M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 }, + + { .format = V4L2_PIX_FMT_NV12M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV21M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV16M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV61M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, /* Bayer RGB formats */ - { .format = V4L2_PIX_FMT_SBGGR8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGBRG8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGRBG8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SRGGB8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SBGGR10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGBRG10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGRBG10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SRGGB10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SBGGR10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGBRG10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGRBG10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SRGGB10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SBGGR10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGBRG10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGRBG10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SRGGB10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SBGGR12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGBRG12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SGRBG12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_SRGGB12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SBGGR12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGBRG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SGRBG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_SRGGB12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, }; unsigned int i; -- cgit v1.2.3 From b373f84d77e1c409aacb4ff5bb5726c45fc8b166 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:08 -0300 Subject: media: v4l2-common: add RGB565 and RGB55 to v4l2_format_info Add RGB565 and RGB555 to the v4l2_format_info table. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 09a3915b98a3..d0e5ebc736f9 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -251,6 +251,8 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 }, /* YUV packed formats */ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 }, -- cgit v1.2.3 From 3ff3a712a9eabb3d7bf52c263dd1ece054345df4 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Mon, 7 Oct 2019 12:10:09 -0300 Subject: media: ti-vpe: vpe: don't rely on colorspace member for conversion Up to now VPE was relying on the colorspace value of struct v4l2_format as an indication to perform color space conversion from YUV to RGB or not. Instead we should used the source/destination fourcc codes as a more reliable indication to perform color space conversion or not. To do so, we rework the csc module to use "struct v4l2_format *" as parameters, and reorganize the coefficients tables in a more logical way. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/csc.c | 254 ++++++++++++++++++++++++------------ drivers/media/platform/ti-vpe/csc.h | 4 +- drivers/media/platform/ti-vpe/vpe.c | 25 ++-- 3 files changed, 184 insertions(+), 99 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c index f0c45d187b5f..bd923bee4a31 100644 --- a/drivers/media/platform/ti-vpe/csc.c +++ b/drivers/media/platform/ti-vpe/csc.c @@ -15,76 +15,96 @@ #include #include #include +#include #include "csc.h" /* - * 16 coefficients in the order: + * 12 coefficients in the order: * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2 - * (we may need to pass non-default values from user space later on, we might - * need to make the coefficient struct more easy to populate) */ -struct colorspace_coeffs { - u16 sd[12]; - u16 hd[12]; +struct quantization { + u16 coeff[12]; }; -/* VIDEO_RANGE: limited range, GRAPHICS_RANGE: full range */ -#define CSC_COEFFS_VIDEO_RANGE_Y2R 0 -#define CSC_COEFFS_GRAPHICS_RANGE_Y2R 1 -#define CSC_COEFFS_VIDEO_RANGE_R2Y 2 -#define CSC_COEFFS_GRAPHICS_RANGE_R2Y 3 +struct colorspace { + struct quantization limited; + struct quantization full; +}; + +struct encoding_direction { + struct colorspace r601; + struct colorspace r709; +}; + +struct csc_coeffs { + struct encoding_direction y2r; + struct encoding_direction r2y; +}; /* default colorspace coefficients */ -static struct colorspace_coeffs colorspace_coeffs[4] = { - [CSC_COEFFS_VIDEO_RANGE_Y2R] = { - { - /* SDTV */ - 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35, - 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88, +static struct csc_coeffs csc_coeffs = { + .y2r = { + .r601 = { + .limited = { + { /* SDTV */ + 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35, + 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88, + } + }, + .full = { + { /* SDTV */ + 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF, + 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC, + } + }, }, - { - /* HDTV */ - 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B, - 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60, + .r709 = { + .limited = { + { /* HDTV */ + 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B, + 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60, + } + }, + .full = { + { /* HDTV */ + 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, + 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, + } + }, }, }, - [CSC_COEFFS_GRAPHICS_RANGE_Y2R] = { - { - /* SDTV */ - 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF, - 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC, + .r2y = { + .r601 = { + .limited = { + { /* SDTV */ + 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B, + 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200, + } + }, + .full = { + { /* SDTV */ + 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2, + 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200, + } + }, }, - { - /* HDTV */ - 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, - 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, - }, - }, - [CSC_COEFFS_VIDEO_RANGE_R2Y] = { - { - /* SDTV */ - 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B, - 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200, - }, - { - /* HDTV */ - 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C, - 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200, - }, - }, - [CSC_COEFFS_GRAPHICS_RANGE_R2Y] = { - { - /* SDTV */ - 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2, - 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200, - }, - { - /* HDTV */ - 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2, - 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200, + .r709 = { + .limited = { + { /* HDTV */ + 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C, + 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200, + } + }, + .full = { + { /* HDTV */ + 0x00bb, 0x0275, 0x003f, 0x1f99, 0x1ea5, 0x01c2, + 0x01c2, 0x1e67, 0x1fd7, 0x0040, 0x0200, 0x0200, + } + }, }, }, + }; void csc_dump_regs(struct csc_data *csc) @@ -117,46 +137,114 @@ EXPORT_SYMBOL(csc_set_coeff_bypass); * set the color space converter coefficient shadow register values */ void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, - enum v4l2_colorspace src_colorspace, - enum v4l2_colorspace dst_colorspace) + struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt) { u32 *csc_reg5 = csc_reg0 + 5; u32 *shadow_csc = csc_reg0; - struct colorspace_coeffs *sd_hd_coeffs; u16 *coeff, *end_coeff; - enum v4l2_colorspace yuv_colorspace; - int sel = 0; - - /* - * support only graphics data range(full range) for now, a control ioctl - * would be nice here - */ - /* Y2R */ - if (dst_colorspace == V4L2_COLORSPACE_SRGB && - (src_colorspace == V4L2_COLORSPACE_SMPTE170M || - src_colorspace == V4L2_COLORSPACE_REC709)) { + const struct v4l2_pix_format *pix; + const struct v4l2_pix_format_mplane *mp; + const struct v4l2_format_info *src_finfo, *dst_finfo; + enum v4l2_ycbcr_encoding src_ycbcr_enc, dst_ycbcr_enc; + enum v4l2_quantization src_quantization, dst_quantization; + u32 src_pixelformat, dst_pixelformat; + + switch (src_fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &src_fmt->fmt.pix; + src_pixelformat = pix->pixelformat; + src_ycbcr_enc = pix->ycbcr_enc; + src_quantization = pix->quantization; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + default: + mp = &src_fmt->fmt.pix_mp; + src_pixelformat = mp->pixelformat; + src_ycbcr_enc = mp->ycbcr_enc; + src_quantization = mp->quantization; + break; + } + + switch (dst_fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &dst_fmt->fmt.pix; + dst_pixelformat = pix->pixelformat; + dst_ycbcr_enc = pix->ycbcr_enc; + dst_quantization = pix->quantization; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + default: + mp = &dst_fmt->fmt.pix_mp; + dst_pixelformat = mp->pixelformat; + dst_ycbcr_enc = mp->ycbcr_enc; + dst_quantization = mp->quantization; + break; + } + + src_finfo = v4l2_format_info(src_pixelformat); + dst_finfo = v4l2_format_info(dst_pixelformat); + + if (v4l2_is_format_yuv(src_finfo) && + v4l2_is_format_rgb(dst_finfo)) { /* Y2R */ - sel = 1; - yuv_colorspace = src_colorspace; - } else if ((dst_colorspace == V4L2_COLORSPACE_SMPTE170M || - dst_colorspace == V4L2_COLORSPACE_REC709) && - src_colorspace == V4L2_COLORSPACE_SRGB) { + + /* + * These are not the standard default values but are + * set this way for historical compatibility + */ + if (src_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + src_ycbcr_enc = V4L2_YCBCR_ENC_601; + + if (src_quantization == V4L2_QUANTIZATION_DEFAULT) + src_quantization = V4L2_QUANTIZATION_FULL_RANGE; + + if (src_ycbcr_enc == V4L2_YCBCR_ENC_601) { + if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.y2r.r601.full.coeff; + else + coeff = csc_coeffs.y2r.r601.limited.coeff; + } else if (src_ycbcr_enc == V4L2_YCBCR_ENC_709) { + if (src_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.y2r.r709.full.coeff; + else + coeff = csc_coeffs.y2r.r709.limited.coeff; + } else { + /* Should never reach this, but it keeps gcc happy */ + coeff = csc_coeffs.y2r.r601.full.coeff; + } + } else if (v4l2_is_format_rgb(src_finfo) && + v4l2_is_format_yuv(dst_finfo)) { /* R2Y */ - sel = 3; - yuv_colorspace = dst_colorspace; + + /* + * These are not the standard default values but are + * set this way for historical compatibility + */ + if (dst_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) + dst_ycbcr_enc = V4L2_YCBCR_ENC_601; + + if (dst_quantization == V4L2_QUANTIZATION_DEFAULT) + dst_quantization = V4L2_QUANTIZATION_FULL_RANGE; + + if (dst_ycbcr_enc == V4L2_YCBCR_ENC_601) { + if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.r2y.r601.full.coeff; + else + coeff = csc_coeffs.r2y.r601.limited.coeff; + } else if (dst_ycbcr_enc == V4L2_YCBCR_ENC_709) { + if (dst_quantization == V4L2_QUANTIZATION_FULL_RANGE) + coeff = csc_coeffs.r2y.r709.full.coeff; + else + coeff = csc_coeffs.r2y.r709.limited.coeff; + } else { + /* Should never reach this, but it keeps gcc happy */ + coeff = csc_coeffs.y2r.r601.full.coeff; + } } else { *csc_reg5 |= CSC_BYPASS; return; } - sd_hd_coeffs = &colorspace_coeffs[sel]; - - /* select between SD or HD coefficients */ - if (yuv_colorspace == V4L2_COLORSPACE_SMPTE170M) - coeff = sd_hd_coeffs->sd; - else - coeff = sd_hd_coeffs->hd; - end_coeff = coeff + 12; for (; coeff < end_coeff; coeff += 2) diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti-vpe/csc.h index de9a58af2ca8..af2e86bccf57 100644 --- a/drivers/media/platform/ti-vpe/csc.h +++ b/drivers/media/platform/ti-vpe/csc.h @@ -58,8 +58,8 @@ struct csc_data { void csc_dump_regs(struct csc_data *csc); void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5); void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, - enum v4l2_colorspace src_colorspace, - enum v4l2_colorspace dst_colorspace); + struct v4l2_format *src_fmt, struct v4l2_format *dst_fmt); + struct csc_data *csc_create(struct platform_device *pdev, const char *res_name); #endif diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index a50511f1e1c3..b54f637633a7 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -756,12 +756,12 @@ static void set_src_registers(struct vpe_ctx *ctx) static void set_dst_registers(struct vpe_ctx *ctx) { struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - struct v4l2_pix_format_mplane *pix; struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; + const struct v4l2_format_info *finfo; u32 val = 0; - pix = &ctx->q_data[Q_DATA_DST].format.fmt.pix_mp; - if (pix->colorspace == V4L2_COLORSPACE_SRGB) { + finfo = v4l2_format_info(fmt->fourcc); + if (v4l2_is_format_rgb(finfo)) { val |= VPE_RGB_OUT_SELECT; vpdma_set_bg_color(ctx->dev->vpdma, (struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff); @@ -865,14 +865,12 @@ static int set_srcdst_params(struct vpe_ctx *ctx) unsigned int dst_w = d_q_data->c_rect.width; unsigned int dst_h = d_q_data->c_rect.height; struct v4l2_pix_format_mplane *spix; - struct v4l2_pix_format_mplane *dpix; size_t mv_buf_size; int ret; ctx->sequence = 0; ctx->field = V4L2_FIELD_TOP; spix = &s_q_data->format.fmt.pix_mp; - dpix = &d_q_data->format.fmt.pix_mp; if ((s_q_data->flags & Q_IS_INTERLACED) && !(d_q_data->flags & Q_IS_INTERLACED)) { @@ -909,7 +907,7 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_dei_regs(ctx); csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], - spix->colorspace, dpix->colorspace); + &s_q_data->format, &d_q_data->format); sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); @@ -1215,9 +1213,9 @@ static void device_run(void *priv) struct sc_data *sc = ctx->dev->sc; struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; - struct v4l2_pix_format_mplane *dpix; + const struct v4l2_format_info *d_finfo; - dpix = &d_q_data->format.fmt.pix_mp; + d_finfo = v4l2_format_info(d_q_data->fmt->fourcc); if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX && ctx->sequence % 2 == 0) { @@ -1290,7 +1288,7 @@ static void device_run(void *priv) if (ctx->deinterlacing) add_out_dtd(ctx, VPE_PORT_MV_OUT); - if (dpix->colorspace == V4L2_COLORSPACE_SRGB) { + if (v4l2_is_format_rgb(d_finfo)) { add_out_dtd(ctx, VPE_PORT_RGB_OUT); } else { add_out_dtd(ctx, VPE_PORT_LUMA_OUT); @@ -1332,7 +1330,7 @@ static void device_run(void *priv) } /* sync on channel control descriptors for output ports */ - if (dpix->colorspace == V4L2_COLORSPACE_SRGB) { + if (v4l2_is_format_rgb(d_finfo)) { vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_RGB_OUT); } else { @@ -1603,6 +1601,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, unsigned int w_align; int i, depth, depth_bytes, height; unsigned int stride = 0; + const struct v4l2_format_info *finfo; if (!fmt || !(fmt->types & type)) { vpe_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n", @@ -1662,6 +1661,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, pix->num_planes = 1; pix->pixelformat = fmt->fourcc; + finfo = v4l2_format_info(fmt->fourcc); /* * For the actual image parameters, we need to consider the field @@ -1673,10 +1673,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, height = pix->height; if (!pix->colorspace) { - if (fmt->fourcc == V4L2_PIX_FMT_RGB24 || - fmt->fourcc == V4L2_PIX_FMT_BGR24 || - fmt->fourcc == V4L2_PIX_FMT_RGB32 || - fmt->fourcc == V4L2_PIX_FMT_BGR32) { + if (v4l2_is_format_rgb(finfo)) { pix->colorspace = V4L2_COLORSPACE_SRGB; } else { if (height > 1280) /* HD */ -- cgit v1.2.3 From 14494583336880640654300c76d0f5df3360d85f Mon Sep 17 00:00:00 2001 From: Thomas Voegtle Date: Wed, 2 Oct 2019 14:26:03 -0300 Subject: media: dvbsky: add support for eyeTV Geniatech T2 lite Adds USB ID for the eyeTV Geniatech T2 lite to the dvbsky driver. This is a Geniatech T230C based stick without IR and a different USB ID. Signed-off-by: Thomas Voegtle Tested-by: Jan Pieter van Woerkom Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvbsky.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index b562ee090c85..199ba6a8201f 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -792,6 +792,9 @@ static const struct usb_device_id dvbsky_id_table[] = { { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C, &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, + { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C_LITE, + &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C Lite", + NULL) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2, &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C v2", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, -- cgit v1.2.3 From d587cdb2a5f57053ef182f64894ed3ba9e1559dc Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 7 Oct 2019 07:36:45 -0300 Subject: media: imon_raw: simplify loop The code for pulse and space is the same so remove duplication. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon_raw.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index d4aedcf76418..aae0a3cc9479 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -57,32 +57,18 @@ static void imon_ir_data(struct imon *imon) * fls will tell us the highest bit set plus 1 (or 0 if no * bits are set). */ + rawir.pulse = !rawir.pulse; bit = fls64(data & (BIT_ULL(offset) - 1)); if (bit < offset) { - dev_dbg(imon->dev, "pulse: %d bits", offset - bit); - rawir.pulse = true; + dev_dbg(imon->dev, "%s: %d bits", + rawir.pulse ? "pulse" : "space", offset - bit); rawir.duration = (offset - bit) * BIT_DURATION; ir_raw_event_store_with_filter(imon->rcdev, &rawir); - if (bit == 0) - break; - offset = bit; } - /* - * Find highest clear bit which is less than offset. - * - * Just invert the data and use same trick as above. - */ - bit = fls64(~data & (BIT_ULL(offset) - 1)); - dev_dbg(imon->dev, "space: %d bits", offset - bit); - - rawir.pulse = false; - rawir.duration = (offset - bit) * BIT_DURATION; - ir_raw_event_store_with_filter(imon->rcdev, &rawir); - - offset = bit; + data = ~data; } while (offset > 0); if (packet_no == 0x0a && !imon->rcdev->idle) { -- cgit v1.2.3 From 727fe909af7545fc317cd1ead4678724ec87df0a Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Wed, 9 Oct 2019 11:55:22 -0300 Subject: media: dvb-frontends: Use DIV_ROUND_CLOSEST directly to make it readable The kernel.h macro DIV_ROUND_CLOSEST performs the computation (x + d/2)/d but is perhaps more readable. Signed-off-by: zhong jiang Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mt312.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c index 7cae7d632030..d43a67045dbe 100644 --- a/drivers/media/dvb-frontends/mt312.c +++ b/drivers/media/dvb-frontends/mt312.c @@ -135,11 +135,6 @@ static inline int mt312_writereg(struct mt312_state *state, return mt312_write(state, reg, &tmp, 1); } -static inline u32 mt312_div(u32 a, u32 b) -{ - return (a + (b / 2)) / b; -} - static int mt312_reset(struct mt312_state *state, const u8 full) { return mt312_writereg(state, RESET, full ? 0x80 : 0x40); @@ -187,7 +182,7 @@ static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr) monitor = (buf[0] << 8) | buf[1]; dprintk("sr(auto) = %u\n", - mt312_div(monitor * 15625, 4)); + DIV_ROUND_CLOSEST(monitor * 15625, 4)); } else { ret = mt312_writereg(state, MON_CTRL, 0x05); if (ret < 0) @@ -291,10 +286,10 @@ static int mt312_initfe(struct dvb_frontend *fe) } /* SYS_CLK */ - buf[0] = mt312_div(state->xtal * state->freq_mult * 2, 1000000); + buf[0] = DIV_ROUND_CLOSEST(state->xtal * state->freq_mult * 2, 1000000); /* DISEQC_RATIO */ - buf[1] = mt312_div(state->xtal, 22000 * 4); + buf[1] = DIV_ROUND_CLOSEST(state->xtal, 22000 * 4); ret = mt312_write(state, SYS_CLK, buf, sizeof(buf)); if (ret < 0) @@ -610,7 +605,7 @@ static int mt312_set_frontend(struct dvb_frontend *fe) } /* sr = (u16)(sr * 256.0 / 1000000.0) */ - sr = mt312_div(p->symbol_rate * 4, 15625); + sr = DIV_ROUND_CLOSEST(p->symbol_rate * 4, 15625); /* SYM_RATE */ buf[0] = (sr >> 8) & 0x3f; -- cgit v1.2.3 From 0f123f820a8affe41cdf80e1998675dda4679574 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Wed, 9 Oct 2019 11:55:23 -0300 Subject: media: tuners/qm1d1c0042: Use DIV_ROUND_CLOSEST directly to make it readable The kernel.h macro DIV_ROUND_CLOSEST performs the computation (x + d/2)/d but is perhaps more readable. Signed-off-by: Zhong Jiang Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/qm1d1c0042.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index 83ca5dc047ea..0e26d22f0b26 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -206,7 +206,7 @@ static int qm1d1c0042_set_params(struct dvb_frontend *fe) if (ret < 0) return ret; - a = (freq + state->cfg.xtal_freq / 2) / state->cfg.xtal_freq; + a = DIV_ROUND_CLOSEST(freq, state->cfg.xtal_freq); state->regs[0x06] &= 0x40; state->regs[0x06] |= (a - 12) / 4; -- cgit v1.2.3 From 22cb099d0c1ba53269bc4b0cfb5039117af4f6fe Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Fri, 11 Oct 2019 16:43:42 -0300 Subject: media: rc: add keymap for Tronsmart Vega S95/S96 remote Add a keymap for the Tronsmart Vega S95 and S96 Android (Amlogic S905/S912) STB devices. Both use the same IR remote. Signed-off-by: Christian Hewitt Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-vega-s9x.c | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-vega-s9x.c (limited to 'drivers/media') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index a56fc634d2d6..4ab4af062abf 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-tt-1500.o \ rc-twinhan-dtv-cab-ci.o \ rc-twinhan1027.o \ + rc-vega-s9x.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ diff --git a/drivers/media/rc/keymaps/rc-vega-s9x.c b/drivers/media/rc/keymaps/rc-vega-s9x.c new file mode 100644 index 000000000000..bf210c4dc535 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-vega-s9x.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Christian Hewitt + +#include +#include + +// +// Keytable for the Tronsmart Vega S9x remote control +// + +static struct rc_map_table vega_s9x[] = { + { 0x18, KEY_POWER }, + { 0x17, KEY_MUTE }, // mouse + + { 0x46, KEY_UP }, + { 0x47, KEY_LEFT }, + { 0x55, KEY_OK }, + { 0x15, KEY_RIGHT }, + { 0x16, KEY_DOWN }, + + { 0x06, KEY_HOME }, + { 0x42, KEY_PLAYPAUSE}, + { 0x40, KEY_BACK }, + + { 0x14, KEY_VOLUMEDOWN }, + { 0x04, KEY_MENU }, + { 0x10, KEY_VOLUMEUP }, +}; + +static struct rc_map_list vega_s9x_map = { + .map = { + .scan = vega_s9x, + .size = ARRAY_SIZE(vega_s9x), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_VEGA_S9X, + } +}; + +static int __init init_rc_map_vega_s9x(void) +{ + return rc_map_register(&vega_s9x_map); +} + +static void __exit exit_rc_map_vega_s9x(void) +{ + rc_map_unregister(&vega_s9x_map); +} + +module_init(init_rc_map_vega_s9x) +module_exit(exit_rc_map_vega_s9x) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt Date: Sun, 13 Oct 2019 23:08:45 -0300 Subject: media: cxusb: detect cxusb_ctrl_msg error in query Don't use uninitialized ircode[] in cxusb_rc_query() when cxusb_ctrl_msg() fails to populate its contents. syzbot reported: dvb-usb: bulk message failed: -22 (1/-30591) ===================================================== BUG: KMSAN: uninit-value in ir_lookup_by_scancode drivers/media/rc/rc-main.c:494 [inline] BUG: KMSAN: uninit-value in rc_g_keycode_from_table drivers/media/rc/rc-main.c:582 [inline] BUG: KMSAN: uninit-value in rc_keydown+0x1a6/0x6f0 drivers/media/rc/rc-main.c:816 CPU: 1 PID: 11436 Comm: kworker/1:2 Not tainted 5.3.0-rc7+ #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Workqueue: events dvb_usb_read_remote_control Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x191/0x1f0 lib/dump_stack.c:113 kmsan_report+0x13a/0x2b0 mm/kmsan/kmsan_report.c:108 __msan_warning+0x73/0xe0 mm/kmsan/kmsan_instr.c:250 bsearch+0x1dd/0x250 lib/bsearch.c:41 ir_lookup_by_scancode drivers/media/rc/rc-main.c:494 [inline] rc_g_keycode_from_table drivers/media/rc/rc-main.c:582 [inline] rc_keydown+0x1a6/0x6f0 drivers/media/rc/rc-main.c:816 cxusb_rc_query+0x2e1/0x360 drivers/media/usb/dvb-usb/cxusb.c:548 dvb_usb_read_remote_control+0xf9/0x290 drivers/media/usb/dvb-usb/dvb-usb-remote.c:261 process_one_work+0x1572/0x1ef0 kernel/workqueue.c:2269 worker_thread+0x111b/0x2460 kernel/workqueue.c:2415 kthread+0x4b5/0x4f0 kernel/kthread.c:256 ret_from_fork+0x35/0x40 arch/x86/entry/entry_64.S:355 Uninit was stored to memory at: kmsan_save_stack_with_flags mm/kmsan/kmsan.c:150 [inline] kmsan_internal_chain_origin+0xd2/0x170 mm/kmsan/kmsan.c:314 __msan_chain_origin+0x6b/0xe0 mm/kmsan/kmsan_instr.c:184 rc_g_keycode_from_table drivers/media/rc/rc-main.c:583 [inline] rc_keydown+0x2c4/0x6f0 drivers/media/rc/rc-main.c:816 cxusb_rc_query+0x2e1/0x360 drivers/media/usb/dvb-usb/cxusb.c:548 dvb_usb_read_remote_control+0xf9/0x290 drivers/media/usb/dvb-usb/dvb-usb-remote.c:261 process_one_work+0x1572/0x1ef0 kernel/workqueue.c:2269 worker_thread+0x111b/0x2460 kernel/workqueue.c:2415 kthread+0x4b5/0x4f0 kernel/kthread.c:256 ret_from_fork+0x35/0x40 arch/x86/entry/entry_64.S:355 Local variable description: ----ircode@cxusb_rc_query Variable was created at: cxusb_rc_query+0x4d/0x360 drivers/media/usb/dvb-usb/cxusb.c:543 dvb_usb_read_remote_control+0xf9/0x290 drivers/media/usb/dvb-usb/dvb-usb-remote.c:261 Signed-off-by: Vito Caputo Reported-by: syzbot Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/cxusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index f02fa0a67aa4..fac19ec46089 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -521,7 +521,8 @@ static int cxusb_rc_query(struct dvb_usb_device *d) { u8 ircode[4]; - cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4); + if (cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4) < 0) + return 0; if (ircode[2] || ircode[3]) rc_keydown(d->rc_dev, RC_PROTO_NEC, -- cgit v1.2.3 From 75564e3a5016651f1c7f8eec63b557e24d6d1e17 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Oct 2019 08:42:47 -0300 Subject: media: mb86a20s: make the bit rate estimation function more generic While 99% of the implementation of the bitrate estimation routine for ISDB-T is generic, the current approach mangles it with some mb86a20s-specific thing. Split the calculus from the specific stuff, in order to make easier to use the same approach on other drivers requiring a similar formula. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sean Young --- drivers/media/dvb-frontends/mb86a20s.c | 54 +++++++++++++++------------------- 1 file changed, 23 insertions(+), 31 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 4e50441c247a..a7faf0cf8788 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -517,7 +517,7 @@ static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) * Estimates the bit rate using the per-segment bit rate given by * ABNT/NBR 15601 spec (table 4). */ -static u32 isdbt_rate[3][5][4] = { +static const u32 isdbt_rate[3][5][4] = { { /* DQPSK/QPSK */ { 280850, 312060, 330420, 340430 }, /* 1/2 */ { 374470, 416080, 440560, 453910 }, /* 2/3 */ @@ -539,13 +539,9 @@ static u32 isdbt_rate[3][5][4] = { } }; -static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, - u32 modulation, u32 forward_error_correction, - u32 guard_interval, - u32 segment) +static u32 isdbt_layer_min_bitrate(struct dtv_frontend_properties *c, + u32 layer) { - struct mb86a20s_state *state = fe->demodulator_priv; - u32 rate; int mod, fec, guard; /* @@ -553,7 +549,7 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, * to consider the lowest bit rate, to avoid taking too long time * to get BER. */ - switch (modulation) { + switch (c->layer[layer].modulation) { case DQPSK: case QPSK: default: @@ -567,7 +563,7 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, break; } - switch (forward_error_correction) { + switch (c->layer[layer].fec) { default: case FEC_1_2: case FEC_AUTO: @@ -587,7 +583,7 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, break; } - switch (guard_interval) { + switch (c->guard_interval) { default: case GUARD_INTERVAL_1_4: guard = 0; @@ -603,29 +599,14 @@ static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, break; } - /* Samples BER at BER_SAMPLING_RATE seconds */ - rate = isdbt_rate[mod][fec][guard] * segment * BER_SAMPLING_RATE; - - /* Avoids sampling too quickly or to overflow the register */ - if (rate < 256) - rate = 256; - else if (rate > (1 << 24) - 1) - rate = (1 << 24) - 1; - - dev_dbg(&state->i2c->dev, - "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n", - __func__, 'A' + layer, - segment * isdbt_rate[mod][fec][guard]/1000, - rate, rate); - - state->estimated_rate[layer] = rate; + return isdbt_rate[mod][fec][guard] * c->layer[layer].segment_count; } static int mb86a20s_get_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int layer, rc; + int layer, rc, rate, counter; dev_dbg(&state->i2c->dev, "%s called.\n", __func__); @@ -676,10 +657,21 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", __func__, rc); c->layer[layer].interleaving = rc; - mb86a20s_layer_bitrate(fe, layer, c->layer[layer].modulation, - c->layer[layer].fec, - c->guard_interval, - c->layer[layer].segment_count); + + rate = isdbt_layer_min_bitrate(c, layer); + counter = rate * BER_SAMPLING_RATE; + + /* Avoids sampling too quickly or to overflow the register */ + if (counter < 256) + counter = 256; + else if (counter > (1 << 24) - 1) + counter = (1 << 24) - 1; + + dev_dbg(&state->i2c->dev, + "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n", + __func__, 'A' + layer, rate / 1000, counter, counter); + + state->estimated_rate[layer] = counter; } rc = mb86a20s_writereg(state, 0x6d, 0x84); -- cgit v1.2.3 From d9aeaa6d4ca44df5fae745fd47aede8b3f6137f1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 6 Oct 2019 09:48:23 -0300 Subject: media: cxd2841er: avoid too many status inquires I2C ops are expensive, as the I2C bus typical speed is 100kbps. Also, stats reading take some time, as it requires to retrieve a certain number of packets to complete. While we don't know the minimal for CXD2841er, trying to do it too quickly is still a very bad idea. So, add some sanity logic there, preventing to retrieve stats faster than one second. This shouldn't cause any issues with well behavior apps, as they usually take stats on a polling rate slower than 1 second. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sean Young --- drivers/media/dvb-frontends/cxd2841er.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 1b30cf570803..758c95bc3b11 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -60,6 +60,7 @@ struct cxd2841er_priv { enum cxd2841er_xtal xtal; enum fe_caps caps; u32 flags; + unsigned long stats_time; }; static const struct cxd2841er_cnr_data s_cn_data[] = { @@ -3279,9 +3280,15 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe, p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; if (status & FE_HAS_LOCK) { + if (priv->stats_time && + (!time_after(jiffies, priv->stats_time))) + return 0; + + /* Prevent retrieving stats faster than once per second */ + priv->stats_time = jiffies + msecs_to_jiffies(1000); + cxd2841er_read_snr(fe); cxd2841er_read_ucblocks(fe); - cxd2841er_read_ber(fe); } else { p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; @@ -3360,6 +3367,9 @@ done: p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + /* Reset the wait for jiffies logic */ + priv->stats_time = 0; + return ret; } -- cgit v1.2.3 From 8c279e9394cade640ed86ec6c6645a0e7df5e0b6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 29 Jul 2019 23:14:55 -0300 Subject: media: uvcvideo: Fix error path in control parsing failure When parsing the UVC control descriptors fails, the error path tries to cleanup a media device that hasn't been initialised, potentially resulting in a crash. Fix this by initialising the media device before the error handling path can be reached. Fixes: 5a254d751e52 ("[media] uvcvideo: Register a v4l2_device") Reported-by: syzbot+c86454eb3af9e8a4da20@syzkaller.appspotmail.com Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_driver.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 66ee168ddc7e..428235ca2635 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2151,6 +2151,20 @@ static int uvc_probe(struct usb_interface *intf, sizeof(dev->name) - len); } + /* Initialize the media device. */ +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &intf->dev; + strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); + if (udev->serial) + strscpy(dev->mdev.serial, udev->serial, + sizeof(dev->mdev.serial)); + usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); + dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); + media_device_init(&dev->mdev); + + dev->vdev.mdev = &dev->mdev; +#endif + /* Parse the Video Class control descriptor. */ if (uvc_parse_control(dev) < 0) { uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " @@ -2171,19 +2185,7 @@ static int uvc_probe(struct usb_interface *intf, "linux-uvc-devel mailing list.\n"); } - /* Initialize the media device and register the V4L2 device. */ -#ifdef CONFIG_MEDIA_CONTROLLER - dev->mdev.dev = &intf->dev; - strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); - if (udev->serial) - strscpy(dev->mdev.serial, udev->serial, - sizeof(dev->mdev.serial)); - usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); - dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); - media_device_init(&dev->mdev); - - dev->vdev.mdev = &dev->mdev; -#endif + /* Register the V4L2 device. */ if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) goto error; -- cgit v1.2.3 From ac7dabf140495a6157270354812a643486096ce5 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 24 Jul 2019 01:56:12 -0300 Subject: media: uvcvideo: Fix a typo in UVC_METATADA_BUF_SIZE It is likely that it should be UVC_METADATA_BUF_SIZE instead. Fix it and use it. Signed-off-by: Christophe JAILLET Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_metadata.c | 4 ++-- drivers/media/usb/uvc/uvc_queue.c | 2 +- drivers/media/usb/uvc/uvcvideo.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/uvc/uvc_metadata.c b/drivers/media/usb/uvc/uvc_metadata.c index 99bb71b47117..b6279ad7ac84 100644 --- a/drivers/media/usb/uvc/uvc_metadata.c +++ b/drivers/media/usb/uvc/uvc_metadata.c @@ -51,7 +51,7 @@ static int uvc_meta_v4l2_get_format(struct file *file, void *fh, memset(fmt, 0, sizeof(*fmt)); fmt->dataformat = stream->meta.format; - fmt->buffersize = UVC_METATADA_BUF_SIZE; + fmt->buffersize = UVC_METADATA_BUF_SIZE; return 0; } @@ -72,7 +72,7 @@ static int uvc_meta_v4l2_try_format(struct file *file, void *fh, fmt->dataformat = fmeta == dev->info->meta_format ? fmeta : V4L2_META_FMT_UVC; - fmt->buffersize = UVC_METATADA_BUF_SIZE; + fmt->buffersize = UVC_METADATA_BUF_SIZE; return 0; } diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index da72577c2998..cd60c6c1749e 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -79,7 +79,7 @@ static int uvc_queue_setup(struct vb2_queue *vq, switch (vq->type) { case V4L2_BUF_TYPE_META_CAPTURE: - size = UVC_METATADA_BUF_SIZE; + size = UVC_METADATA_BUF_SIZE; break; default: diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index c7c1baa90dea..f773dc5d802c 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -491,7 +491,7 @@ struct uvc_stats_stream { unsigned int max_sof; /* Maximum STC.SOF value */ }; -#define UVC_METATADA_BUF_SIZE 1024 +#define UVC_METADATA_BUF_SIZE 1024 /** * struct uvc_copy_op: Context structure to schedule asynchronous memcpy -- cgit v1.2.3 From 137272cdf7cc5be835f44216e6003769d1638480 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Oct 2019 06:32:40 -0300 Subject: media: vb2: add V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF This patch adds support for the V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF flag. It also adds a new V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF capability. Drivers should set vb2_queue->subsystem_flags to VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF to indicate support for this flag. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 5a9ba3846f0a..e652f4318284 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -49,8 +49,11 @@ module_param(debug, int, 0644); V4L2_BUF_FLAG_REQUEST_FD | \ V4L2_BUF_FLAG_TIMESTAMP_MASK) /* Output buffer flags that should be passed on to the driver */ -#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \ - V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE) +#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | \ + V4L2_BUF_FLAG_BFRAME | \ + V4L2_BUF_FLAG_KEYFRAME | \ + V4L2_BUF_FLAG_TIMECODE | \ + V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) /* * __verify_planes_array() - verify that the planes array passed in struct @@ -194,6 +197,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b } vbuf->sequence = 0; vbuf->request_fd = -1; + vbuf->is_held = false; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { switch (b->memory) { @@ -321,6 +325,8 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b */ vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE; vbuf->field = b->field; + if (!(q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)) + vbuf->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; } else { /* Zero any output buffer flags as this is a capture buffer */ vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS; @@ -654,6 +660,8 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR; if (q->io_modes & VB2_DMABUF) *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; + if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) + *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; -- cgit v1.2.3 From f8cca8c97a63d77f48334cde81d15014f43530ef Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Oct 2019 06:32:41 -0300 Subject: media: v4l2-mem2mem: support held capture buffers Check for held buffers that are ready to be returned to vb2 in __v4l2_m2m_try_queue(). This avoids drivers having to handle this case. Add v4l2_m2m_buf_done_and_job_finish() to correctly return source and destination buffers and mark the job as finished while taking a held destination buffer into account (i.e. that buffer won't be returned). This has to be done while job_spinlock is held to avoid race conditions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 130 ++++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 33 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 19937dd3c6f6..79c3656f24f7 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -284,7 +284,8 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, struct v4l2_m2m_ctx *m2m_ctx) { - unsigned long flags_job, flags_out, flags_cap; + unsigned long flags_job; + struct vb2_v4l2_buffer *dst, *src; dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); @@ -307,20 +308,30 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, goto job_unlock; } - spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); - if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue) - && !m2m_ctx->out_q_ctx.buffered) { + src = v4l2_m2m_next_src_buf(m2m_ctx); + dst = v4l2_m2m_next_dst_buf(m2m_ctx); + if (!src && !m2m_ctx->out_q_ctx.buffered) { dprintk("No input buffers available\n"); - goto out_unlock; + goto job_unlock; } - spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); - if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue) - && !m2m_ctx->cap_q_ctx.buffered) { + if (!dst && !m2m_ctx->cap_q_ctx.buffered) { dprintk("No output buffers available\n"); - goto cap_unlock; + goto job_unlock; + } + + if (src && dst && + dst->is_held && dst->vb2_buf.copied_timestamp && + dst->vb2_buf.timestamp != src->vb2_buf.timestamp) { + dst->is_held = false; + v4l2_m2m_dst_buf_remove(m2m_ctx); + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); + dst = v4l2_m2m_next_dst_buf(m2m_ctx); + + if (!dst && !m2m_ctx->cap_q_ctx.buffered) { + dprintk("No output buffers available after returning held buffer\n"); + goto job_unlock; + } } - spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); - spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); if (m2m_dev->m2m_ops->job_ready && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { @@ -331,13 +342,6 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue); m2m_ctx->job_flags |= TRANS_QUEUED; - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); - return; - -cap_unlock: - spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); -out_unlock: - spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); job_unlock: spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); } @@ -412,37 +416,97 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) } } -void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, - struct v4l2_m2m_ctx *m2m_ctx) +/* + * Schedule the next job, called from v4l2_m2m_job_finish() or + * v4l2_m2m_buf_done_and_job_finish(). + */ +static void v4l2_m2m_schedule_next_job(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) { - unsigned long flags; + /* + * This instance might have more buffers ready, but since we do not + * allow more than one job on the job_queue per instance, each has + * to be scheduled separately after the previous one finishes. + */ + __v4l2_m2m_try_queue(m2m_dev, m2m_ctx); - spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + /* + * We might be running in atomic context, + * but the job must be run in non-atomic context. + */ + schedule_work(&m2m_dev->job_work); +} + +/* + * Assumes job_spinlock is held, called from v4l2_m2m_job_finish() or + * v4l2_m2m_buf_done_and_job_finish(). + */ +static bool _v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) +{ if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); dprintk("Called by an instance not currently running\n"); - return; + return false; } list_del(&m2m_dev->curr_ctx->queue); m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); wake_up(&m2m_dev->curr_ctx->finished); m2m_dev->curr_ctx = NULL; + return true; +} - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - - /* This instance might have more buffers ready, but since we do not - * allow more than one job on the job_queue per instance, each has - * to be scheduled separately after the previous one finishes. */ - __v4l2_m2m_try_queue(m2m_dev, m2m_ctx); +void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) +{ + unsigned long flags; + bool schedule_next; - /* We might be running in atomic context, - * but the job must be run in non-atomic context. + /* + * This function should not be used for drivers that support + * holding capture buffers. Those should use + * v4l2_m2m_buf_done_and_job_finish() instead. */ - schedule_work(&m2m_dev->job_work); + WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags & + VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF); + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + if (schedule_next) + v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx); } EXPORT_SYMBOL(v4l2_m2m_job_finish); +void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *src_buf, *dst_buf; + bool schedule_next = false; + unsigned long flags; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx); + + if (WARN_ON(!src_buf || !dst_buf)) + goto unlock; + v4l2_m2m_buf_done(src_buf, state); + dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; + if (!dst_buf->is_held) { + v4l2_m2m_dst_buf_remove(m2m_ctx); + v4l2_m2m_buf_done(dst_buf, state); + } + schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx); +unlock: + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + if (schedule_next) + v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx); +} +EXPORT_SYMBOL(v4l2_m2m_buf_done_and_job_finish); + int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_requestbuffers *reqbufs) { -- cgit v1.2.3 From bef41d93aac64b54c3008ca6170bec54f85784f5 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Fri, 11 Oct 2019 06:32:43 -0300 Subject: media: v4l2-mem2mem: add stateless_(try_)decoder_cmd ioctl helpers These helpers are used by stateless codecs when they support multiple slices per frame and hold capture buffer flag is set. It's expected that all such codecs will use this code. Signed-off-by: Jernej Skrabec Co-developed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 79c3656f24f7..b46d2c388349 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -1218,6 +1218,59 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, } EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd); +int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + if (dc->cmd != V4L2_DEC_CMD_FLUSH) + return -EINVAL; + + dc->flags = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_try_decoder_cmd); + +int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *dc) +{ + struct v4l2_fh *fh = file->private_data; + struct vb2_v4l2_buffer *out_vb, *cap_vb; + struct v4l2_m2m_dev *m2m_dev = fh->m2m_ctx->m2m_dev; + unsigned long flags; + int ret; + + ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, dc); + if (ret < 0) + return ret; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + out_vb = v4l2_m2m_last_src_buf(fh->m2m_ctx); + cap_vb = v4l2_m2m_last_dst_buf(fh->m2m_ctx); + + /* + * If there is an out buffer pending, then clear any HOLD flag. + * + * By clearing this flag we ensure that when this output + * buffer is processed any held capture buffer will be released. + */ + if (out_vb) { + out_vb->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; + } else if (cap_vb && cap_vb->is_held) { + /* + * If there were no output buffers, but there is a + * capture buffer that is held, then release that + * buffer. + */ + cap_vb->is_held = false; + v4l2_m2m_dst_buf_remove(fh->m2m_ctx); + v4l2_m2m_buf_done(cap_vb, VB2_BUF_STATE_DONE); + } + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_decoder_cmd); + /* * v4l2_file_operations helpers. It is assumed here same lock is used * for the output and the capture buffer queue. -- cgit v1.2.3 From f07602ac388723233e9e3c5a05b54baf34e0a3e9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Oct 2019 06:32:44 -0300 Subject: media: v4l2-mem2mem: add new_frame detection Drivers that support VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF typically want to know if a new frame is started (i.e. the first slice is about to be processed). Add a new_frame bool to v4l2_m2m_ctx and set it accordingly. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index b46d2c388349..db07ef3bf3d0 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -319,8 +319,10 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, goto job_unlock; } - if (src && dst && - dst->is_held && dst->vb2_buf.copied_timestamp && + m2m_ctx->new_frame = true; + + if (src && dst && dst->is_held && + dst->vb2_buf.copied_timestamp && dst->vb2_buf.timestamp != src->vb2_buf.timestamp) { dst->is_held = false; v4l2_m2m_dst_buf_remove(m2m_ctx); @@ -333,6 +335,11 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, } } + if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags & + VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)) + m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp || + dst->vb2_buf.timestamp != src->vb2_buf.timestamp; + if (m2m_dev->m2m_ops->job_ready && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { dprintk("Driver not ready\n"); -- cgit v1.2.3 From 96f49c1ac370c23ae55c63c67a0b40d83928bfbd Mon Sep 17 00:00:00 2001 From: Vandana BN Date: Mon, 14 Oct 2019 05:40:19 -0300 Subject: media: v4l2-core: correctly validate video and metadata ioctls If the type is VFL_TYPE_GRABBER, then also check device_caps to see if the video device supports video and/or metadata and disable unneeded ioctls. Without this change, format ioctls for both video and metadata devices could be called on both device nodes. This is true for other ioctls as well, even if the device supports only video or metadata. Metadata devices act similar to VBI devices w.r.t. which ioctls should be enabled. This makes sense since VBI *is* metadata. Signed-off-by: Vandana BN Co-developed-by: Hans Verkuil Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 62 +++++++++++++++++++++++------------- drivers/media/v4l2-core/v4l2-ioctl.c | 16 ++++++++-- 2 files changed, 52 insertions(+), 26 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 4037689a945a..1bf543932e4f 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -533,13 +533,23 @@ static int get_index(struct video_device *vdev) */ static void determine_valid_ioctls(struct video_device *vdev) { + const u32 vid_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE; + const u32 meta_caps = V4L2_CAP_META_CAPTURE | + V4L2_CAP_META_OUTPUT; DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; - bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER; + bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER && + (vdev->device_caps & vid_caps); bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI; bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO; bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR; bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH; + bool is_meta = vdev->vfl_type == VFL_TYPE_GRABBER && + (vdev->device_caps & meta_caps); bool is_rx = vdev->vfl_dir != VFL_DIR_TX; bool is_tx = vdev->vfl_dir != VFL_DIR_RX; @@ -587,39 +597,31 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); if (is_vid || is_tch) { - /* video and metadata specific ioctls */ + /* video and touch specific ioctls */ if ((is_rx && (ops->vidioc_enum_fmt_vid_cap || - ops->vidioc_enum_fmt_vid_overlay || - ops->vidioc_enum_fmt_meta_cap)) || - (is_tx && (ops->vidioc_enum_fmt_vid_out || - ops->vidioc_enum_fmt_meta_out))) + ops->vidioc_enum_fmt_vid_overlay)) || + (is_tx && ops->vidioc_enum_fmt_vid_out)) set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane || - ops->vidioc_g_fmt_vid_overlay || - ops->vidioc_g_fmt_meta_cap)) || + ops->vidioc_g_fmt_vid_overlay)) || (is_tx && (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane || - ops->vidioc_g_fmt_vid_out_overlay || - ops->vidioc_g_fmt_meta_out))) + ops->vidioc_g_fmt_vid_out_overlay))) set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_s_fmt_vid_cap || ops->vidioc_s_fmt_vid_cap_mplane || - ops->vidioc_s_fmt_vid_overlay || - ops->vidioc_s_fmt_meta_cap)) || + ops->vidioc_s_fmt_vid_overlay)) || (is_tx && (ops->vidioc_s_fmt_vid_out || ops->vidioc_s_fmt_vid_out_mplane || - ops->vidioc_s_fmt_vid_out_overlay || - ops->vidioc_s_fmt_meta_out))) + ops->vidioc_s_fmt_vid_out_overlay))) set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_try_fmt_vid_cap || ops->vidioc_try_fmt_vid_cap_mplane || - ops->vidioc_try_fmt_vid_overlay || - ops->vidioc_try_fmt_meta_cap)) || + ops->vidioc_try_fmt_vid_overlay)) || (is_tx && (ops->vidioc_try_fmt_vid_out || ops->vidioc_try_fmt_vid_out_mplane || - ops->vidioc_try_fmt_vid_out_overlay || - ops->vidioc_try_fmt_meta_out))) + ops->vidioc_try_fmt_vid_out_overlay))) set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); @@ -641,7 +643,21 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection); SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection); - } else if (is_vbi) { + } + if (is_meta && is_rx) { + /* metadata capture specific ioctls */ + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_meta_cap); + SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_meta_cap); + SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_meta_cap); + SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_meta_cap); + } else if (is_meta && is_tx) { + /* metadata output specific ioctls */ + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_meta_out); + SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_meta_out); + SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_meta_out); + SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_meta_out); + } + if (is_vbi) { /* vbi specific ioctls */ if ((is_rx && (ops->vidioc_g_fmt_vbi_cap || ops->vidioc_g_fmt_sliced_vbi_cap)) || @@ -681,8 +697,8 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); } - if (is_vid || is_vbi || is_sdr || is_tch) { - /* ioctls valid for video, metadata, vbi or sdr */ + if (is_vid || is_vbi || is_sdr || is_tch || is_meta) { + /* ioctls valid for video, vbi, sdr, touch and metadata */ SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); @@ -694,8 +710,8 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); } - if (is_vid || is_vbi || is_tch) { - /* ioctls valid for video or vbi */ + if (is_vid || is_vbi || is_tch || is_meta) { + /* ioctls valid for video, vbi, touch and metadata */ if (ops->vidioc_s_std) set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 51b912743f0f..20b3107dd4e8 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -932,12 +932,22 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) static int check_fmt(struct file *file, enum v4l2_buf_type type) { + const u32 vid_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE; + const u32 meta_caps = V4L2_CAP_META_CAPTURE | + V4L2_CAP_META_OUTPUT; struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; + bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER && + (vfd->device_caps & vid_caps); bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; + bool is_meta = vfd->vfl_type == VFL_TYPE_GRABBER && + (vfd->device_caps & meta_caps); bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; @@ -996,11 +1006,11 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) return 0; break; case V4L2_BUF_TYPE_META_CAPTURE: - if (is_vid && is_rx && ops->vidioc_g_fmt_meta_cap) + if (is_meta && is_rx && ops->vidioc_g_fmt_meta_cap) return 0; break; case V4L2_BUF_TYPE_META_OUTPUT: - if (is_vid && is_tx && ops->vidioc_g_fmt_meta_out) + if (is_meta && is_tx && ops->vidioc_g_fmt_meta_out) return 0; break; default: -- cgit v1.2.3 From 8e72244b4e8f1a163d358c271f39c737b5b8106a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Oct 2019 05:40:20 -0300 Subject: media: v4l2-dev: simplify the SDR checks In determine_valid_ioctls() we can use SET_VALID_IOCTL to enable ioctls for SDR, simplifying the code. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 1bf543932e4f..27fb96a6c2a8 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -677,24 +677,16 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); } else if (is_sdr && is_rx) { /* SDR receiver specific ioctls */ - if (ops->vidioc_enum_fmt_sdr_cap) - set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); - if (ops->vidioc_g_fmt_sdr_cap) - set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); - if (ops->vidioc_s_fmt_sdr_cap) - set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); - if (ops->vidioc_try_fmt_sdr_cap) - set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_sdr_cap); + SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_sdr_cap); + SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_sdr_cap); + SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_sdr_cap); } else if (is_sdr && is_tx) { /* SDR transmitter specific ioctls */ - if (ops->vidioc_enum_fmt_sdr_out) - set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); - if (ops->vidioc_g_fmt_sdr_out) - set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); - if (ops->vidioc_s_fmt_sdr_out) - set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); - if (ops->vidioc_try_fmt_sdr_out) - set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_sdr_out); + SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_sdr_out); + SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_sdr_out); + SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_sdr_out); } if (is_vid || is_vbi || is_sdr || is_tch || is_meta) { -- cgit v1.2.3 From 4fbd54bbd24360c28ceec2ae9ee2c950b743aee1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Oct 2019 05:40:21 -0300 Subject: media: v4l2-dev: fix is_tch checks Touch devices mark too many ioctls as valid. Restrict the list of valid ioctls for touch devices. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 27fb96a6c2a8..cec588b04711 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -596,8 +596,8 @@ static void determine_valid_ioctls(struct video_device *vdev) if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); - if (is_vid || is_tch) { - /* video and touch specific ioctls */ + if (is_vid) { + /* video specific ioctls */ if ((is_rx && (ops->vidioc_enum_fmt_vid_cap || ops->vidioc_enum_fmt_vid_overlay)) || (is_tx && ops->vidioc_enum_fmt_vid_out)) @@ -675,6 +675,19 @@ static void determine_valid_ioctls(struct video_device *vdev) ops->vidioc_try_fmt_sliced_vbi_out))) set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); + } else if (is_tch) { + /* touch specific ioctls */ + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_vid_cap); + SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_vid_cap); + SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_vid_cap); + SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_vid_cap); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals); + SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); + SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); + SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); + SET_VALID_IOCTL(ops, VIDIOC_G_PARM, vidioc_g_parm); + SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); } else if (is_sdr && is_rx) { /* SDR receiver specific ioctls */ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_sdr_cap); @@ -702,8 +715,8 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); } - if (is_vid || is_vbi || is_tch || is_meta) { - /* ioctls valid for video, vbi, touch and metadata */ + if (is_vid || is_vbi || is_meta) { + /* ioctls valid for video, vbi and metadata */ if (ops->vidioc_s_std) set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std); @@ -727,8 +740,7 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout); SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout); } - if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER && - ops->vidioc_g_std)) + if (ops->vidioc_g_parm || ops->vidioc_g_std) set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings); -- cgit v1.2.3 From 8669d8474a58268295414b9f78a41659698bdb87 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Oct 2019 09:01:05 -0300 Subject: media: v4l2-dev: disable frequency and tuner ioctls for touch Touch devices have obviously no tuner, so don't attempt to enable those ioctls for such devices. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index cec588b04711..da42d172714a 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -581,8 +581,10 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_querymenu) set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency); - SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); + if (!is_tch) { + SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency); + SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); + } SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status); #ifdef CONFIG_VIDEO_ADV_DEBUG set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls); @@ -754,7 +756,7 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator); SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator); } - if (is_rx) { + if (is_rx && !is_tch) { /* receiver only ioctls */ SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner); SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner); -- cgit v1.2.3 From d5797cf685a0a3310457d89ef640be68a1de0743 Mon Sep 17 00:00:00 2001 From: Vandana BN Date: Tue, 15 Oct 2019 07:40:15 -0300 Subject: media: vivid: Add metadata capture support This patch adds meatadata capture support in vivid driver. Adds new files for metadata capture. Adds vivid controls to generate PTS and SCR for metadata stream. also fixes v4l2-compliance issues seen on metadata device. Signed-off-by: Vandana BN Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/Makefile | 2 +- drivers/media/platform/vivid/vivid-core.c | 109 +++++++++++- drivers/media/platform/vivid/vivid-core.h | 14 ++ drivers/media/platform/vivid/vivid-ctrls.c | 65 ++++++++ drivers/media/platform/vivid/vivid-kthread-cap.c | 54 +++++- drivers/media/platform/vivid/vivid-meta-cap.c | 201 +++++++++++++++++++++++ drivers/media/platform/vivid/vivid-meta-cap.h | 29 ++++ drivers/media/platform/vivid/vivid-vid-cap.c | 5 +- 8 files changed, 465 insertions(+), 14 deletions(-) create mode 100644 drivers/media/platform/vivid/vivid-meta-cap.c create mode 100644 drivers/media/platform/vivid/vivid-meta-cap.h (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile index 2f5762e3309a..af94abf9bce6 100644 --- a/drivers/media/platform/vivid/Makefile +++ b/drivers/media/platform/vivid/Makefile @@ -3,7 +3,7 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \ vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ - vivid-osd.o + vivid-osd.o vivid-meta-cap.o ifeq ($(CONFIG_VIDEO_VIVID_CEC),y) vivid-objs += vivid-cec.o endif diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 53315c8dd2bb..a44984536ad0 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -37,6 +37,7 @@ #include "vivid-osd.h" #include "vivid-cec.h" #include "vivid-ctrls.h" +#include "vivid-meta-cap.h" #define VIVID_MODULE_NAME "vivid" @@ -79,6 +80,10 @@ static int radio_tx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; module_param_array(radio_tx_nr, int, NULL, 0444); MODULE_PARM_DESC(radio_tx_nr, " radioX start number, -1 is autodetect"); +static int meta_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(meta_cap_nr, int, NULL, 0444); +MODULE_PARM_DESC(meta_cap_nr, " videoX start number, -1 is autodetect"); + static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; module_param_array(ccs_cap_mode, int, NULL, 0444); MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n" @@ -95,10 +100,15 @@ static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 module_param_array(multiplanar, uint, NULL, 0444); MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device."); -/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */ -static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d }; +/* + * Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + + * vbi-out + vid-out + meta-cap + */ +static unsigned int node_types[VIVID_MAX_DEVS] = { + [0 ... (VIVID_MAX_DEVS - 1)] = 0x21d3d +}; module_param_array(node_types, uint, NULL, 0444); -MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n" +MODULE_PARM_DESC(node_types, " node types, default is 0x21d3d. Bitmask with the following meaning:\n" "\t\t bit 0: Video Capture node\n" "\t\t bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" "\t\t bit 4: Radio Receiver node\n" @@ -106,7 +116,8 @@ MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the f "\t\t bit 8: Video Output node\n" "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" "\t\t bit 12: Radio Transmitter node\n" - "\t\t bit 16: Framebuffer for testing overlays"); + "\t\t bit 16: Framebuffer for testing overlays\n" + "\t\t bit 17: Metadata Capture node\n"); /* Default: 4 inputs */ static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 }; @@ -205,7 +216,7 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps | dev->vbi_cap_caps | dev->vbi_out_caps | dev->radio_rx_caps | dev->radio_tx_caps | - dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS; + dev->sdr_cap_caps | dev->meta_cap_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -433,7 +444,8 @@ static bool vivid_is_last_user(struct vivid_dev *dev) vivid_is_in_use(&dev->vbi_out_dev) + vivid_is_in_use(&dev->sdr_cap_dev) + vivid_is_in_use(&dev->radio_rx_dev) + - vivid_is_in_use(&dev->radio_tx_dev); + vivid_is_in_use(&dev->radio_tx_dev) + + vivid_is_in_use(&dev->meta_cap_dev); return uses == 1; } @@ -459,6 +471,7 @@ static int vivid_fop_release(struct file *file) set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); } mutex_unlock(&dev->mutex); if (file->private_data == dev->overlay_cap_owner) @@ -604,6 +617,11 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_log_status = vidioc_log_status, .vidioc_subscribe_event = vidioc_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_enum_fmt_meta_cap = vidioc_enum_fmt_meta_cap, + .vidioc_g_fmt_meta_cap = vidioc_g_fmt_meta_cap, + .vidioc_s_fmt_meta_cap = vidioc_g_fmt_meta_cap, + .vidioc_try_fmt_meta_cap = vidioc_g_fmt_meta_cap, }; /* ----------------------------------------------------------------- @@ -818,6 +836,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->has_scaler_out ? 'Y' : 'N'); } + /* do we create a meta capture device */ + dev->has_meta_cap = node_type & 0x20000; + /* end detecting feature set */ if (dev->has_vid_cap) { @@ -875,6 +896,16 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR | V4L2_CAP_READWRITE; + /* set up the capabilities of meta capture device */ + if (dev->has_meta_cap) { + dev->meta_cap_caps = V4L2_CAP_META_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_inputs) + dev->meta_cap_caps |= V4L2_CAP_AUDIO; + if (in_type_counter[TV]) + dev->meta_cap_caps |= V4L2_CAP_TUNER; + } + ret = -ENOMEM; /* initialize the test pattern generator */ tpg_init(&dev->tpg, 640, 360); @@ -934,6 +965,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO); v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO); v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_ENUMAUDIO); } if (!dev->has_audio_outputs) { v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT); @@ -959,12 +993,16 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY); v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY); v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_FREQUENCY); } if (!has_tuner) { v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER); v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER); v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER); v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_TUNER); } if (in_type_counter[HDMI] == 0) { v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID); @@ -990,6 +1028,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK); v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK); v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK); + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_HW_FREQ_SEEK); v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY); v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY); v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES); @@ -1078,6 +1117,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) INIT_LIST_HEAD(&dev->vbi_cap_active); INIT_LIST_HEAD(&dev->vbi_out_active); INIT_LIST_HEAD(&dev->sdr_cap_active); + INIT_LIST_HEAD(&dev->meta_cap_active); INIT_LIST_HEAD(&dev->cec_work_list); spin_lock_init(&dev->cec_slock); @@ -1225,6 +1265,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->fb_info.node); } + if (dev->has_meta_cap) { + /* initialize meta_cap queue */ + q = &dev->vb_meta_cap_q; + q->type = V4L2_BUF_TYPE_META_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + if (!allocator) + q->io_modes |= VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_meta_cap_qops; + q->mem_ops = vivid_mem_ops[allocator]; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &dev->mutex; + q->dev = dev->v4l2_dev.dev; + q->supports_requests = true; + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + #ifdef CONFIG_VIDEO_VIVID_CEC if (dev->has_vid_cap && in_type_counter[HDMI]) { struct cec_adapter *adap; @@ -1265,6 +1326,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_cap); /* finally start creating the device nodes */ if (dev->has_vid_cap) { @@ -1492,6 +1554,35 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) video_device_node_name(vfd)); } + if (dev->has_meta_cap) { + vfd = &dev->meta_cap_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-meta-cap", inst); + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->meta_cap_caps; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_meta_cap_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_cap; + video_set_drvdata(vfd, dev); +#ifdef CONFIG_MEDIA_CONTROLLER + dev->meta_cap_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, + &dev->meta_cap_pad); + if (ret) + goto unreg_dev; +#endif + ret = video_register_device(vfd, VFL_TYPE_GRABBER, + meta_cap_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, + "V4L2 metadata capture device registered as %s\n", + video_device_node_name(vfd)); + } + #ifdef CONFIG_MEDIA_CONTROLLER /* Register the media device */ ret = media_device_register(&dev->mdev); @@ -1508,6 +1599,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) return 0; unreg_dev: + video_unregister_device(&dev->meta_cap_dev); video_unregister_device(&dev->radio_tx_dev); video_unregister_device(&dev->radio_rx_dev); video_unregister_device(&dev->sdr_cap_dev); @@ -1624,6 +1716,11 @@ static int vivid_remove(struct platform_device *pdev) unregister_framebuffer(&dev->fb_info); vivid_fb_release_buffers(dev); } + if (dev->has_meta_cap) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->meta_cap_dev)); + video_unregister_device(&dev->meta_cap_dev); + } cec_unregister_adapter(dev->cec_rx_adap); for (j = 0; j < MAX_OUTPUTS; j++) cec_unregister_adapter(dev->cec_tx_adap[j]); diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index 7ebb14673c75..fd601345a17c 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -131,6 +131,7 @@ struct vivid_dev { struct media_pad vbi_cap_pad; struct media_pad vbi_out_pad; struct media_pad sdr_cap_pad; + struct media_pad meta_cap_pad; #endif struct v4l2_ctrl_handler ctrl_hdl_user_gen; struct v4l2_ctrl_handler ctrl_hdl_user_vid; @@ -153,6 +154,9 @@ struct vivid_dev { struct v4l2_ctrl_handler ctrl_hdl_radio_tx; struct video_device sdr_cap_dev; struct v4l2_ctrl_handler ctrl_hdl_sdr_cap; + struct video_device meta_cap_dev; + struct v4l2_ctrl_handler ctrl_hdl_meta_cap; + spinlock_t slock; struct mutex mutex; @@ -164,6 +168,7 @@ struct vivid_dev { u32 sdr_cap_caps; u32 radio_rx_caps; u32 radio_tx_caps; + u32 meta_cap_caps; /* supported features */ bool multiplanar; @@ -189,6 +194,7 @@ struct vivid_dev { bool has_radio_tx; bool has_sdr_cap; bool has_fb; + bool has_meta_cap; bool can_loop_video; @@ -390,6 +396,8 @@ struct vivid_dev { struct list_head vid_cap_active; struct vb2_queue vb_vbi_cap_q; struct list_head vbi_cap_active; + struct vb2_queue vb_meta_cap_q; + struct list_head meta_cap_active; /* thread for generating video capture stream */ struct task_struct *kthread_vid_cap; @@ -407,6 +415,9 @@ struct vivid_dev { u32 vbi_cap_seq_count; bool vbi_cap_streaming; bool stream_sliced_vbi_cap; + u32 meta_cap_seq_start; + u32 meta_cap_seq_count; + bool meta_cap_streaming; /* video output */ const struct vivid_fmt *fmt_out; @@ -527,6 +538,9 @@ struct vivid_dev { /* CEC OSD String */ char osd[14]; unsigned long osd_jiffies; + + bool meta_pts; + bool meta_scr; }; static inline bool vivid_is_webcam(const struct vivid_dev *dev) diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index cb19a9a73092..36e5944b51bb 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -94,6 +94,9 @@ #define VIVID_CID_SDR_CAP_FM_DEVIATION (VIVID_CID_VIVID_BASE + 110) +#define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111) +#define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112) + /* General User Controls */ static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) @@ -110,6 +113,7 @@ static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); + clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); break; case VIVID_CID_BUTTON: dev->button_pressed = 30; @@ -1421,6 +1425,47 @@ static const struct v4l2_ctrl_config vivid_ctrl_sdr_cap_fm_deviation = { .step = 1, }; +/* Metadata Capture Control */ + +static int vivid_meta_cap_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, + ctrl_hdl_meta_cap); + + switch (ctrl->id) { + case VIVID_CID_META_CAP_GENERATE_PTS: + dev->meta_pts = ctrl->val; + break; + case VIVID_CID_META_CAP_GENERATE_SCR: + dev->meta_scr = ctrl->val; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops vivid_meta_cap_ctrl_ops = { + .s_ctrl = vivid_meta_cap_s_ctrl, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_meta_has_pts = { + .ops = &vivid_meta_cap_ctrl_ops, + .id = VIVID_CID_META_CAP_GENERATE_PTS, + .name = "Generate PTS", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_meta_has_src_clk = { + .ops = &vivid_meta_cap_ctrl_ops, + .id = VIVID_CID_META_CAP_GENERATE_SCR, + .name = "Generate SCR", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; static const struct v4l2_ctrl_config vivid_ctrl_class = { .ops = &vivid_user_gen_ctrl_ops, @@ -1448,6 +1493,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, struct v4l2_ctrl_handler *hdl_radio_rx = &dev->ctrl_hdl_radio_rx; struct v4l2_ctrl_handler *hdl_radio_tx = &dev->ctrl_hdl_radio_tx; struct v4l2_ctrl_handler *hdl_sdr_cap = &dev->ctrl_hdl_sdr_cap; + struct v4l2_ctrl_handler *hdl_meta_cap = &dev->ctrl_hdl_meta_cap; + struct v4l2_ctrl_config vivid_ctrl_dv_timings = { .ops = &vivid_vid_cap_ctrl_ops, .id = VIVID_CID_DV_TIMINGS, @@ -1486,6 +1533,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_radio_tx, &vivid_ctrl_class, NULL); v4l2_ctrl_handler_init(hdl_sdr_cap, 19); v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_meta_cap, 2); + v4l2_ctrl_new_custom(hdl_meta_cap, &vivid_ctrl_class, NULL); /* User Controls */ dev->volume = v4l2_ctrl_new_std(hdl_user_aud, NULL, @@ -1743,6 +1792,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_sdr_cap_fm_deviation, NULL); } + if (dev->has_meta_cap) { + v4l2_ctrl_new_custom(hdl_meta_cap, + &vivid_ctrl_meta_has_pts, NULL); + v4l2_ctrl_new_custom(hdl_meta_cap, + &vivid_ctrl_meta_has_src_clk, NULL); + } + if (hdl_user_gen->error) return hdl_user_gen->error; if (hdl_user_vid->error) @@ -1817,6 +1873,14 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, return hdl_sdr_cap->error; dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap; } + if (dev->has_meta_cap) { + v4l2_ctrl_add_handler(hdl_meta_cap, hdl_user_gen, NULL, false); + v4l2_ctrl_add_handler(hdl_meta_cap, hdl_streaming, NULL, false); + if (hdl_meta_cap->error) + return hdl_meta_cap->error; + dev->meta_cap_dev.ctrl_handler = hdl_meta_cap; + } + return 0; } @@ -1836,4 +1900,5 @@ void vivid_free_controls(struct vivid_dev *dev) v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap); v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_cap); v4l2_ctrl_handler_free(&dev->ctrl_hdl_fb); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_cap); } diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index 003319d7816d..9f981e8bae6e 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -39,6 +39,7 @@ #include "vivid-osd.h" #include "vivid-ctrls.h" #include "vivid-kthread-cap.h" +#include "vivid-meta-cap.h" static inline v4l2_std_id vivid_get_std_cap(const struct vivid_dev *dev) { @@ -677,6 +678,7 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, { struct vivid_buffer *vid_cap_buf = NULL; struct vivid_buffer *vbi_cap_buf = NULL; + struct vivid_buffer *meta_cap_buf = NULL; u64 f_time = 0; dprintk(dev, 1, "Video Capture Thread Tick\n"); @@ -704,15 +706,19 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, list_del(&vbi_cap_buf->list); } } + if (!list_empty(&dev->meta_cap_active)) { + meta_cap_buf = list_entry(dev->meta_cap_active.next, + struct vivid_buffer, list); + list_del(&meta_cap_buf->list); + } + spin_unlock(&dev->slock); - if (!vid_cap_buf && !vbi_cap_buf) + if (!vid_cap_buf && !vbi_cap_buf && !meta_cap_buf) goto update_mv; f_time = dev->cap_frame_period * dev->vid_cap_seq_count + dev->cap_stream_start + dev->time_wrap_offset; - if (!dev->tstamp_src_is_soe) - f_time += dev->cap_frame_eof_offset; if (vid_cap_buf) { v4l2_ctrl_request_setup(vid_cap_buf->vb.vb2_buf.req_obj.req, @@ -735,6 +741,8 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, vid_cap_buf->vb.vb2_buf.index); vid_cap_buf->vb.vb2_buf.timestamp = f_time; + if (!dev->tstamp_src_is_soe) + vid_cap_buf->vb.vb2_buf.timestamp += dev->cap_frame_eof_offset; } if (vbi_cap_buf) { @@ -756,8 +764,22 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, /* If capturing a VBI, offset by 0.05 */ vbi_period = dev->cap_frame_period * 5; do_div(vbi_period, 100); - vbi_cap_buf->vb.vb2_buf.timestamp = f_time + vbi_period; + vbi_cap_buf->vb.vb2_buf.timestamp = f_time + dev->cap_frame_eof_offset + vbi_period; + } + + if (meta_cap_buf) { + v4l2_ctrl_request_setup(meta_cap_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_cap); + vivid_meta_cap_fillbuff(dev, meta_cap_buf, f_time); + v4l2_ctrl_request_complete(meta_cap_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_cap); + vb2_buffer_done(&meta_cap_buf->vb.vb2_buf, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "meta_cap %d done\n", + meta_cap_buf->vb.vb2_buf.index); + meta_cap_buf->vb.vb2_buf.timestamp = f_time + dev->cap_frame_eof_offset; } + dev->dqbuf_error = false; update_mv: @@ -835,6 +857,7 @@ static int vivid_thread_vid_cap(void *data) dev->cap_seq_count = buffers_since_start + dev->cap_seq_offset; dev->vid_cap_seq_count = dev->cap_seq_count - dev->vid_cap_seq_start; dev->vbi_cap_seq_count = dev->cap_seq_count - dev->vbi_cap_seq_start; + dev->meta_cap_seq_count = dev->cap_seq_count - dev->meta_cap_seq_start; vivid_thread_vid_cap_tick(dev, dropped_bufs); @@ -883,8 +906,10 @@ int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) if (pstreaming == &dev->vid_cap_streaming) dev->vid_cap_seq_start = seq_count; - else + else if (pstreaming == &dev->vbi_cap_streaming) dev->vbi_cap_seq_start = seq_count; + else + dev->meta_cap_seq_start = seq_count; *pstreaming = true; return 0; } @@ -894,6 +919,7 @@ int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) dev->vid_cap_seq_start = dev->seq_wrap * 128; dev->vbi_cap_seq_start = dev->seq_wrap * 128; + dev->meta_cap_seq_start = dev->seq_wrap * 128; dev->kthread_vid_cap = kthread_run(vivid_thread_vid_cap, dev, "%s-vid-cap", dev->v4l2_dev.name); @@ -951,7 +977,23 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) } } - if (dev->vid_cap_streaming || dev->vbi_cap_streaming) + if (pstreaming == &dev->meta_cap_streaming) { + while (!list_empty(&dev->meta_cap_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->meta_cap_active.next, + struct vivid_buffer, list); + list_del(&buf->list); + v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_cap); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "meta_cap buffer %d done\n", + buf->vb.vb2_buf.index); + } + } + + if (dev->vid_cap_streaming || dev->vbi_cap_streaming || + dev->meta_cap_streaming) return; /* shutdown control thread */ diff --git a/drivers/media/platform/vivid/vivid-meta-cap.c b/drivers/media/platform/vivid/vivid-meta-cap.c new file mode 100644 index 000000000000..780f96860a6d --- /dev/null +++ b/drivers/media/platform/vivid/vivid-meta-cap.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vivid-meta-cap.c - meta capture support functions. + */ + +#include +#include +#include +#include +#include + +#include "vivid-core.h" +#include "vivid-kthread-cap.h" +#include "vivid-meta-cap.h" + +static int meta_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned int size = sizeof(struct vivid_uvc_meta_buf); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + } else { + sizes[0] = size; + } + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = 1; + return 0; +} + +static int meta_cap_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = sizeof(struct vivid_uvc_meta_buf); + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void meta_cap_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->meta_cap_active); + spin_unlock(&dev->slock); +} + +static int meta_cap_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + dprintk(dev, 1, "%s\n", __func__); + dev->meta_cap_seq_count = 0; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else { + err = vivid_start_generating_vid_cap(dev, + &dev->meta_cap_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, + &dev->meta_cap_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void meta_cap_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + dprintk(dev, 1, "%s\n", __func__); + vivid_stop_generating_vid_cap(dev, &dev->meta_cap_streaming); +} + +static void meta_cap_buf_request_complete(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_meta_cap); +} + +const struct vb2_ops vivid_meta_cap_qops = { + .queue_setup = meta_cap_queue_setup, + .buf_prepare = meta_cap_buf_prepare, + .buf_queue = meta_cap_buf_queue, + .start_streaming = meta_cap_start_streaming, + .stop_streaming = meta_cap_stop_streaming, + .buf_request_complete = meta_cap_buf_request_complete, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (f->index > 0) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_META_CAPTURE; + f->pixelformat = V4L2_META_FMT_UVC; + return 0; +} + +int vidioc_g_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (!vivid_is_webcam(dev) || !dev->has_meta_cap) + return -EINVAL; + + meta->dataformat = V4L2_META_FMT_UVC; + meta->buffersize = sizeof(struct vivid_uvc_meta_buf); + return 0; +} + +void vivid_meta_cap_fillbuff(struct vivid_dev *dev, + struct vivid_buffer *buf, u64 soe) +{ + struct vivid_uvc_meta_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + int buf_off = 0; + + buf->vb.sequence = dev->meta_cap_seq_count; + if (dev->field_cap == V4L2_FIELD_ALTERNATE) + buf->vb.sequence /= 2; + memset(meta, 1, vb2_plane_size(&buf->vb.vb2_buf, 0)); + + meta->ns = ktime_get_ns(); + meta->sof = buf->vb.sequence * 30; + meta->length = sizeof(*meta) - offsetof(struct vivid_uvc_meta_buf, length); + meta->flags = UVC_STREAM_EOH | UVC_STREAM_EOF; + + if ((buf->vb.sequence % 2) == 0) + meta->flags |= UVC_STREAM_FID; + + dprintk(dev, 2, "%s ns:%llu sof:%4d len:%u flags: 0x%02x", + __func__, meta->ns, meta->sof, meta->length, meta->flags); + if (dev->meta_pts) { + meta->flags |= UVC_STREAM_PTS; + meta->buf[0] = div_u64(soe, VIVID_META_CLOCK_UNIT); + buf_off = 4; + dprintk(dev, 2, " pts: %u\n", *(__u32 *)(meta->buf)); + } + + if (dev->meta_scr) { + meta->flags |= UVC_STREAM_SCR; + meta->buf[buf_off] = div_u64((soe + dev->cap_frame_eof_offset), + VIVID_META_CLOCK_UNIT); + + meta->buf[buf_off + 4] = (buf->vb.sequence * 30) % 1000; + dprintk(dev, 2, " stc: %u, sof counter: %u\n", + *(__u32 *)(meta->buf + buf_off), + *(__u16 *)(meta->buf + buf_off + 4)); + } + dprintk(dev, 2, "\n"); +} diff --git a/drivers/media/platform/vivid/vivid-meta-cap.h b/drivers/media/platform/vivid/vivid-meta-cap.h new file mode 100644 index 000000000000..4670d00d1576 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-meta-cap.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * vivid-meta-cap.h - meta capture support functions. + */ +#ifndef _VIVID_META_CAP_H_ +#define _VIVID_META_CAP_H_ + +#define VIVID_META_CLOCK_UNIT 10 /* 100 MHz */ + +struct vivid_uvc_meta_buf { + __u64 ns; + __u16 sof; + __u8 length; + __u8 flags; + __u8 buf[10]; /* PTS(4)+STC(4)+SOF(2) */ +} __packed; + +void vivid_meta_cap_fillbuff(struct vivid_dev *dev, + struct vivid_buffer *buf, u64 soe); + +int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f); + +int vidioc_g_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_format *f); + +extern const struct vb2_ops vivid_meta_cap_qops; + +#endif diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index 2d030732feac..e94beef008c8 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -1356,7 +1356,9 @@ int vidioc_s_input(struct file *file, void *priv, unsigned i) if (i == dev->input) return 0; - if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q)) + if (vb2_is_busy(&dev->vb_vid_cap_q) || + vb2_is_busy(&dev->vb_vbi_cap_q) || + vb2_is_busy(&dev->vb_meta_cap_q)) return -EBUSY; dev->input = i; @@ -1366,6 +1368,7 @@ int vidioc_s_input(struct file *file, void *priv, unsigned i) dev->vid_cap_dev.tvnorms = V4L2_STD_ALL; } dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; + dev->meta_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; vivid_update_format_cap(dev, false); if (dev->colorspace) { -- cgit v1.2.3 From 78892b6ba3ba7a6c20bc21548ee90fc980fdfbf4 Mon Sep 17 00:00:00 2001 From: Vandana BN Date: Tue, 15 Oct 2019 07:40:16 -0300 Subject: media: v4l2-core: Add new metadata format Add new metadata format to support metadata output in vivid. Signed-off-by: Vandana BN Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 20b3107dd4e8..2753073cf340 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1340,6 +1340,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break; case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break; case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break; + case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break; default: /* Compressed formats */ -- cgit v1.2.3 From 746facd39370cc10038eba695f37269c6a401fda Mon Sep 17 00:00:00 2001 From: Vandana BN Date: Tue, 15 Oct 2019 07:40:17 -0300 Subject: media: vivid: Add metadata output support Support metadata output in vivid driver. Metadata output is used to set brightness, contrast, saturation and hue. Adds new files for metadata output. Signed-off-by: Vandana BN Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/Makefile | 2 +- drivers/media/platform/vivid/vivid-core.c | 98 ++++++++++++- drivers/media/platform/vivid/vivid-core.h | 10 ++ drivers/media/platform/vivid/vivid-ctrls.c | 12 +- drivers/media/platform/vivid/vivid-kthread-out.c | 49 ++++++- drivers/media/platform/vivid/vivid-meta-out.c | 174 +++++++++++++++++++++++ drivers/media/platform/vivid/vivid-meta-out.h | 25 ++++ drivers/media/platform/vivid/vivid-vid-out.c | 5 +- 8 files changed, 364 insertions(+), 11 deletions(-) create mode 100644 drivers/media/platform/vivid/vivid-meta-out.c create mode 100644 drivers/media/platform/vivid/vivid-meta-out.h (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile index af94abf9bce6..e8a50c506dc9 100644 --- a/drivers/media/platform/vivid/Makefile +++ b/drivers/media/platform/vivid/Makefile @@ -3,7 +3,7 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \ vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ - vivid-osd.o vivid-meta-cap.o + vivid-osd.o vivid-meta-cap.o vivid-meta-out.o ifeq ($(CONFIG_VIDEO_VIVID_CEC),y) vivid-objs += vivid-cec.o endif diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index a44984536ad0..dadfc59c92c5 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -38,6 +38,7 @@ #include "vivid-cec.h" #include "vivid-ctrls.h" #include "vivid-meta-cap.h" +#include "vivid-meta-out.h" #define VIVID_MODULE_NAME "vivid" @@ -84,6 +85,10 @@ static int meta_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; module_param_array(meta_cap_nr, int, NULL, 0444); MODULE_PARM_DESC(meta_cap_nr, " videoX start number, -1 is autodetect"); +static int meta_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(meta_out_nr, int, NULL, 0444); +MODULE_PARM_DESC(meta_out_nr, " videoX start number, -1 is autodetect"); + static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; module_param_array(ccs_cap_mode, int, NULL, 0444); MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n" @@ -105,10 +110,10 @@ MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 cr * vbi-out + vid-out + meta-cap */ static unsigned int node_types[VIVID_MAX_DEVS] = { - [0 ... (VIVID_MAX_DEVS - 1)] = 0x21d3d + [0 ... (VIVID_MAX_DEVS - 1)] = 0x61d3d }; module_param_array(node_types, uint, NULL, 0444); -MODULE_PARM_DESC(node_types, " node types, default is 0x21d3d. Bitmask with the following meaning:\n" +MODULE_PARM_DESC(node_types, " node types, default is 0x61d3d. Bitmask with the following meaning:\n" "\t\t bit 0: Video Capture node\n" "\t\t bit 2-3: VBI Capture node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" "\t\t bit 4: Radio Receiver node\n" @@ -117,7 +122,8 @@ MODULE_PARM_DESC(node_types, " node types, default is 0x21d3d. Bitmask with the "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" "\t\t bit 12: Radio Transmitter node\n" "\t\t bit 16: Framebuffer for testing overlays\n" - "\t\t bit 17: Metadata Capture node\n"); + "\t\t bit 17: Metadata Capture node\n" + "\t\t bit 18: Metadata Output node\n"); /* Default: 4 inputs */ static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 }; @@ -216,7 +222,8 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps | dev->vbi_cap_caps | dev->vbi_out_caps | dev->radio_rx_caps | dev->radio_tx_caps | - dev->sdr_cap_caps | dev->meta_cap_caps | V4L2_CAP_DEVICE_CAPS; + dev->sdr_cap_caps | dev->meta_cap_caps | + dev->meta_out_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -445,7 +452,8 @@ static bool vivid_is_last_user(struct vivid_dev *dev) vivid_is_in_use(&dev->sdr_cap_dev) + vivid_is_in_use(&dev->radio_rx_dev) + vivid_is_in_use(&dev->radio_tx_dev) + - vivid_is_in_use(&dev->meta_cap_dev); + vivid_is_in_use(&dev->meta_cap_dev) + + vivid_is_in_use(&dev->meta_out_dev); return uses == 1; } @@ -472,6 +480,7 @@ static int vivid_fop_release(struct file *file) set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags); } mutex_unlock(&dev->mutex); if (file->private_data == dev->overlay_cap_owner) @@ -622,6 +631,11 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_g_fmt_meta_cap = vidioc_g_fmt_meta_cap, .vidioc_s_fmt_meta_cap = vidioc_g_fmt_meta_cap, .vidioc_try_fmt_meta_cap = vidioc_g_fmt_meta_cap, + + .vidioc_enum_fmt_meta_out = vidioc_enum_fmt_meta_out, + .vidioc_g_fmt_meta_out = vidioc_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = vidioc_g_fmt_meta_out, + .vidioc_try_fmt_meta_out = vidioc_g_fmt_meta_out, }; /* ----------------------------------------------------------------- @@ -839,6 +853,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* do we create a meta capture device */ dev->has_meta_cap = node_type & 0x20000; + /* do we create a metadata output device */ + dev->has_meta_out = node_type & 0x40000; + /* end detecting feature set */ if (dev->has_vid_cap) { @@ -905,6 +922,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (in_type_counter[TV]) dev->meta_cap_caps |= V4L2_CAP_TUNER; } + /* set up the capabilities of meta output device */ + if (dev->has_meta_out) { + dev->meta_out_caps = V4L2_CAP_META_OUTPUT | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + if (dev->has_audio_outputs) + dev->meta_out_caps |= V4L2_CAP_AUDIO; + } ret = -ENOMEM; /* initialize the test pattern generator */ @@ -976,6 +1000,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_AUDOUT); v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_AUDOUT); v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_ENUMAUDOUT); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_S_AUDOUT); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_G_AUDOUT); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_ENUMAUDOUT); } if (!in_type_counter[TV] && !in_type_counter[SVID]) { v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_STD); @@ -1035,6 +1062,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMEINTERVALS); v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_FREQUENCY); v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(&dev->meta_out_dev, VIDIOC_G_FREQUENCY); /* configure internal data */ dev->fmt_cap = &vivid_formats[0]; @@ -1118,6 +1147,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) INIT_LIST_HEAD(&dev->vbi_out_active); INIT_LIST_HEAD(&dev->sdr_cap_active); INIT_LIST_HEAD(&dev->meta_cap_active); + INIT_LIST_HEAD(&dev->meta_out_active); INIT_LIST_HEAD(&dev->cec_work_list); spin_lock_init(&dev->cec_slock); @@ -1286,6 +1316,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto unreg_dev; } + if (dev->has_meta_out) { + /* initialize meta_out queue */ + q = &dev->vb_meta_out_q; + q->type = V4L2_BUF_TYPE_META_OUTPUT; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; + if (!allocator) + q->io_modes |= VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_meta_out_qops; + q->mem_ops = vivid_mem_ops[allocator]; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 1; + q->lock = &dev->mutex; + q->dev = dev->v4l2_dev.dev; + q->supports_requests = true; + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + #ifdef CONFIG_VIDEO_VIVID_CEC if (dev->has_vid_cap && in_type_counter[HDMI]) { struct cec_adapter *adap; @@ -1327,6 +1378,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_out); /* finally start creating the device nodes */ if (dev->has_vid_cap) { @@ -1583,6 +1635,36 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) video_device_node_name(vfd)); } + if (dev->has_meta_out) { + vfd = &dev->meta_out_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-meta-out", inst); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->device_caps = dev->meta_out_caps; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_meta_out_q; + vfd->lock = &dev->mutex; + vfd->tvnorms = tvnorms_out; + video_set_drvdata(vfd, dev); +#ifdef CONFIG_MEDIA_CONTROLLER + dev->meta_out_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&vfd->entity, 1, + &dev->meta_out_pad); + if (ret) + goto unreg_dev; +#endif + ret = video_register_device(vfd, VFL_TYPE_GRABBER, + meta_out_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, + "V4L2 metadata output device registered as %s\n", + video_device_node_name(vfd)); + } + #ifdef CONFIG_MEDIA_CONTROLLER /* Register the media device */ ret = media_device_register(&dev->mdev); @@ -1599,6 +1681,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) return 0; unreg_dev: + video_unregister_device(&dev->meta_out_dev); video_unregister_device(&dev->meta_cap_dev); video_unregister_device(&dev->radio_tx_dev); video_unregister_device(&dev->radio_rx_dev); @@ -1721,6 +1804,11 @@ static int vivid_remove(struct platform_device *pdev) video_device_node_name(&dev->meta_cap_dev)); video_unregister_device(&dev->meta_cap_dev); } + if (dev->has_meta_out) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->meta_out_dev)); + video_unregister_device(&dev->meta_out_dev); + } cec_unregister_adapter(dev->cec_rx_adap); for (j = 0; j < MAX_OUTPUTS; j++) cec_unregister_adapter(dev->cec_tx_adap[j]); diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index fd601345a17c..d57066ed31f0 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -132,6 +132,7 @@ struct vivid_dev { struct media_pad vbi_out_pad; struct media_pad sdr_cap_pad; struct media_pad meta_cap_pad; + struct media_pad meta_out_pad; #endif struct v4l2_ctrl_handler ctrl_hdl_user_gen; struct v4l2_ctrl_handler ctrl_hdl_user_vid; @@ -156,6 +157,8 @@ struct vivid_dev { struct v4l2_ctrl_handler ctrl_hdl_sdr_cap; struct video_device meta_cap_dev; struct v4l2_ctrl_handler ctrl_hdl_meta_cap; + struct video_device meta_out_dev; + struct v4l2_ctrl_handler ctrl_hdl_meta_out; spinlock_t slock; struct mutex mutex; @@ -169,6 +172,7 @@ struct vivid_dev { u32 radio_rx_caps; u32 radio_tx_caps; u32 meta_cap_caps; + u32 meta_out_caps; /* supported features */ bool multiplanar; @@ -195,6 +199,7 @@ struct vivid_dev { bool has_sdr_cap; bool has_fb; bool has_meta_cap; + bool has_meta_out; bool can_loop_video; @@ -432,6 +437,8 @@ struct vivid_dev { struct list_head vid_out_active; struct vb2_queue vb_vbi_out_q; struct list_head vbi_out_active; + struct vb2_queue vb_meta_out_q; + struct list_head meta_out_active; /* video loop precalculated rectangles */ @@ -472,6 +479,9 @@ struct vivid_dev { u32 vbi_out_seq_count; bool vbi_out_streaming; bool stream_sliced_vbi_out; + u32 meta_out_seq_start; + u32 meta_out_seq_count; + bool meta_out_streaming; /* SDR capture */ struct vb2_queue vb_sdr_cap_q; diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index 36e5944b51bb..b250fc3764e2 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -1494,6 +1494,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, struct v4l2_ctrl_handler *hdl_radio_tx = &dev->ctrl_hdl_radio_tx; struct v4l2_ctrl_handler *hdl_sdr_cap = &dev->ctrl_hdl_sdr_cap; struct v4l2_ctrl_handler *hdl_meta_cap = &dev->ctrl_hdl_meta_cap; + struct v4l2_ctrl_handler *hdl_meta_out = &dev->ctrl_hdl_meta_out; struct v4l2_ctrl_config vivid_ctrl_dv_timings = { .ops = &vivid_vid_cap_ctrl_ops, @@ -1535,6 +1536,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL); v4l2_ctrl_handler_init(hdl_meta_cap, 2); v4l2_ctrl_new_custom(hdl_meta_cap, &vivid_ctrl_class, NULL); + v4l2_ctrl_handler_init(hdl_meta_out, 2); + v4l2_ctrl_new_custom(hdl_meta_out, &vivid_ctrl_class, NULL); /* User Controls */ dev->volume = v4l2_ctrl_new_std(hdl_user_aud, NULL, @@ -1880,7 +1883,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, return hdl_meta_cap->error; dev->meta_cap_dev.ctrl_handler = hdl_meta_cap; } - + if (dev->has_meta_out) { + v4l2_ctrl_add_handler(hdl_meta_out, hdl_user_gen, NULL, false); + v4l2_ctrl_add_handler(hdl_meta_out, hdl_streaming, NULL, false); + if (hdl_meta_out->error) + return hdl_meta_out->error; + dev->meta_out_dev.ctrl_handler = hdl_meta_out; + } return 0; } @@ -1901,4 +1910,5 @@ void vivid_free_controls(struct vivid_dev *dev) v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_cap); v4l2_ctrl_handler_free(&dev->ctrl_hdl_fb); v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_cap); + v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_out); } diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c index ce5bcda2348c..c974235d7de3 100644 --- a/drivers/media/platform/vivid/vivid-kthread-out.c +++ b/drivers/media/platform/vivid/vivid-kthread-out.c @@ -38,11 +38,13 @@ #include "vivid-osd.h" #include "vivid-ctrls.h" #include "vivid-kthread-out.h" +#include "vivid-meta-out.h" static void vivid_thread_vid_out_tick(struct vivid_dev *dev) { struct vivid_buffer *vid_out_buf = NULL; struct vivid_buffer *vbi_out_buf = NULL; + struct vivid_buffer *meta_out_buf = NULL; dprintk(dev, 1, "Video Output Thread Tick\n"); @@ -69,9 +71,14 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev) struct vivid_buffer, list); list_del(&vbi_out_buf->list); } + if (!list_empty(&dev->meta_out_active)) { + meta_out_buf = list_entry(dev->meta_out_active.next, + struct vivid_buffer, list); + list_del(&meta_out_buf->list); + } spin_unlock(&dev->slock); - if (!vid_out_buf && !vbi_out_buf) + if (!vid_out_buf && !vbi_out_buf && !meta_out_buf) return; if (vid_out_buf) { @@ -111,6 +118,21 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev) dprintk(dev, 2, "vbi_out buffer %d done\n", vbi_out_buf->vb.vb2_buf.index); } + if (meta_out_buf) { + v4l2_ctrl_request_setup(meta_out_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_out); + v4l2_ctrl_request_complete(meta_out_buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_out); + vivid_meta_out_process(dev, meta_out_buf); + meta_out_buf->vb.sequence = dev->meta_out_seq_count; + meta_out_buf->vb.vb2_buf.timestamp = + ktime_get_ns() + dev->time_wrap_offset; + vb2_buffer_done(&meta_out_buf->vb.vb2_buf, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dprintk(dev, 2, "meta_out buffer %d done\n", + meta_out_buf->vb.vb2_buf.index); + } + dev->dqbuf_error = false; } @@ -136,6 +158,7 @@ static int vivid_thread_vid_out(void *data) dev->out_seq_count = 0xffffff80U; dev->jiffies_vid_out = jiffies; dev->vid_out_seq_start = dev->vbi_out_seq_start = 0; + dev->meta_out_seq_start = 0; dev->out_seq_resync = false; for (;;) { @@ -178,6 +201,7 @@ static int vivid_thread_vid_out(void *data) dev->out_seq_count = buffers_since_start + dev->out_seq_offset; dev->vid_out_seq_count = dev->out_seq_count - dev->vid_out_seq_start; dev->vbi_out_seq_count = dev->out_seq_count - dev->vbi_out_seq_start; + dev->meta_out_seq_count = dev->out_seq_count - dev->meta_out_seq_start; vivid_thread_vid_out_tick(dev); mutex_unlock(&dev->mutex); @@ -229,8 +253,10 @@ int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) if (pstreaming == &dev->vid_out_streaming) dev->vid_out_seq_start = seq_count; - else + else if (pstreaming == &dev->vbi_out_streaming) dev->vbi_out_seq_start = seq_count; + else + dev->meta_out_seq_start = seq_count; *pstreaming = true; return 0; } @@ -239,6 +265,7 @@ int vivid_start_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) dev->jiffies_vid_out = jiffies; dev->vid_out_seq_start = dev->seq_wrap * 128; dev->vbi_out_seq_start = dev->seq_wrap * 128; + dev->meta_out_seq_start = dev->seq_wrap * 128; dev->kthread_vid_out = kthread_run(vivid_thread_vid_out, dev, "%s-vid-out", dev->v4l2_dev.name); @@ -296,7 +323,23 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) } } - if (dev->vid_out_streaming || dev->vbi_out_streaming) + if (pstreaming == &dev->meta_out_streaming) { + while (!list_empty(&dev->meta_out_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->meta_out_active.next, + struct vivid_buffer, list); + list_del(&buf->list); + v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req, + &dev->ctrl_hdl_meta_out); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dprintk(dev, 2, "meta_out buffer %d done\n", + buf->vb.vb2_buf.index); + } + } + + if (dev->vid_out_streaming || dev->vbi_out_streaming || + dev->meta_out_streaming) return; /* shutdown control thread */ diff --git a/drivers/media/platform/vivid/vivid-meta-out.c b/drivers/media/platform/vivid/vivid-meta-out.c new file mode 100644 index 000000000000..ff8a039aba72 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-meta-out.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vivid-meta-out.c - meta output support functions. + */ + +#include +#include +#include +#include +#include + +#include "vivid-core.h" +#include "vivid-kthread-out.h" +#include "vivid-meta-out.h" + +static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + unsigned int size = sizeof(struct vivid_meta_out_buf); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + } else { + sizes[0] = size; + } + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2 - vq->num_buffers; + + *nplanes = 1; + return 0; +} + +static int meta_out_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = sizeof(struct vivid_meta_out_buf); + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void meta_out_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->meta_out_active); + spin_unlock(&dev->slock); +} + +static int meta_out_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err; + + dprintk(dev, 1, "%s\n", __func__); + dev->meta_out_seq_count = 0; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else { + err = vivid_start_generating_vid_out(dev, + &dev->meta_out_streaming); + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, + &dev->meta_out_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void meta_out_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + dprintk(dev, 1, "%s\n", __func__); + vivid_stop_generating_vid_out(dev, &dev->meta_out_streaming); +} + +static void meta_out_buf_request_complete(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_meta_out); +} + +const struct vb2_ops vivid_meta_out_qops = { + .queue_setup = meta_out_queue_setup, + .buf_prepare = meta_out_buf_prepare, + .buf_queue = meta_out_buf_queue, + .start_streaming = meta_out_start_streaming, + .stop_streaming = meta_out_stop_streaming, + .buf_request_complete = meta_out_buf_request_complete, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int vidioc_enum_fmt_meta_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vivid_dev *dev = video_drvdata(file); + + if (!vivid_is_webcam(dev)) + return -EINVAL; + + if (f->index > 0) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_META_OUTPUT; + f->pixelformat = V4L2_META_FMT_VIVID; + return 0; +} + +int vidioc_g_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (!vivid_is_webcam(dev) || !dev->has_meta_out) + return -EINVAL; + + meta->dataformat = V4L2_META_FMT_VIVID; + meta->buffersize = sizeof(struct vivid_meta_out_buf); + return 0; +} + +void vivid_meta_out_process(struct vivid_dev *dev, + struct vivid_buffer *buf) +{ + struct vivid_meta_out_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + + tpg_s_brightness(&dev->tpg, meta->brightness); + tpg_s_contrast(&dev->tpg, meta->contrast); + tpg_s_saturation(&dev->tpg, meta->saturation); + tpg_s_hue(&dev->tpg, meta->hue); + dprintk(dev, 2, " %s brightness %u contrast %u saturation %u hue %d\n", + __func__, meta->brightness, meta->contrast, + meta->saturation, meta->hue); +} diff --git a/drivers/media/platform/vivid/vivid-meta-out.h b/drivers/media/platform/vivid/vivid-meta-out.h new file mode 100644 index 000000000000..0c639b7c2842 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-meta-out.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * vivid-meta-out.h - meta output support functions. + */ +#ifndef _VIVID_META_OUT_H_ +#define _VIVID_META_OUT_H_ + +struct vivid_meta_out_buf { + u16 brightness; + u16 contrast; + u16 saturation; + s16 hue; +}; + +void vivid_meta_out_process(struct vivid_dev *dev, struct vivid_buffer *buf); +int vidioc_enum_fmt_meta_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f); +int vidioc_g_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f); +int vidioc_s_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f); + +extern const struct vb2_ops vivid_meta_out_qops; + +#endif diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index a0364ac497f9..ee3446e3217c 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -1079,7 +1079,9 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o) if (o == dev->output) return 0; - if (vb2_is_busy(&dev->vb_vid_out_q) || vb2_is_busy(&dev->vb_vbi_out_q)) + if (vb2_is_busy(&dev->vb_vid_out_q) || + vb2_is_busy(&dev->vb_vbi_out_q) || + vb2_is_busy(&dev->vb_meta_out_q)) return -EBUSY; dev->output = o; @@ -1090,6 +1092,7 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o) dev->vid_out_dev.tvnorms = 0; dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms; + dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms; vivid_update_format_out(dev); v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev)); -- cgit v1.2.3 From 256fa3920874b0f1f4cb79ad6766493a22187153 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Tue, 22 Oct 2019 12:26:52 -0300 Subject: media: v4l: Add definitions for HEVC stateless decoding This introduces the required definitions for HEVC decoding support with stateless VPUs. The controls associated to the HEVC slice format provide the required meta-data for decoding slices extracted from the bitstream. They are not exported to the public V4L2 API since reworking this API will likely be needed for covering various use-cases and new hardware. Multi-slice decoding is exposed as a valid decoding mode to match current H.264 support but it is not yet implemented. The interface comes with the following limitations: * No custom quantization matrices (scaling lists); * Support for a single temporal layer only; * No slice entry point offsets support; * No conformance window support; * No VUI parameters support; * No support for SPS extensions: range, multilayer, 3d, scc, 4 bits; * No support for PPS extensions: range, multilayer, 3d, scc, 4 bits. Signed-off-by: Paul Kocialkowski [hverkuil-cisco@xs4all.nl: use 1ULL in flags defines in hevc-ctrls.h] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 109 ++++++++++++++++++++++++++++++++++- drivers/media/v4l2-core/v4l2-ioctl.c | 1 + 2 files changed, 109 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index bf50d37ef6c1..b4caf2d4d076 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -568,6 +568,16 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Disabled at slice boundary", "NULL", }; + static const char * const hevc_decode_mode[] = { + "Slice-Based", + "Frame-Based", + NULL, + }; + static const char * const hevc_start_code[] = { + "No Start Code", + "Annex B Start Code", + NULL, + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -689,7 +699,10 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return hevc_tier; case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: return hevc_loop_filter_mode; - + case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: + return hevc_decode_mode; + case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: + return hevc_start_code; default: return NULL; } @@ -959,6 +972,11 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field"; case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame"; case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR"; + case V4L2_CID_MPEG_VIDEO_HEVC_SPS: return "HEVC Sequence Parameter Set"; + case V4L2_CID_MPEG_VIDEO_HEVC_PPS: return "HEVC Picture Parameter Set"; + case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters"; + case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: return "HEVC Decode Mode"; + case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return "HEVC Start Code"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -1268,6 +1286,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: case V4L2_CID_MPEG_VIDEO_HEVC_TIER: case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: + case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -1378,6 +1398,15 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER: *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER; break; + case V4L2_CID_MPEG_VIDEO_HEVC_SPS: + *type = V4L2_CTRL_TYPE_HEVC_SPS; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PPS: + *type = V4L2_CTRL_TYPE_HEVC_PPS; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: + *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS; + break; case V4L2_CID_UNIT_CELL_SIZE: *type = V4L2_CTRL_TYPE_AREA; *flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -1682,8 +1711,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, { struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; + struct v4l2_ctrl_hevc_sps *p_hevc_sps; + struct v4l2_ctrl_hevc_pps *p_hevc_pps; + struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; struct v4l2_area *area; void *p = ptr.p + idx * ctrl->elem_size; + unsigned int i; switch ((u32)ctrl->type) { case V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS: @@ -1759,11 +1792,76 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, zero_padding(p_vp8_frame_header->entropy_header); zero_padding(p_vp8_frame_header->coder_state); break; + + case V4L2_CTRL_TYPE_HEVC_SPS: + p_hevc_sps = p; + + if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) { + p_hevc_sps->pcm_sample_bit_depth_luma_minus1 = 0; + p_hevc_sps->pcm_sample_bit_depth_chroma_minus1 = 0; + p_hevc_sps->log2_min_pcm_luma_coding_block_size_minus3 = 0; + p_hevc_sps->log2_diff_max_min_pcm_luma_coding_block_size = 0; + } + + if (!(p_hevc_sps->flags & + V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT)) + p_hevc_sps->num_long_term_ref_pics_sps = 0; + break; + + case V4L2_CTRL_TYPE_HEVC_PPS: + p_hevc_pps = p; + + if (!(p_hevc_pps->flags & + V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED)) + p_hevc_pps->diff_cu_qp_delta_depth = 0; + + if (!(p_hevc_pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)) { + p_hevc_pps->num_tile_columns_minus1 = 0; + p_hevc_pps->num_tile_rows_minus1 = 0; + memset(&p_hevc_pps->column_width_minus1, 0, + sizeof(p_hevc_pps->column_width_minus1)); + memset(&p_hevc_pps->row_height_minus1, 0, + sizeof(p_hevc_pps->row_height_minus1)); + + p_hevc_pps->flags &= + ~V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED; + } + + if (p_hevc_pps->flags & + V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER) { + p_hevc_pps->pps_beta_offset_div2 = 0; + p_hevc_pps->pps_tc_offset_div2 = 0; + } + + zero_padding(*p_hevc_pps); + break; + + case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + p_hevc_slice_params = p; + + if (p_hevc_slice_params->num_active_dpb_entries > + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; + + zero_padding(p_hevc_slice_params->pred_weight_table); + + for (i = 0; i < p_hevc_slice_params->num_active_dpb_entries; + i++) { + struct v4l2_hevc_dpb_entry *dpb_entry = + &p_hevc_slice_params->dpb[i]; + + zero_padding(*dpb_entry); + } + + zero_padding(*p_hevc_slice_params); + break; + case V4L2_CTRL_TYPE_AREA: area = p; if (!area->width || !area->height) return -EINVAL; break; + default: return -EINVAL; } @@ -2438,6 +2536,15 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header); break; + case V4L2_CTRL_TYPE_HEVC_SPS: + elem_size = sizeof(struct v4l2_ctrl_hevc_sps); + break; + case V4L2_CTRL_TYPE_HEVC_PPS: + elem_size = sizeof(struct v4l2_ctrl_hevc_pps); + break; + case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params); + break; case V4L2_CTRL_TYPE_AREA: elem_size = sizeof(struct v4l2_area); break; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2753073cf340..d26c83d4c255 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1367,6 +1367,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_VP8_FRAME: descr = "VP8 Frame"; break; case V4L2_PIX_FMT_VP9: descr = "VP9"; break; case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */ + case V4L2_PIX_FMT_HEVC_SLICE: descr = "HEVC Parsed Slice Data"; break; case V4L2_PIX_FMT_FWHT: descr = "FWHT"; break; /* used in vicodec */ case V4L2_PIX_FMT_FWHT_STATELESS: descr = "FWHT Stateless"; break; /* used in vicodec */ case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break; -- cgit v1.2.3 From 0e78795e95c540bc49c2e094103932eb6a06e6c8 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Fri, 13 Sep 2019 15:11:04 -0300 Subject: media: aspeed: refine hsync/vsync polarity setting logic To prevent inaccurate detections of resolution, this commit enables clearing of hsync/vsync polarity bits based on probed sync state. Signed-off-by: Jae Hyun Yoo Reviewed-by: Eddie James Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 43 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index eb12f3793062..8f77079da55a 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -614,7 +614,7 @@ static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) int i; int hsync_counter = 0; int vsync_counter = 0; - u32 sts; + u32 sts, ctrl; for (i = 0; i < NUM_POLARITY_CHECKS; ++i) { sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS); @@ -629,30 +629,29 @@ static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) hsync_counter++; } - if (hsync_counter < 0 || vsync_counter < 0) { - u32 ctrl = 0; + ctrl = aspeed_video_read(video, VE_CTRL); - if (hsync_counter < 0) { - ctrl = VE_CTRL_HSYNC_POL; - video->detected_timings.polarities &= - ~V4L2_DV_HSYNC_POS_POL; - } else { - video->detected_timings.polarities |= - V4L2_DV_HSYNC_POS_POL; - } - - if (vsync_counter < 0) { - ctrl = VE_CTRL_VSYNC_POL; - video->detected_timings.polarities &= - ~V4L2_DV_VSYNC_POS_POL; - } else { - video->detected_timings.polarities |= - V4L2_DV_VSYNC_POS_POL; - } + if (hsync_counter < 0) { + ctrl |= VE_CTRL_HSYNC_POL; + video->detected_timings.polarities &= + ~V4L2_DV_HSYNC_POS_POL; + } else { + ctrl &= ~VE_CTRL_HSYNC_POL; + video->detected_timings.polarities |= + V4L2_DV_HSYNC_POS_POL; + } - if (ctrl) - aspeed_video_update(video, VE_CTRL, 0, ctrl); + if (vsync_counter < 0) { + ctrl |= VE_CTRL_VSYNC_POL; + video->detected_timings.polarities &= + ~V4L2_DV_VSYNC_POS_POL; + } else { + ctrl &= ~VE_CTRL_VSYNC_POL; + video->detected_timings.polarities |= + V4L2_DV_VSYNC_POS_POL; } + + aspeed_video_write(video, VE_CTRL, ctrl); } static bool aspeed_video_alloc_buf(struct aspeed_video *video, -- cgit v1.2.3 From 5b3f3c41c5c791c1c22cd91655e7ef4b2a1dff7c Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Fri, 13 Sep 2019 15:11:05 -0300 Subject: media: aspeed: set hsync and vsync polarities to normal before starting mode detection Sometimes it detects a weird resolution such as 1024x287 when the actual resolution is 1024x768. To resolve such an issue, this commit adds clearing for hsync and vsync polarity register bits at the beginning of the first mode detection. This is recommended in the datasheet. Signed-off-by: Jae Hyun Yoo Reviewed-by: Eddie James Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index 8f77079da55a..929b3a5b8849 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -740,6 +740,8 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) } set_bit(VIDEO_RES_DETECT, &video->flags); + aspeed_video_update(video, VE_CTRL, + VE_CTRL_VSYNC_POL | VE_CTRL_HSYNC_POL, 0); aspeed_video_enable_mode_detect(video); rc = wait_event_interruptible_timeout(video->wait, -- cgit v1.2.3 From 65d270acb2d662c3346793663ac3a759eb4491b8 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Thu, 26 Sep 2019 19:27:43 -0300 Subject: media: aspeed: clear garbage interrupts CAPTURE_COMPLETE and FRAME_COMPLETE interrupts come even when these are disabled in the VE_INTERRUPT_CTRL register and eventually this behavior causes disabling irq itself like below: [10055.108784] irq 23: nobody cared (try booting with the "irqpoll" option) [10055.115525] CPU: 0 PID: 331 Comm: swampd Tainted: G W 5.3.0-4fde000-dirty-d683e2e #1 [10055.124565] Hardware name: Generic DT based system [10055.129355] Backtrace: [10055.131854] [<80107d7c>] (dump_backtrace) from [<80107fb0>] (show_stack+0x20/0x24) [10055.139431] r7:00000017 r6:00000001 r5:00000000 r4:9d51dc00 [10055.145120] [<80107f90>] (show_stack) from [<8074bf50>] (dump_stack+0x20/0x28) [10055.152361] [<8074bf30>] (dump_stack) from [<80150ffc>] (__report_bad_irq+0x40/0xc0) [10055.160109] [<80150fbc>] (__report_bad_irq) from [<80150f2c>] (note_interrupt+0x23c/0x294) [10055.168374] r9:015b6e60 r8:00000000 r7:00000017 r6:00000001 r5:00000000 r4:9d51dc00 [10055.176136] [<80150cf0>] (note_interrupt) from [<8014df1c>] (handle_irq_event_percpu+0x88/0x98) [10055.184835] r10:7eff7910 r9:015b6e60 r8:00000000 r7:9d417600 r6:00000001 r5:00000002 [10055.192657] r4:9d51dc00 r3:00000000 [10055.196248] [<8014de94>] (handle_irq_event_percpu) from [<8014df64>] (handle_irq_event+0x38/0x4c) [10055.205113] r5:80b56d50 r4:9d51dc00 [10055.208697] [<8014df2c>] (handle_irq_event) from [<80151f1c>] (handle_level_irq+0xbc/0x12c) [10055.217037] r5:80b56d50 r4:9d51dc00 [10055.220623] [<80151e60>] (handle_level_irq) from [<8014d4b8>] (generic_handle_irq+0x30/0x44) [10055.229052] r5:80b56d50 r4:00000017 [10055.232648] [<8014d488>] (generic_handle_irq) from [<8014d524>] (__handle_domain_irq+0x58/0xb4) [10055.241356] [<8014d4cc>] (__handle_domain_irq) from [<801021e4>] (avic_handle_irq+0x68/0x70) [10055.249797] r9:015b6e60 r8:00c5387d r7:00c5387d r6:ffffffff r5:9dd33fb0 r4:9d402380 [10055.257539] [<8010217c>] (avic_handle_irq) from [<80101e34>] (__irq_usr+0x54/0x80) [10055.265105] Exception stack(0x9dd33fb0 to 0x9dd33ff8) [10055.270152] 3fa0: 015d0530 00000000 00000000 015d0538 [10055.278328] 3fc0: 015d0530 015b6e60 00000000 00000000 0052c5d0 015b6e60 7eff7910 7eff7918 [10055.286496] 3fe0: 76ce5614 7eff7908 0050e2f4 76a3a08c 20000010 ffffffff [10055.293104] r5:20000010 r4:76a3a08c [10055.296673] handlers: [10055.298967] [<79f218a5>] irq_default_primary_handler threaded [<1de88514>] aspeed_video_irq [10055.307344] Disabling IRQ #23 To fix this issue, this commit makes the interrupt handler clear these garbage interrupts. This driver enables and uses only COMP_COMPLETE interrupt instead for frame handling. Signed-off-by: Jae Hyun Yoo Reviewed-by: Eddie James Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index 929b3a5b8849..db45502774b1 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -606,6 +606,16 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) aspeed_video_start_frame(video); } + /* + * CAPTURE_COMPLETE and FRAME_COMPLETE interrupts come even when these + * are disabled in the VE_INTERRUPT_CTRL register so clear them to + * prevent unnecessary interrupt calls. + */ + if (sts & VE_INTERRUPT_CAPTURE_COMPLETE) + sts &= ~VE_INTERRUPT_CAPTURE_COMPLETE; + if (sts & VE_INTERRUPT_FRAME_COMPLETE) + sts &= ~VE_INTERRUPT_FRAME_COMPLETE; + return sts ? IRQ_NONE : IRQ_HANDLED; } -- cgit v1.2.3 From 69e3235d58260d3d03570271da314829f9486237 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 9 Oct 2019 11:49:19 -0300 Subject: media: cec-pin: add 'received' callback Drivers that use the CEC pin framework have no way of processing messages themselves by providing the 'received' callback. This is present in cec_ops, but not in cec_pin_ops. Add support for this callback. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-pin.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c index 8f987bc0dd88..660fe111f540 100644 --- a/drivers/media/cec/cec-pin.c +++ b/drivers/media/cec/cec-pin.c @@ -1279,6 +1279,15 @@ static void cec_pin_adap_free(struct cec_adapter *adap) kfree(pin); } +static int cec_pin_received(struct cec_adapter *adap, struct cec_msg *msg) +{ + struct cec_pin *pin = adap->pin; + + if (pin->ops->received) + return pin->ops->received(adap, msg); + return -ENOMSG; +} + void cec_pin_changed(struct cec_adapter *adap, bool value) { struct cec_pin *pin = adap->pin; @@ -1301,6 +1310,7 @@ static const struct cec_adap_ops cec_pin_adap_ops = { .error_inj_parse_line = cec_pin_error_inj_parse_line, .error_inj_show = cec_pin_error_inj_show, #endif + .received = cec_pin_received, }; struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops, -- cgit v1.2.3 From 2289adbfa559050d2a38bcd9caac1c18b800e928 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Wed, 9 Oct 2019 12:01:47 -0300 Subject: media: usb: fix memory leak in af9005_identify_state In af9005_identify_state when returning -EIO the allocated buffer should be released. Replace the "return -EIO" with assignment into ret and move deb_info() under a check. Fixes: af4e067e1dcf ("V4L/DVB (5625): Add support for the AF9005 demodulator from Afatech") Signed-off-by: Navid Emamdoost Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/af9005.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c index 02697d86e8c1..ac93e88d7038 100644 --- a/drivers/media/usb/dvb-usb/af9005.c +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -976,8 +976,9 @@ static int af9005_identify_state(struct usb_device *udev, else if (reply == 0x02) *cold = 0; else - return -EIO; - deb_info("Identify state cold = %d\n", *cold); + ret = -EIO; + if (!ret) + deb_info("Identify state cold = %d\n", *cold); err: kfree(buf); -- cgit v1.2.3 From b19c25f46745cd57715c32d3ace9c79d1426bebd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 11 Oct 2019 07:49:19 -0300 Subject: media: adv7180: Only print 'chip found' message on successful probe Currently the "chip found" message is shown even in the case where the I2C address is wrongly passed in the device tree, or also in the case of probe failure, which is misleading. To avoid such problem, move this message after real I2C transactions have been successfully made and we are certain that the adv7180 is really present and probed. Signed-off-by: Fabio Estevam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index e780969cc2f2..6528e2343fc8 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1309,9 +1309,6 @@ static int adv7180_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); - state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); if (state == NULL) return -ENOMEM; @@ -1382,6 +1379,9 @@ static int adv7180_probe(struct i2c_client *client, if (ret) goto err_free_irq; + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr, client->adapter->name); + return 0; err_free_irq: -- cgit v1.2.3 From 05ff862e0ca5895174c2f28bc10f6dc3495ac0bd Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 11 Oct 2019 10:29:40 -0300 Subject: media: ti-vpe: vpe: use r2y instead of y2r, copy-paste error There appears to be a copy-paste error on the access of csc_coeffs.y2r.r601.full.coeff, I believe csc_coeffs.2yr.r601.full.coeff should be used instead. This is a moot point as the code is never reached, but at least use the correct structure element. Addresses-Coverity: ("Copy-paste error") Fixes: 3ff3a712a9ea ("media: ti-vpe: vpe: don't rely on colorspace member for conversion") Signed-off-by: Colin Ian King Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/csc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c index bd923bee4a31..834114a4eebe 100644 --- a/drivers/media/platform/ti-vpe/csc.c +++ b/drivers/media/platform/ti-vpe/csc.c @@ -238,7 +238,7 @@ void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, coeff = csc_coeffs.r2y.r709.limited.coeff; } else { /* Should never reach this, but it keeps gcc happy */ - coeff = csc_coeffs.y2r.r601.full.coeff; + coeff = csc_coeffs.r2y.r601.full.coeff; } } else { *csc_reg5 |= CSC_BYPASS; -- cgit v1.2.3 From 9b950ce05732ac103023c2a1a3b81778f4c06e2f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 11 Oct 2019 15:57:46 -0300 Subject: media: gspca: remove redundant assignment to variable ret The variable ret is being initialized with a value that is never read and is being re-assigned a little later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/stv0680.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/stv0680.c b/drivers/media/usb/gspca/stv0680.c index f869eb6065ce..b23988d8c7bc 100644 --- a/drivers/media/usb/gspca/stv0680.c +++ b/drivers/media/usb/gspca/stv0680.c @@ -35,7 +35,7 @@ struct sd { static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, int size) { - int ret = -1; + int ret; u8 req_type = 0; unsigned int pipe = 0; -- cgit v1.2.3 From 4e383575a1313fbb706b141124053caaf13eb407 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 11 Oct 2019 17:48:29 -0300 Subject: media: i2c: adv7842: make array cri static and const, makes object smaller Don't populate the array 'cri' on the stack but instead make it static and const. Makes the object code smaller by 165 bytes. Turn the 2nd parameter of 'log_infoframe()' const accordingly. Before: text data bss dec hex filename 98533 20024 256 118813 1d01d drivers/media/i2c/adv7842.o After: text data bss dec hex filename 98304 20088 256 118648 1cf78 drivers/media/i2c/adv7842.o Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7842.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 885619841719..0855f648416d 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2547,7 +2547,7 @@ struct adv7842_cfg_read_infoframe { u8 payload_addr; }; -static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infoframe *cri) +static void log_infoframe(struct v4l2_subdev *sd, const struct adv7842_cfg_read_infoframe *cri) { int i; u8 buffer[32]; @@ -2585,7 +2585,7 @@ static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infofr static void adv7842_log_infoframes(struct v4l2_subdev *sd) { int i; - struct adv7842_cfg_read_infoframe cri[] = { + static const struct adv7842_cfg_read_infoframe cri[] = { { "AVI", 0x01, 0xe0, 0x00 }, { "Audio", 0x02, 0xe3, 0x1c }, { "SDP", 0x04, 0xe6, 0x2a }, -- cgit v1.2.3 From cfc4652dee4a9435197e9859322ceef60a900e4d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 15 Oct 2019 16:18:17 -0300 Subject: media: cec-gpio: Use CONFIG_PREEMPTION CONFIG_PREEMPTION is selected by CONFIG_PREEMPT and by CONFIG_PREEMPT_RT. Both PREEMPT and PREEMPT_RT require the same functionality which today depends on CONFIG_PREEMPT. Switch the Kconfig dependency to CONFIG_PREEMPTION. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 997de1a83ff9..54ed2787bcc6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -586,7 +586,7 @@ config VIDEO_MESON_G12A_AO_CEC config CEC_GPIO tristate "Generic GPIO-based CEC driver" - depends on PREEMPT || COMPILE_TEST + depends on PREEMPTION || COMPILE_TEST select CEC_CORE select CEC_PIN select CEC_NOTIFIER -- cgit v1.2.3 From 57822068dd120386b98891cb151dc20107b63ba7 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sun, 13 Oct 2019 09:50:45 -0300 Subject: media: ov6650: Fix stored frame interval not in sync with hardware The driver stores a frame interval value supposed to be in line with hardware state in a device private structure. Since the driver initial submission, the respective field of the structure has never been initialised on device probe. Moreover, if updated from .s_frame_interval(), a new value is stored before it is applied on hardware. If an error occurs during device update, the stored value may no longer reflect hardware state and consecutive calls to .g_frame_interval() may return incorrect information. Assuming a failed update of the device means its actual state hasn't changed, update the frame interval field of the device private structure with a new value only after it is successfully applied on hardware so it always reflects actual hardware state to the extent possible. Also, initialise the field with hardware default frame interval on device probe. Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 16887049f0cd..f60aeb1f7813 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -130,6 +130,7 @@ #define CLKRC_24MHz 0xc0 #define CLKRC_DIV_MASK 0x3f #define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1) +#define DEF_CLKRC 0x00 #define COMA_RESET BIT(7) #define COMA_QCIF BIT(5) @@ -783,19 +784,17 @@ static int ov6650_s_frame_interval(struct v4l2_subdev *sd, else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK)) div = GET_CLKRC_DIV(CLKRC_DIV_MASK); - /* - * Keep result to be used as tpf limit - * for subsequent clock divider calculations - */ - priv->tpf.numerator = div; - priv->tpf.denominator = FRAME_RATE_MAX; + tpf->numerator = div; + tpf->denominator = FRAME_RATE_MAX; - clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); + clkrc = to_clkrc(tpf, priv->pclk_limit, priv->pclk_max); ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK); if (!ret) { - tpf->numerator = GET_CLKRC_DIV(clkrc); - tpf->denominator = FRAME_RATE_MAX; + priv->tpf.numerator = GET_CLKRC_DIV(clkrc); + priv->tpf.denominator = FRAME_RATE_MAX; + + *tpf = priv->tpf; } return ret; @@ -1038,6 +1037,10 @@ static int ov6650_probe(struct i2c_client *client, priv->rect.width = W_CIF; priv->rect.height = H_CIF; + /* Hardware default frame interval */ + priv->tpf.numerator = GET_CLKRC_DIV(DEF_CLKRC); + priv->tpf.denominator = FRAME_RATE_MAX; + priv->subdev.internal_ops = &ov6650_internal_ops; ret = v4l2_async_register_subdev(&priv->subdev); -- cgit v1.2.3 From b1c5794382208c455c4e511c3f0b55565b69dd53 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sun, 13 Oct 2019 09:50:46 -0300 Subject: media: ov6650: Drop obsolete .pclk_limit attribute That attribute used to be obtained from platform data by a soc_camera host interface and passed to the sensor driver for .s_mbus_fmt() video operation callback, later reused as .set_fmt() pad operation callback, to be able to limit frame rate. The driver stored that value in its private structure for further use from .g/s_parm(), later converted to g/s_frame_interval(). On conversion of the driver from soc_camera sensor to a standalone V4L2 subdevice by commit 23a52386fabe ("media: ov6650: convert to standalone v4l2 subdevice"), that attribute had been replaced by a constant and hardcoded to an arbitrarily chosen pixel clock limit. Drop it. Host interfaces can limit frame rate if needed by calling .s_frame_interval(). Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index f60aeb1f7813..a50244401491 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -197,7 +197,6 @@ struct ov6650 { struct v4l2_clk *clk; bool half_scale; /* scale down output by 2 */ struct v4l2_rect rect; /* sensor cropping window */ - unsigned long pclk_limit; /* from host */ unsigned long pclk_max; /* from resolution and format */ struct v4l2_fract tpf; /* as requested with s_frame_interval */ u32 code; @@ -546,8 +545,7 @@ static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect) return width > rect->width >> 1 || height > rect->height >> 1; } -static u8 to_clkrc(struct v4l2_fract *timeperframe, - unsigned long pclk_limit, unsigned long pclk_max) +static u8 to_clkrc(struct v4l2_fract *timeperframe, unsigned long pclk_max) { unsigned long pclk; @@ -557,9 +555,6 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe, else pclk = pclk_max; - if (pclk_limit && pclk_limit < pclk) - pclk = pclk_limit; - return (pclk_max - 1) / pclk; } @@ -653,10 +648,9 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) clkrc = CLKRC_12MHz; mclk = 12000000; - priv->pclk_limit = 1334000; dev_dbg(&client->dev, "using 12MHz input clock\n"); - clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); + clkrc |= to_clkrc(&priv->tpf, priv->pclk_max); pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc); dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n", @@ -756,7 +750,7 @@ static int ov6650_g_frame_interval(struct v4l2_subdev *sd, struct ov6650 *priv = to_ov6650(client); ival->interval.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf, - priv->pclk_limit, priv->pclk_max)); + priv->pclk_max)); ival->interval.denominator = FRAME_RATE_MAX; dev_dbg(&client->dev, "Frame interval: %u/%u s\n", @@ -787,7 +781,7 @@ static int ov6650_s_frame_interval(struct v4l2_subdev *sd, tpf->numerator = div; tpf->denominator = FRAME_RATE_MAX; - clkrc = to_clkrc(tpf, priv->pclk_limit, priv->pclk_max); + clkrc = to_clkrc(tpf, priv->pclk_max); ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK); if (!ret) { -- cgit v1.2.3 From d898692e9ddb223210ab9a6e99d58b1fff9658a0 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sun, 13 Oct 2019 09:50:47 -0300 Subject: media: ov6650: Simplify clock divisor calculation As appears from an analysis of to_clkrc() helper code after its pclk_limit argument has been dropped, its result no longer depends on another argument - pclk_max. Moreover, assuming that a constant value of FRAME_RATE_MAX is always used as a denominator of the only significant argument left - a struct v4l2_fract, the result in fact depends only on the numerator value of that argument. As a further consequence, it no longer makes sense to recalculate frame intervals by converting them forth and back with a GET_CLKRC_DIV(to_clkrc(tpf)) construct. Drop use of GET_CLKRC_DIV() on results of to_clkrc() where possible - use the frame interval value directly. Furthermore, replace the to_clkrc() helper function with a simple macro and update its users to always use FRAME_RATE_MAX as frame interval denominator and pass only its numerator as an argument. Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index a50244401491..61ddd4ea4c26 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -545,18 +545,7 @@ static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect) return width > rect->width >> 1 || height > rect->height >> 1; } -static u8 to_clkrc(struct v4l2_fract *timeperframe, unsigned long pclk_max) -{ - unsigned long pclk; - - if (timeperframe->numerator && timeperframe->denominator) - pclk = pclk_max * timeperframe->denominator / - (FRAME_RATE_MAX * timeperframe->numerator); - else - pclk = pclk_max; - - return (pclk_max - 1) / pclk; -} +#define to_clkrc(div) ((div) - 1) /* set the format we will capture in */ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) @@ -650,7 +639,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) mclk = 12000000; dev_dbg(&client->dev, "using 12MHz input clock\n"); - clkrc |= to_clkrc(&priv->tpf, priv->pclk_max); + clkrc |= to_clkrc(priv->tpf.numerator); pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc); dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n", @@ -749,9 +738,7 @@ static int ov6650_g_frame_interval(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - ival->interval.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf, - priv->pclk_max)); - ival->interval.denominator = FRAME_RATE_MAX; + ival->interval = priv->tpf; dev_dbg(&client->dev, "Frame interval: %u/%u s\n", ival->interval.numerator, ival->interval.denominator); @@ -766,7 +753,6 @@ static int ov6650_s_frame_interval(struct v4l2_subdev *sd, struct ov6650 *priv = to_ov6650(client); struct v4l2_fract *tpf = &ival->interval; int div, ret; - u8 clkrc; if (tpf->numerator == 0 || tpf->denominator == 0) div = 1; /* Reset to full rate */ @@ -778,14 +764,9 @@ static int ov6650_s_frame_interval(struct v4l2_subdev *sd, else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK)) div = GET_CLKRC_DIV(CLKRC_DIV_MASK); - tpf->numerator = div; - tpf->denominator = FRAME_RATE_MAX; - - clkrc = to_clkrc(tpf, priv->pclk_max); - - ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK); + ret = ov6650_reg_rmw(client, REG_CLKRC, to_clkrc(div), CLKRC_DIV_MASK); if (!ret) { - priv->tpf.numerator = GET_CLKRC_DIV(clkrc); + priv->tpf.numerator = div; priv->tpf.denominator = FRAME_RATE_MAX; *tpf = priv->tpf; -- cgit v1.2.3 From 82d4a161df49476209b62e63dd775261ad07b37e Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sun, 13 Oct 2019 09:50:48 -0300 Subject: media: ov6650: Don't reapply pixel clock divisor on format change As calculation of pixel clock hardware divisor no longer depends on mbus format specific maximum pixel clock, there is no need to reapply the divisor on format change. Drop related code from ov6650_s_fmt() helper. Since a master clock hardware divisor, so far applied only together with the pixel clock divisor in a single operation, will no longer be applied from ov6650_s_fmt(), apply it, still using a hardcoded value for now, from ov6650_prog_dflt() helper so hardware is still initialised correctly on device probe. Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 61ddd4ea4c26..e95ea132ef06 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -564,8 +564,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) .r.height = mf->height << half_scale, }; u32 code = mf->code; - unsigned long mclk, pclk; - u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc; + u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask; int ret; /* select color matrix configuration for given color encoding */ @@ -635,21 +634,9 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) coma_mask |= COMA_QCIF; } - clkrc = CLKRC_12MHz; - mclk = 12000000; - dev_dbg(&client->dev, "using 12MHz input clock\n"); - - clkrc |= to_clkrc(priv->tpf.numerator); - - pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc); - dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n", - mclk / pclk, 10 * mclk % pclk / pclk); - ret = ov6650_set_selection(sd, NULL, &sel); if (!ret) ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); - if (!ret) - ret = ov6650_reg_write(client, REG_CLKRC, clkrc); if (!ret) { priv->half_scale = half_scale; @@ -798,6 +785,8 @@ static int ov6650_prog_dflt(struct i2c_client *client) dev_dbg(&client->dev, "initializing\n"); ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */ + if (!ret) + ret = ov6650_reg_write(client, REG_CLKRC, CLKRC_12MHz); if (!ret) ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER); -- cgit v1.2.3 From 74f84922478bc8c932e30ac58936df86a78e0cb1 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sun, 13 Oct 2019 09:50:49 -0300 Subject: media: ov6650: Drop unused .pclk_max field This field of the driver private structure is no longer used, drop it. Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index e95ea132ef06..f4723c0b5c70 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -197,7 +197,6 @@ struct ov6650 { struct v4l2_clk *clk; bool half_scale; /* scale down output by 2 */ struct v4l2_rect rect; /* sensor cropping window */ - unsigned long pclk_max; /* from resolution and format */ struct v4l2_fract tpf; /* as requested with s_frame_interval */ u32 code; }; @@ -618,17 +617,14 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) code == MEDIA_BUS_FMT_SBGGR8_1X8) { coml_mask = COML_ONE_CHANNEL; coml_set = 0; - priv->pclk_max = 4000000; } else { coml_mask = 0; coml_set = COML_ONE_CHANNEL; - priv->pclk_max = 8000000; } if (half_scale) { dev_dbg(&client->dev, "max resolution: QCIF\n"); coma_set |= COMA_QCIF; - priv->pclk_max /= 2; } else { dev_dbg(&client->dev, "max resolution: CIF\n"); coma_mask |= COMA_QCIF; -- cgit v1.2.3 From fdd5b6e3eff25b5ca9893dc0895589ae49b71008 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Sun, 13 Oct 2019 09:50:50 -0300 Subject: media: ov6650: Fix arbitrary selection of master clock rate A hardcoded 12 MHz master clock frequency has been assumed since conversion of the driver from soc_camera sensor to a standalone V4L2 subdevice by commit 23a52386fabe ("media: ov6650: convert to standalone v4l2 subdevice"). Fix it. Define a static table of supported master clock rates (fix misnamed symbol while being at it), then use v4l2_clk_get/set_rate() to obtain a clock rate matching one of those supported. On success, apply respective master clock hardware divisor provided by the matching element of the table. [Sakari Ailus: Initialize xclk to NULL.] Signed-off-by: Janusz Krzysztofik Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 64 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index f4723c0b5c70..3c8f50f8619f 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -124,7 +124,7 @@ #define DEF_AECH 0x4D -#define CLKRC_6MHz 0x00 +#define CLKRC_8MHz 0x00 #define CLKRC_12MHz 0x40 #define CLKRC_16MHz 0x80 #define CLKRC_24MHz 0xc0 @@ -201,6 +201,29 @@ struct ov6650 { u32 code; }; +struct ov6650_xclk { + unsigned long rate; + u8 clkrc; +}; + +static const struct ov6650_xclk ov6650_xclk[] = { +{ + .rate = 8000000, + .clkrc = CLKRC_8MHz, +}, +{ + .rate = 12000000, + .clkrc = CLKRC_12MHz, +}, +{ + .rate = 16000000, + .clkrc = CLKRC_16MHz, +}, +{ + .rate = 24000000, + .clkrc = CLKRC_24MHz, +}, +}; static u32 ov6650_codes[] = { MEDIA_BUS_FMT_YUYV8_2X8, @@ -774,7 +797,7 @@ static int ov6650_reset(struct i2c_client *client) } /* program default register values */ -static int ov6650_prog_dflt(struct i2c_client *client) +static int ov6650_prog_dflt(struct i2c_client *client, u8 clkrc) { int ret; @@ -782,7 +805,7 @@ static int ov6650_prog_dflt(struct i2c_client *client) ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */ if (!ret) - ret = ov6650_reg_write(client, REG_CLKRC, CLKRC_12MHz); + ret = ov6650_reg_write(client, REG_CLKRC, clkrc); if (!ret) ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER); @@ -793,8 +816,10 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov6650 *priv = to_ov6650(client); - u8 pidh, pidl, midh, midl; - int ret; + const struct ov6650_xclk *xclk = NULL; + unsigned long rate; + u8 pidh, pidl, midh, midl; + int i, ret; priv->clk = v4l2_clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { @@ -803,6 +828,33 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) return ret; } + rate = v4l2_clk_get_rate(priv->clk); + for (i = 0; rate && i < ARRAY_SIZE(ov6650_xclk); i++) { + if (rate != ov6650_xclk[i].rate) + continue; + + xclk = &ov6650_xclk[i]; + dev_info(&client->dev, "using host default clock rate %lukHz\n", + rate / 1000); + break; + } + for (i = 0; !xclk && i < ARRAY_SIZE(ov6650_xclk); i++) { + ret = v4l2_clk_set_rate(priv->clk, ov6650_xclk[i].rate); + if (ret || v4l2_clk_get_rate(priv->clk) != ov6650_xclk[i].rate) + continue; + + xclk = &ov6650_xclk[i]; + dev_info(&client->dev, "using negotiated clock rate %lukHz\n", + xclk->rate / 1000); + break; + } + if (!xclk) { + dev_err(&client->dev, "unable to get supported clock rate\n"); + if (!ret) + ret = -EINVAL; + goto eclkput; + } + ret = ov6650_s_power(sd, 1); if (ret < 0) goto eclkput; @@ -836,7 +888,7 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) ret = ov6650_reset(client); if (!ret) - ret = ov6650_prog_dflt(client); + ret = ov6650_prog_dflt(client, xclk->clkrc); if (!ret) { struct v4l2_mbus_framefmt mf = ov6650_def_fmt; -- cgit v1.2.3 From ecfaec43e4999cb584135fccc09760be775b735c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 11 Oct 2019 14:13:54 -0300 Subject: media: lm3646: remove redundant assignment to variable rval The variable rval is being initialized with a value that is never read and is being re-assigned a little later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/lm3646.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index d8a8853f9a2b..c76ccf67a909 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -134,7 +134,7 @@ static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl) { struct lm3646_flash *flash = to_lm3646_flash(ctrl); unsigned int reg_val; - int rval = -EINVAL; + int rval; switch (ctrl->id) { case V4L2_CID_FLASH_LED_MODE: -- cgit v1.2.3 From cc196e48e517d4810304db936cbe193b8954fa7e Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Wed, 9 Oct 2019 09:35:08 -0300 Subject: media: ov5640: add PIXEL_RATE control Add v4l2 controls to report the pixel rates of each mode. This is needed by some CSI2 receiver in order to perform proper DPHY configuration. Signed-off-by: Benoit Parrot Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 500d9bbff10b..b93b61baace5 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -193,6 +193,7 @@ struct ov5640_mode_info { struct ov5640_ctrls { struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *pixel_rate; struct { struct v4l2_ctrl *auto_exp; struct v4l2_ctrl *exposure; @@ -1614,6 +1615,16 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, return mode; } +static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor) +{ + u64 rate; + + rate = sensor->current_mode->vtot * sensor->current_mode->htot; + rate *= ov5640_framerates[sensor->current_fr]; + + return rate; +} + /* * sensor changes between scaling and subsampling, go through * exposure calculation @@ -1818,8 +1829,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) * All the formats we support have 16 bits per pixel, seems to require * the same rate than YUV, so we can just use 16 bpp all the time. */ - rate = mode->vtot * mode->htot * 16; - rate *= ov5640_framerates[sensor->current_fr]; + rate = ov5640_calc_pixel_rate(sensor) * 16; if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) { rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes; ret = ov5640_set_mipi_pclk(sensor, rate); @@ -2233,6 +2243,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, if (mbus_fmt->code != sensor->fmt.code) sensor->pending_fmt_change = true; + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, + ov5640_calc_pixel_rate(sensor)); out: mutex_unlock(&sensor->lock); return ret; @@ -2657,6 +2669,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) /* we can use our own mutex for the ctrl lock */ hdl->lock = &sensor->lock; + /* Clock related controls */ + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + 0, INT_MAX, 1, + ov5640_calc_pixel_rate(sensor)); + /* Auto/manual white balance */ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, @@ -2704,6 +2721,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) goto free_ctrls; } + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; @@ -2816,6 +2834,9 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, sensor->frame_interval = fi->interval; sensor->current_mode = mode; sensor->pending_mode_change = true; + + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, + ov5640_calc_pixel_rate(sensor)); } out: mutex_unlock(&sensor->lock); -- cgit v1.2.3 From 92b9096c0fe02764561c8d972315d0e7509441bf Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Wed, 9 Oct 2019 09:35:09 -0300 Subject: media: ov5640: Fix 1920x1080 mode to remove extra enable/disable In the 1920x1080 register array an extra pair of reset ctrl disable re-enable was causing unwanted init delays. Signed-off-by: Benoit Parrot Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index b93b61baace5..065c9b61ecbd 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -490,7 +490,6 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = { }; static const struct reg_value ov5640_setting_1080P_1920_1080[] = { - {0x3008, 0x42, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, @@ -518,7 +517,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, {0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, - {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, + {0x4005, 0x1a, 0, 0}, }; static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { -- cgit v1.2.3 From 981e445454531c9d5ac5d3fa8c0f1bd55262d001 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Wed, 9 Oct 2019 09:35:10 -0300 Subject: media: ov5640: Make 2592x1944 mode only available at 15 fps The sensor data sheet clearly state that 2592x1944 only works at 15 fps make sure we don't try to miss configure the pll out of acceptable range. Signed-off-by: Benoit Parrot Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5640.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 065c9b61ecbd..5e495c833d32 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1611,6 +1611,11 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, !(mode->hact == 640 && mode->vact == 480)) return NULL; + /* 2592x1944 only works at 15fps max */ + if ((mode->hact == 2592 && mode->vact == 1944) && + fr > OV5640_15_FPS) + return NULL; + return mode; } -- cgit v1.2.3 From 801ef7c4919efba6b96b5aed1e72844ca69e26d3 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 10:28:51 -0300 Subject: media: ad5820: Define entity function Without this patch, media_device_register_entity throws a warning: dev_warn(mdev->dev, "Entity type for entity %s was not initialized!\n", entity->name); Signed-off-by: Ricardo Ribalda Delgado Acked-by: Pavel Machek Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad5820.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 925c171e7797..7a49651f4d1f 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -309,6 +309,7 @@ static int ad5820_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops); coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; coil->subdev.internal_ops = &ad5820_internal_ops; + coil->subdev.entity.function = MEDIA_ENT_F_LENS; strscpy(coil->subdev.name, "ad5820 focus", sizeof(coil->subdev.name)); ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL); -- cgit v1.2.3 From c01674e75a4193d1e23f509689e676634f067a0b Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 10:28:53 -0300 Subject: media: ad5820: Add support for enable pin This patch adds support for a programmable enable pin. It can be used in situations where the ANA-vcc is not configurable (dummy-regulator), or just to have a more fine control of the power saving. The use of the enable pin is optional. Signed-off-by: Ricardo Ribalda Delgado Acked-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/ad5820.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 78dc64d7b0e7..30d460844dff 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -975,7 +975,7 @@ if MEDIA_CAMERA_SUPPORT config VIDEO_AD5820 tristate "AD5820 lens voice coil support" - depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on GPIOLIB && I2C && VIDEO_V4L2 && MEDIA_CONTROLLER help This is a driver for the AD5820 camera lens voice coil. It is used for example in Nokia N900 (RX-51). diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 7a49651f4d1f..76aab651f217 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,8 @@ struct ad5820_device { u32 focus_ramp_time; u32 focus_ramp_mode; + struct gpio_desc *enable_gpio; + struct mutex power_lock; int power_count; @@ -114,6 +117,8 @@ static int ad5820_power_off(struct ad5820_device *coil, bool standby) ret = ad5820_update_hw(coil); } + gpiod_set_value_cansleep(coil->enable_gpio, 0); + ret2 = regulator_disable(coil->vana); if (ret) return ret; @@ -128,6 +133,8 @@ static int ad5820_power_on(struct ad5820_device *coil, bool restore) if (ret < 0) return ret; + gpiod_set_value_cansleep(coil->enable_gpio, 1); + if (restore) { /* Restore the hardware settings. */ coil->standby = false; @@ -138,6 +145,7 @@ static int ad5820_power_on(struct ad5820_device *coil, bool restore) return 0; fail: + gpiod_set_value_cansleep(coil->enable_gpio, 0); coil->standby = true; regulator_disable(coil->vana); @@ -304,6 +312,15 @@ static int ad5820_probe(struct i2c_client *client, return ret; } + coil->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(coil->enable_gpio)) { + ret = PTR_ERR(coil->enable_gpio); + if (ret != -EPROBE_DEFER) + dev_err(&client->dev, "could not get enable gpio\n"); + return ret; + } + mutex_init(&coil->power_lock); v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops); -- cgit v1.2.3 From 1c7ae4a51298d52a21f63b2214657982036c7498 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 10:28:54 -0300 Subject: media: ad5820: Add support for of-autoload Since kernel 4.16, i2c devices with DT compatible tag are modprobed using their DT modalias. Without this patch, if this driver is build as module it would never be autoprobed. There is no need to mask it with CONFIG_OF to allow ACPI loading, this also builds find with CONFIG_OF=n. Signed-off-by: Ricardo Ribalda Delgado Acked-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad5820.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 76aab651f217..5651609e5095 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -364,12 +364,19 @@ static const struct i2c_device_id ad5820_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); +static const struct of_device_id ad5820_of_table[] = { + { .compatible = "adi,ad5820" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5820_of_table); + static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume); static struct i2c_driver ad5820_i2c_driver = { .driver = { .name = AD5820_NAME, .pm = &ad5820_pm, + .of_match_table = ad5820_of_table, }, .probe = ad5820_probe, .remove = ad5820_remove, -- cgit v1.2.3 From b8bf73136bae83ec5fef89a8df87619d61ccd3eb Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 7 Oct 2019 10:28:56 -0300 Subject: media: ad5820: Add support for ad5821 and ad5823 According to the datasheet, both AD5821 and AD5820 share a compatible register-set: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5821.pdf Some camera modules also refer that AD5823 is a replacement of AD5820: https://download.kamami.com/p564094-OV8865_DS.pdf Suggested-by: Pavel Machek Signed-off-by: Ricardo Ribalda Delgado Acked-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad5820.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 5651609e5095..19c74db0649f 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -25,8 +25,6 @@ #include #include -#define AD5820_NAME "ad5820" - /* Register definitions */ #define AD5820_POWER_DOWN (1 << 15) #define AD5820_DAC_SHIFT 4 @@ -359,13 +357,17 @@ static int ad5820_remove(struct i2c_client *client) } static const struct i2c_device_id ad5820_id_table[] = { - { AD5820_NAME, 0 }, + { "ad5820", 0 }, + { "ad5821", 0 }, + { "ad5823", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); static const struct of_device_id ad5820_of_table[] = { { .compatible = "adi,ad5820" }, + { .compatible = "adi,ad5821" }, + { .compatible = "adi,ad5823" }, { } }; MODULE_DEVICE_TABLE(of, ad5820_of_table); @@ -374,7 +376,7 @@ static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume); static struct i2c_driver ad5820_i2c_driver = { .driver = { - .name = AD5820_NAME, + .name = "ad5820", .pm = &ad5820_pm, .of_match_table = ad5820_of_table, }, -- cgit v1.2.3 From c31e2febdeb2a58cfb1e37c73d411c8c3d75a72b Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sun, 6 Oct 2019 04:38:38 -0300 Subject: media: i2c: ov5695: Fix randbuild error If VIDEO_OV5695 is y and V4L2_FWNODE is m, building fails: drivers/media/i2c/ov5695.o: In function `ov5695_probe': ov5695.c:(.text+0xf4c): undefined reference to `v4l2_async_register_subdev_sensor_common' Select V4L2_FWNODE like OV5675 does. Fixes: 623df5d710fe ("media: i2c: ov5695: Modify the function of async register subdev related devices") Signed-off-by: YueHaibing Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 30d460844dff..ae57f5c58b7b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -729,6 +729,7 @@ config VIDEO_OV5675 config VIDEO_OV5695 tristate "OmniVision OV5695 sensor support" depends on I2C && VIDEO_V4L2 + select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. -- cgit v1.2.3 From 828dbc299278065b634e913d2700d254a3224853 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 4 Oct 2019 13:05:25 -0300 Subject: media: i2c: Add IMX290 CMOS image sensor driver Add driver for Sony IMX290 CMOS image sensor driver. The driver only supports I2C interface for programming and MIPI CSI-2 for sensor output. [Sakari Ailus: Rewrapped a few lines over 80 chars a little.] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx290.c | 884 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 896 insertions(+) create mode 100644 drivers/media/i2c/imx290.c (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index ae57f5c58b7b..992f60825ccd 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -597,6 +597,17 @@ config VIDEO_IMX274 This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. +config VIDEO_IMX290 + tristate "Sony IMX290 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX290 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called imx290. + config VIDEO_IMX319 tristate "Sony IMX319 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index beb170b002dc..6a80a353393f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c new file mode 100644 index 000000000000..f7678e5a5d87 --- /dev/null +++ b/drivers/media/i2c/imx290.c @@ -0,0 +1,884 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sony IMX290 CMOS Image Sensor Driver + * + * Copyright (C) 2019 FRAMOS GmbH. + * + * Copyright (C) 2019 Linaro Ltd. + * Author: Manivannan Sadhasivam + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX290_STANDBY 0x3000 +#define IMX290_REGHOLD 0x3001 +#define IMX290_XMSTA 0x3002 +#define IMX290_GAIN 0x3014 + +#define IMX290_DEFAULT_LINK_FREQ 445500000 + +static const char * const imx290_supply_name[] = { + "vdda", + "vddd", + "vdddo", +}; + +#define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name) + +struct imx290_regval { + u16 reg; + u8 val; +}; + +struct imx290_mode { + u32 width; + u32 height; + u32 pixel_rate; + u32 link_freq_index; + + const struct imx290_regval *data; + u32 data_size; +}; + +struct imx290 { + struct device *dev; + struct clk *xclk; + struct regmap *regmap; + + struct v4l2_subdev sd; + struct v4l2_fwnode_endpoint ep; + struct media_pad pad; + struct v4l2_mbus_framefmt current_format; + const struct imx290_mode *current_mode; + + struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES]; + struct gpio_desc *rst_gpio; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + + struct mutex lock; +}; + +struct imx290_pixfmt { + u32 code; +}; + +static const struct imx290_pixfmt imx290_formats[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10 }, +}; + +static const struct regmap_config imx290_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct imx290_regval imx290_global_init_settings[] = { + { 0x3007, 0x00 }, + { 0x3009, 0x00 }, + { 0x3018, 0x65 }, + { 0x3019, 0x04 }, + { 0x301a, 0x00 }, + { 0x3443, 0x03 }, + { 0x3444, 0x20 }, + { 0x3445, 0x25 }, + { 0x3407, 0x03 }, + { 0x303a, 0x0c }, + { 0x3040, 0x00 }, + { 0x3041, 0x00 }, + { 0x303c, 0x00 }, + { 0x303d, 0x00 }, + { 0x3042, 0x9c }, + { 0x3043, 0x07 }, + { 0x303e, 0x49 }, + { 0x303f, 0x04 }, + { 0x304b, 0x0a }, + { 0x300f, 0x00 }, + { 0x3010, 0x21 }, + { 0x3012, 0x64 }, + { 0x3016, 0x09 }, + { 0x3070, 0x02 }, + { 0x3071, 0x11 }, + { 0x309b, 0x10 }, + { 0x309c, 0x22 }, + { 0x30a2, 0x02 }, + { 0x30a6, 0x20 }, + { 0x30a8, 0x20 }, + { 0x30aa, 0x20 }, + { 0x30ac, 0x20 }, + { 0x30b0, 0x43 }, + { 0x3119, 0x9e }, + { 0x311c, 0x1e }, + { 0x311e, 0x08 }, + { 0x3128, 0x05 }, + { 0x313d, 0x83 }, + { 0x3150, 0x03 }, + { 0x317e, 0x00 }, + { 0x32b8, 0x50 }, + { 0x32b9, 0x10 }, + { 0x32ba, 0x00 }, + { 0x32bb, 0x04 }, + { 0x32c8, 0x50 }, + { 0x32c9, 0x10 }, + { 0x32ca, 0x00 }, + { 0x32cb, 0x04 }, + { 0x332c, 0xd3 }, + { 0x332d, 0x10 }, + { 0x332e, 0x0d }, + { 0x3358, 0x06 }, + { 0x3359, 0xe1 }, + { 0x335a, 0x11 }, + { 0x3360, 0x1e }, + { 0x3361, 0x61 }, + { 0x3362, 0x10 }, + { 0x33b0, 0x50 }, + { 0x33b2, 0x1a }, + { 0x33b3, 0x04 }, +}; + +static const struct imx290_regval imx290_1080p_settings[] = { + /* mode settings */ + { 0x3007, 0x00 }, + { 0x303a, 0x0c }, + { 0x3414, 0x0a }, + { 0x3472, 0x80 }, + { 0x3473, 0x07 }, + { 0x3418, 0x38 }, + { 0x3419, 0x04 }, + { 0x3012, 0x64 }, + { 0x3013, 0x00 }, + { 0x305c, 0x18 }, + { 0x305d, 0x03 }, + { 0x305e, 0x20 }, + { 0x305f, 0x01 }, + { 0x315e, 0x1a }, + { 0x3164, 0x1a }, + { 0x3480, 0x49 }, + /* data rate settings */ + { 0x3009, 0x01 }, + { 0x3405, 0x10 }, + { 0x3446, 0x57 }, + { 0x3447, 0x00 }, + { 0x3448, 0x37 }, + { 0x3449, 0x00 }, + { 0x344a, 0x1f }, + { 0x344b, 0x00 }, + { 0x344c, 0x1f }, + { 0x344d, 0x00 }, + { 0x344e, 0x1f }, + { 0x344f, 0x00 }, + { 0x3450, 0x77 }, + { 0x3451, 0x00 }, + { 0x3452, 0x1f }, + { 0x3453, 0x00 }, + { 0x3454, 0x17 }, + { 0x3455, 0x00 }, + { 0x301c, 0x98 }, + { 0x301d, 0x08 }, +}; + +static const struct imx290_regval imx290_720p_settings[] = { + /* mode settings */ + { 0x3007, 0x10 }, + { 0x303a, 0x06 }, + { 0x3414, 0x04 }, + { 0x3472, 0x00 }, + { 0x3473, 0x05 }, + { 0x3418, 0xd0 }, + { 0x3419, 0x02 }, + { 0x3012, 0x64 }, + { 0x3013, 0x00 }, + { 0x305c, 0x20 }, + { 0x305d, 0x00 }, + { 0x305e, 0x20 }, + { 0x305f, 0x01 }, + { 0x315e, 0x1a }, + { 0x3164, 0x1a }, + { 0x3480, 0x49 }, + /* data rate settings */ + { 0x3009, 0x01 }, + { 0x3405, 0x10 }, + { 0x3446, 0x4f }, + { 0x3447, 0x00 }, + { 0x3448, 0x2f }, + { 0x3449, 0x00 }, + { 0x344a, 0x17 }, + { 0x344b, 0x00 }, + { 0x344c, 0x17 }, + { 0x344d, 0x00 }, + { 0x344e, 0x17 }, + { 0x344f, 0x00 }, + { 0x3450, 0x57 }, + { 0x3451, 0x00 }, + { 0x3452, 0x17 }, + { 0x3453, 0x00 }, + { 0x3454, 0x17 }, + { 0x3455, 0x00 }, + { 0x301c, 0xe4 }, + { 0x301d, 0x0c }, +}; + +static const struct imx290_regval imx290_10bit_settings[] = { + { 0x3005, 0x00}, + { 0x3046, 0x00}, + { 0x3129, 0x1d}, + { 0x317c, 0x12}, + { 0x31ec, 0x37}, + { 0x3441, 0x0a}, + { 0x3442, 0x0a}, + { 0x300a, 0x3c}, + { 0x300b, 0x00}, +}; + +/* supported link frequencies */ +static const s64 imx290_link_freq[] = { + IMX290_DEFAULT_LINK_FREQ, +}; + +/* Mode configs */ +static const struct imx290_mode imx290_modes[] = { + { + .width = 1920, + .height = 1080, + .data = imx290_1080p_settings, + .data_size = ARRAY_SIZE(imx290_1080p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, + { + .width = 1280, + .height = 720, + .data = imx290_720p_settings, + .data_size = ARRAY_SIZE(imx290_720p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, +}; + +static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx290, sd); +} + +static inline int imx290_read_reg(struct imx290 *imx290, u16 addr, u8 *value) +{ + unsigned int regval; + int ret; + + ret = regmap_read(imx290->regmap, addr, ®val); + if (ret) { + dev_err(imx290->dev, "I2C read failed for addr: %x\n", addr); + return ret; + } + + *value = regval & 0xff; + + return 0; +} + +static int imx290_write_reg(struct imx290 *imx290, u16 addr, u8 value) +{ + int ret; + + ret = regmap_write(imx290->regmap, addr, value); + if (ret) { + dev_err(imx290->dev, "I2C write failed for addr: %x\n", addr); + return ret; + } + + return ret; +} + +static int imx290_set_register_array(struct imx290 *imx290, + const struct imx290_regval *settings, + unsigned int num_settings) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_settings; ++i, ++settings) { + ret = imx290_write_reg(imx290, settings->reg, settings->val); + if (ret < 0) + return ret; + + /* Settle time is 10ms for all registers */ + msleep(10); + } + + return 0; +} + +static int imx290_write_buffered_reg(struct imx290 *imx290, u16 address_low, + u8 nr_regs, u32 value) +{ + unsigned int i; + int ret; + + ret = imx290_write_reg(imx290, IMX290_REGHOLD, 0x01); + if (ret) { + dev_err(imx290->dev, "Error setting hold register\n"); + return ret; + } + + for (i = 0; i < nr_regs; i++) { + ret = imx290_write_reg(imx290, address_low + i, + (u8)(value >> (i * 8))); + if (ret) { + dev_err(imx290->dev, "Error writing buffered registers\n"); + return ret; + } + } + + ret = imx290_write_reg(imx290, IMX290_REGHOLD, 0x00); + if (ret) { + dev_err(imx290->dev, "Error setting hold register\n"); + return ret; + } + + return ret; +} + +static int imx290_set_gain(struct imx290 *imx290, u32 value) +{ + int ret; + + ret = imx290_write_buffered_reg(imx290, IMX290_GAIN, 1, value); + if (ret) + dev_err(imx290->dev, "Unable to write gain\n"); + + return ret; +} + +/* Stop streaming */ +static int imx290_stop_streaming(struct imx290 *imx290) +{ + int ret; + + ret = imx290_write_reg(imx290, IMX290_STANDBY, 0x01); + if (ret < 0) + return ret; + + msleep(30); + + return imx290_write_reg(imx290, IMX290_XMSTA, 0x01); +} + +static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx290 *imx290 = container_of(ctrl->handler, + struct imx290, ctrls); + int ret = 0; + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(imx290->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ret = imx290_set_gain(imx290, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(imx290->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx290_ctrl_ops = { + .s_ctrl = imx290_set_ctrl, +}; + +static int imx290_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(imx290_formats)) + return -EINVAL; + + code->code = imx290_formats[code->index].code; + + return 0; +} + +static int imx290_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx290 *imx290 = to_imx290(sd); + struct v4l2_mbus_framefmt *framefmt; + + mutex_lock(&imx290->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + framefmt = v4l2_subdev_get_try_format(&imx290->sd, cfg, + fmt->pad); + else + framefmt = &imx290->current_format; + + fmt->format = *framefmt; + + mutex_unlock(&imx290->lock); + + return 0; +} + +static int imx290_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx290 *imx290 = to_imx290(sd); + const struct imx290_mode *mode; + struct v4l2_mbus_framefmt *format; + unsigned int i; + + mutex_lock(&imx290->lock); + + mode = v4l2_find_nearest_size(imx290_modes, + ARRAY_SIZE(imx290_modes), + width, height, + fmt->format.width, fmt->format.height); + + fmt->format.width = mode->width; + fmt->format.height = mode->height; + + for (i = 0; i < ARRAY_SIZE(imx290_formats); i++) + if (imx290_formats[i].code == fmt->format.code) + break; + + if (i >= ARRAY_SIZE(imx290_formats)) + i = 0; + + fmt->format.code = imx290_formats[i].code; + fmt->format.field = V4L2_FIELD_NONE; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + } else { + format = &imx290->current_format; + __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, mode->pixel_rate); + + imx290->current_mode = mode; + } + + *format = fmt->format; + + mutex_unlock(&imx290->lock); + + return 0; +} + +static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.width = 1920; + fmt.format.height = 1080; + + imx290_set_fmt(subdev, cfg, &fmt); + + return 0; +} + +static int imx290_write_current_format(struct imx290 *imx290, + struct v4l2_mbus_framefmt *format) +{ + int ret; + + switch (format->code) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + ret = imx290_set_register_array(imx290, imx290_10bit_settings, + ARRAY_SIZE( + imx290_10bit_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set format registers\n"); + return ret; + } + break; + default: + dev_err(imx290->dev, "Unknown pixel format\n"); + return -EINVAL; + } + + return 0; +} + +/* Start streaming */ +static int imx290_start_streaming(struct imx290 *imx290) +{ + int ret; + + /* Set init register settings */ + ret = imx290_set_register_array(imx290, imx290_global_init_settings, + ARRAY_SIZE( + imx290_global_init_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set init registers\n"); + return ret; + } + + /* Set current frame format */ + ret = imx290_write_current_format(imx290, &imx290->current_format); + if (ret < 0) { + dev_err(imx290->dev, "Could not set frame format\n"); + return ret; + } + + /* Apply default values of current mode */ + ret = imx290_set_register_array(imx290, imx290->current_mode->data, + imx290->current_mode->data_size); + if (ret < 0) { + dev_err(imx290->dev, "Could not set current mode\n"); + return ret; + } + + /* Apply customized values from user */ + ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); + if (ret) { + dev_err(imx290->dev, "Could not sync v4l2 controls\n"); + return ret; + } + + ret = imx290_write_reg(imx290, IMX290_STANDBY, 0x00); + if (ret < 0) + return ret; + + msleep(30); + + /* Start streaming */ + return imx290_write_reg(imx290, IMX290_XMSTA, 0x00); +} + +static int imx290_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx290 *imx290 = to_imx290(sd); + int ret = 0; + + if (enable) { + ret = pm_runtime_get_sync(imx290->dev); + if (ret < 0) { + pm_runtime_put_noidle(imx290->dev); + goto unlock_and_return; + } + + ret = imx290_start_streaming(imx290); + if (ret) { + dev_err(imx290->dev, "Start stream failed\n"); + pm_runtime_put(imx290->dev); + goto unlock_and_return; + } + } else { + imx290_stop_streaming(imx290); + pm_runtime_put(imx290->dev); + } + +unlock_and_return: + + return ret; +} + +static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) +{ + unsigned int i; + + for (i = 0; i < IMX290_NUM_SUPPLIES; i++) + imx290->supplies[i].supply = imx290_supply_name[i]; + + return devm_regulator_bulk_get(dev, IMX290_NUM_SUPPLIES, + imx290->supplies); +} + +static int imx290_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx290 *imx290 = to_imx290(sd); + int ret; + + ret = clk_prepare_enable(imx290->xclk); + if (ret) { + dev_err(imx290->dev, "Failed to enable clock\n"); + return ret; + } + + ret = regulator_bulk_enable(IMX290_NUM_SUPPLIES, imx290->supplies); + if (ret) { + dev_err(imx290->dev, "Failed to enable regulators\n"); + clk_disable_unprepare(imx290->xclk); + return ret; + } + + usleep_range(1, 2); + gpiod_set_value_cansleep(imx290->rst_gpio, 1); + usleep_range(30000, 31000); + + return 0; +} + +static int imx290_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx290 *imx290 = to_imx290(sd); + + clk_disable_unprepare(imx290->xclk); + gpiod_set_value_cansleep(imx290->rst_gpio, 0); + regulator_bulk_disable(IMX290_NUM_SUPPLIES, imx290->supplies); + + return 0; +} + +static const struct dev_pm_ops imx290_pm_ops = { + SET_RUNTIME_PM_OPS(imx290_power_on, imx290_power_off, NULL) +}; + +static const struct v4l2_subdev_video_ops imx290_video_ops = { + .s_stream = imx290_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx290_pad_ops = { + .init_cfg = imx290_entity_init_cfg, + .enum_mbus_code = imx290_enum_mbus_code, + .get_fmt = imx290_get_fmt, + .set_fmt = imx290_set_fmt, +}; + +static const struct v4l2_subdev_ops imx290_subdev_ops = { + .video = &imx290_video_ops, + .pad = &imx290_pad_ops, +}; + +static const struct media_entity_operations imx290_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int imx290_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct fwnode_handle *endpoint; + struct imx290 *imx290; + u32 xclk_freq; + int ret; + + imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); + if (!imx290) + return -ENOMEM; + + imx290->dev = dev; + imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config); + if (IS_ERR(imx290->regmap)) { + dev_err(dev, "Unable to initialize I2C\n"); + return -ENODEV; + } + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &imx290->ep); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(dev, "Parsing endpoint node failed\n"); + goto free_err; + } + + if (!imx290->ep.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + ret = -EINVAL; + goto free_err; + } + + if (imx290->ep.link_frequencies[0] != IMX290_DEFAULT_LINK_FREQ) { + dev_err(dev, "Unsupported link frequency\n"); + ret = -EINVAL; + goto free_err; + } + + /* Only CSI2 is supported for now */ + if (imx290->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { + dev_err(dev, "Unsupported bus type, should be CSI2\n"); + ret = -EINVAL; + goto free_err; + } + + /* Set default mode to max resolution */ + imx290->current_mode = &imx290_modes[0]; + + /* get system clock (xclk) */ + imx290->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(imx290->xclk)) { + dev_err(dev, "Could not get xclk"); + ret = PTR_ERR(imx290->xclk); + goto free_err; + } + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &xclk_freq); + if (ret) { + dev_err(dev, "Could not get xclk frequency\n"); + goto free_err; + } + + /* external clock must be 37.125 MHz */ + if (xclk_freq != 37125000) { + dev_err(dev, "External clock frequency %u is not supported\n", + xclk_freq); + ret = -EINVAL; + goto free_err; + } + + ret = clk_set_rate(imx290->xclk, xclk_freq); + if (ret) { + dev_err(dev, "Could not set xclk frequency\n"); + goto free_err; + } + + ret = imx290_get_regulators(dev, imx290); + if (ret < 0) { + dev_err(dev, "Cannot get regulators\n"); + goto free_err; + } + + imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + if (IS_ERR(imx290->rst_gpio)) { + dev_err(dev, "Cannot get reset gpio\n"); + ret = PTR_ERR(imx290->rst_gpio); + goto free_err; + } + + mutex_init(&imx290->lock); + + v4l2_ctrl_handler_init(&imx290->ctrls, 3); + + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_GAIN, 0, 72, 1, 0); + imx290->link_freq = + v4l2_ctrl_new_int_menu(&imx290->ctrls, + &imx290_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(imx290_link_freq) - 1, + 0, imx290_link_freq); + if (imx290->link_freq) + imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, + imx290_modes[0].pixel_rate); + + imx290->sd.ctrl_handler = &imx290->ctrls; + + if (imx290->ctrls.error) { + dev_err(dev, "Control initialization error %d\n", + imx290->ctrls.error); + ret = imx290->ctrls.error; + goto free_ctrl; + } + + v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); + imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx290->sd.dev = &client->dev; + imx290->sd.entity.ops = &imx290_subdev_entity_ops; + imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + imx290->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad); + if (ret < 0) { + dev_err(dev, "Could not register media entity\n"); + goto free_ctrl; + } + + ret = v4l2_async_register_subdev(&imx290->sd); + if (ret < 0) { + dev_err(dev, "Could not register v4l2 device\n"); + goto free_entity; + } + + /* Power on the device to match runtime PM state below */ + ret = imx290_power_on(dev); + if (ret < 0) { + dev_err(dev, "Could not power on the device\n"); + goto free_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + v4l2_fwnode_endpoint_free(&imx290->ep); + + return 0; + +free_entity: + media_entity_cleanup(&imx290->sd.entity); +free_ctrl: + v4l2_ctrl_handler_free(&imx290->ctrls); + mutex_destroy(&imx290->lock); +free_err: + v4l2_fwnode_endpoint_free(&imx290->ep); + + return ret; +} + +static int imx290_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx290 *imx290 = to_imx290(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + mutex_destroy(&imx290->lock); + + pm_runtime_disable(imx290->dev); + if (!pm_runtime_status_suspended(imx290->dev)) + imx290_power_off(imx290->dev); + pm_runtime_set_suspended(imx290->dev); + + return 0; +} + +static const struct of_device_id imx290_of_match[] = { + { .compatible = "sony,imx290" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx290_of_match); + +static struct i2c_driver imx290_i2c_driver = { + .probe_new = imx290_probe, + .remove = imx290_remove, + .driver = { + .name = "imx290", + .pm = &imx290_pm_ops, + .of_match_table = of_match_ptr(imx290_of_match), + }, +}; + +module_i2c_driver(imx290_i2c_driver); + +MODULE_DESCRIPTION("Sony IMX290 CMOS Image Sensor Driver"); +MODULE_AUTHOR("FRAMOS GmbH"); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 61c03b631b74a38ab53753f3ee971a55886d4843 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Thu, 17 Oct 2019 22:46:56 -0300 Subject: media: st-mipid02: add a check for devm_gpiod_get_optional mipid02_probe misses a check for devm_gpiod_get_optional and may miss the failure. Add a check to fix the problem. Signed-off-by: Chuhong Yuan Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/st-mipid02.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index 81285b8d5cfb..003ba22334cd 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -971,6 +971,11 @@ static int mipid02_probe(struct i2c_client *client) bridge->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(bridge->reset_gpio)) { + dev_err(dev, "failed to get reset GPIO\n"); + return PTR_ERR(bridge->reset_gpio); + } + ret = mipid02_get_regulators(bridge); if (ret) { dev_err(dev, "failed to get regulators %d", ret); -- cgit v1.2.3 From 837c07ebb030bc3582c14907dd2b79ca9b0b6984 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Sep 2019 10:05:37 -0300 Subject: media: smiapp: Use the BIT macro where appropriate, remove useless definition The BIT macro is a better way to define register bits, for 1 << bit is risky for 32-bit registers. Also remove the definition of SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN which has a value of zero. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 3 +-- drivers/media/i2c/smiapp/smiapp-reg.h | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 9adf8e034e7d..8fa0290ad15c 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -986,8 +986,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, rval = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, - SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | - SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN); if (rval) goto out; diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h index 2804a4d9a4e1..02b0abef6a22 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -11,25 +11,26 @@ #ifndef __SMIAPP_REG_H_ #define __SMIAPP_REG_H_ +#include + #include "smiapp-reg-defs.h" /* Bits for above register */ -#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) -#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) - -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) - -#define SMIAPP_SOFTWARE_RESET (1 << 0) - -#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) -#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) +#define SMIAPP_IMAGE_ORIENTATION_HFLIP BIT(0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP BIT(1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN BIT(1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR BIT(2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY BIT(1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA BIT(2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE BIT(3) + +#define SMIAPP_SOFTWARE_RESET BIT(0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE BIT(1) #define SMIAPP_DPHY_CTRL_AUTOMATIC 0 /* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ -- cgit v1.2.3 From a5b1d5413534607b05fb34470ff62bf395f5c8d0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Sep 2019 11:25:42 -0300 Subject: media: smiapp: Fix error handling at NVM reading If NVM reading failed, the device was left powered on. Fix that. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 8fa0290ad15c..13f7a6315f39 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2326,11 +2326,12 @@ smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, if (rval < 0) { if (rval != -EBUSY && rval != -EAGAIN) pm_runtime_set_active(&client->dev); - pm_runtime_put(&client->dev); + pm_runtime_put_noidle(&client->dev); return -ENODEV; } if (smiapp_read_nvm(sensor, sensor->nvm)) { + pm_runtime_put(&client->dev); dev_err(&client->dev, "nvm read failed\n"); return -ENODEV; } -- cgit v1.2.3 From e367095df32b5c8db762b17dd68a67ea0d9edfcf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Sep 2019 10:15:11 -0300 Subject: media: smiapp: Refactor reading NVM page Split out reading a single NVM page into a separate function. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 83 +++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 37 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 13f7a6315f39..5a136068f4d6 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -970,56 +970,65 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor) * SMIA++ NVM handling * */ -static int smiapp_read_nvm(struct smiapp_sensor *sensor, - unsigned char *nvm) + +static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm) { - u32 i, s, p, np, v; - int rval = 0, rval2; + unsigned int i; + int rval; - np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; - for (p = 0; p < np; p++) { - rval = smiapp_write( + rval = smiapp_write(sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + return rval; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN); + if (rval) + return rval; + + for (i = 1000; i > 0; i--) { + u32 s; + + rval = smiapp_read( sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); - if (rval) - goto out; + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); - rval = smiapp_write(sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, - SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN); if (rval) - goto out; + return rval; - for (i = 1000; i > 0; i--) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; + } + if (!i) + return -ETIMEDOUT; - if (rval) - goto out; + for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { + u32 v; - if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) - break; + rval = smiapp_read(sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, + &v); + if (rval) + return rval; - } - if (!i) { - rval = -ETIMEDOUT; - goto out; - } + *nvm++ = v; + } - for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, - &v); - if (rval) - goto out; + return 0; +} - *nvm++ = v; - } +static int smiapp_read_nvm(struct smiapp_sensor *sensor, + unsigned char *nvm) +{ + u32 p, np; + int rval = 0, rval2; + + np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; + for (p = 0; p < np && !rval; p++) { + rval = smiapp_read_nvm_page(sensor, p, nvm); + nvm += SMIAPP_NVM_PAGE_SIZE; } -out: rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); if (rval < 0) return rval; -- cgit v1.2.3 From d5e550cf9f292ac889d9a686c4cb5fa040eeab7b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 24 Sep 2019 04:58:01 -0300 Subject: media: smiapp: Add definitions for data transfer if capability bits The data transfer capability register was defined but its bits were not. Do that now. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-reg.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h index 02b0abef6a22..43505cd0616e 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -27,6 +27,9 @@ #define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA BIT(2) #define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE BIT(3) +#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL BIT(2) + #define SMIAPP_SOFTWARE_RESET BIT(0) #define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) -- cgit v1.2.3 From 23fc92fad8ff6829b3139c55d83b659020812016 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 23 Sep 2019 10:18:38 -0300 Subject: media: smiapp: Don't poll for NVM ready on devices that don't need it Only some devices require polling for NVM ready. Do the polling only on devices that need it. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 5a136068f4d6..5a04ae2544aa 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -986,21 +986,27 @@ static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm) if (rval) return rval; - for (i = 1000; i > 0; i--) { - u32 s; + if (sensor->limits[SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY] & + SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL) { + for (i = 1000; i > 0; i--) { + u32 s; - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + rval = smiapp_read( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, + &s); - if (rval) - return rval; + if (rval) + return rval; - if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) - break; + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; + + } + + if (!i) + return -ETIMEDOUT; } - if (!i) - return -ETIMEDOUT; for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { u32 v; -- cgit v1.2.3 From 941e1d36b5b69aa6efcbc8e2bdd0ad349b95d641 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 24 Sep 2019 04:31:05 -0300 Subject: media: smiapp: Support probing NVM size The interface supports probing for the NVM size but this was not implemented in the driver. Do that now. This will also make nokia,nvm-size property redundant. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 103 ++++++++++++++++----------------- drivers/media/i2c/smiapp/smiapp.h | 3 - 2 files changed, 50 insertions(+), 56 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 5a04ae2544aa..a274527987b8 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -971,10 +971,14 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor) * */ -static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm) +static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm, + u8 *status) { unsigned int i; int rval; + u32 s; + + *status = 0; rval = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); @@ -986,10 +990,21 @@ static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm) if (rval) return rval; + rval = smiapp_read(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, + &s); + if (rval) + return rval; + + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE) { + *status = s; + return -ENODATA; + } + if (sensor->limits[SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY] & SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL) { for (i = 1000; i > 0; i--) { - u32 s; + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; rval = smiapp_read( sensor, @@ -998,10 +1013,6 @@ static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm) if (rval) return rval; - - if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) - break; - } if (!i) @@ -1023,23 +1034,27 @@ static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm) return 0; } -static int smiapp_read_nvm(struct smiapp_sensor *sensor, - unsigned char *nvm) +static int smiapp_read_nvm(struct smiapp_sensor *sensor, unsigned char *nvm, + size_t nvm_size) { - u32 p, np; + u8 status = 0; + u32 p; int rval = 0, rval2; - np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; - for (p = 0; p < np && !rval; p++) { - rval = smiapp_read_nvm_page(sensor, p, nvm); + for (p = 0; p < nvm_size / SMIAPP_NVM_PAGE_SIZE && !rval; p++) { + rval = smiapp_read_nvm_page(sensor, p, nvm, &status); nvm += SMIAPP_NVM_PAGE_SIZE; } + if (rval == -ENODATA && + status & SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE) + rval = 0; + rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); if (rval < 0) return rval; else - return rval2; + return rval2 ?: p * SMIAPP_NVM_PAGE_SIZE; } /* @@ -2326,42 +2341,34 @@ smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); struct i2c_client *client = v4l2_get_subdevdata(subdev); struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int nbytes; + int rval; if (!sensor->dev_init_done) return -EBUSY; - if (!sensor->nvm_size) { - int rval; - - /* NVM not read yet - read it now */ - sensor->nvm_size = sensor->hwcfg->nvm_size; + rval = pm_runtime_get_sync(&client->dev); + if (rval < 0) { + if (rval != -EBUSY && rval != -EAGAIN) + pm_runtime_set_active(&client->dev); + pm_runtime_put_noidle(&client->dev); + return -ENODEV; + } - rval = pm_runtime_get_sync(&client->dev); - if (rval < 0) { - if (rval != -EBUSY && rval != -EAGAIN) - pm_runtime_set_active(&client->dev); - pm_runtime_put_noidle(&client->dev); - return -ENODEV; - } + rval = smiapp_read_nvm(sensor, buf, PAGE_SIZE); + if (rval < 0) { + pm_runtime_put(&client->dev); + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } - if (smiapp_read_nvm(sensor, sensor->nvm)) { - pm_runtime_put(&client->dev); - dev_err(&client->dev, "nvm read failed\n"); - return -ENODEV; - } + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); - pm_runtime_mark_last_busy(&client->dev); - pm_runtime_put_autosuspend(&client->dev); - } /* * NVM is still way below a PAGE_SIZE, so we can safely * assume this for now. */ - nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); - memcpy(buf, sensor->nvm, nbytes); - - return nbytes; + return rval; } static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); @@ -2825,16 +2832,13 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) } } - /* NVM size is not mandatory */ - fwnode_property_read_u32(fwnode, "nokia,nvm-size", &hwcfg->nvm_size); - rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &hwcfg->ext_clk); if (rval) dev_info(dev, "can't get clock-frequency\n"); - dev_dbg(dev, "nvm %d, clk %d, mode %d\n", - hwcfg->nvm_size, hwcfg->ext_clk, hwcfg->csi_signalling_mode); + dev_dbg(dev, "clk %d, mode %d\n", hwcfg->ext_clk, + hwcfg->csi_signalling_mode); if (!bus_cfg.nr_of_link_frequencies) { dev_warn(dev, "no link frequencies defined\n"); @@ -3018,17 +3022,10 @@ static int smiapp_probe(struct i2c_client *client) rval = -ENOENT; goto out_power_off; } - /* SMIA++ NVM initialization - it will be read from the sensor - * when it is first requested by userspace. - */ - if (sensor->minfo.smiapp_version && sensor->hwcfg->nvm_size) { - sensor->nvm = devm_kzalloc(&client->dev, - sensor->hwcfg->nvm_size, GFP_KERNEL); - if (sensor->nvm == NULL) { - rval = -ENOMEM; - goto out_cleanup; - } + if (sensor->minfo.smiapp_version && + sensor->limits[SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY] & + SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED) { if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { dev_err(&client->dev, "sysfs nvm entry failed\n"); rval = -EBUSY; diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index ecf8a17dbe37..3ab874a5deba 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -208,9 +208,6 @@ struct smiapp_sensor { bool dev_init_done; u8 compressed_min_bpp; - u8 *nvm; /* nvm memory buffer */ - unsigned int nvm_size; /* bytes */ - struct smiapp_module_info minfo; struct smiapp_pll pll; -- cgit v1.2.3 From 4a9a75923acb7f6333a0f3c4090d20709710067a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 17 Oct 2019 08:17:05 -0300 Subject: media: smiapp: Destroy sensor's mutex Destroy the mutex initialised by the driver in probe. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index a274527987b8..ea118e04dcdb 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2881,7 +2881,6 @@ static int smiapp_probe(struct i2c_client *client) return -ENOMEM; sensor->hwcfg = hwcfg; - mutex_init(&sensor->mutex); sensor->src = &sensor->ssds[sensor->ssds_used]; v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); @@ -2945,6 +2944,8 @@ static int smiapp_probe(struct i2c_client *client) if (rval < 0) return rval; + mutex_init(&sensor->mutex); + rval = smiapp_identify_module(sensor); if (rval) { rval = -ENODEV; @@ -3134,6 +3135,7 @@ out_cleanup: out_power_off: smiapp_power_off(&client->dev); + mutex_destroy(&sensor->mutex); return rval; } @@ -3156,6 +3158,7 @@ static int smiapp_remove(struct i2c_client *client) media_entity_cleanup(&sensor->ssds[i].sd.entity); } smiapp_cleanup(sensor); + mutex_destroy(&sensor->mutex); return 0; } -- cgit v1.2.3 From b0388c0727679f9e96f999ca712e6a263ffc00fb Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 1 Oct 2019 08:31:38 -0300 Subject: media: smiapp: Don't get binning limits dynamically The driver implementation assumed the binning limits could change dynamically based on the binning configuration. This is not actually the case; these limits are static and suitable to be used with all binning configurations but possibly not optimal limit for many of those configurations. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 65 ---------------------------------- 1 file changed, 65 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index ea118e04dcdb..b490761bf174 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -682,66 +682,6 @@ static int smiapp_get_all_limits(struct smiapp_sensor *sensor) return 0; } -static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - static u32 const limits[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, - }; - static u32 const limits_replace[] = { - SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, - SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, - SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, - }; - unsigned int i; - int rval; - - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == - SMIAPP_BINNING_CAPABILITY_NO) { - for (i = 0; i < ARRAY_SIZE(limits); i++) - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - - return 0; - } - - rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); - if (rval < 0) - return rval; - - /* - * Sanity check whether the binning limits are valid. If not, - * use the non-binning ones. - */ - if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) - return 0; - - for (i = 0; i < ARRAY_SIZE(limits); i++) { - dev_dbg(&client->dev, - "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limits[i]].addr, - smiapp_reg_limits[limits[i]].what, - sensor->limits[limits_replace[i]], - sensor->limits[limits_replace[i]]); - sensor->limits[limits[i]] = - sensor->limits[limits_replace[i]]; - } - - return 0; -} - static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); @@ -940,11 +880,6 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor) if (rval < 0) return rval; - /* Get updated limits due to binning */ - rval = smiapp_get_limits_binning(sensor); - if (rval < 0) - return rval; - rval = smiapp_pll_update(sensor); if (rval < 0) return rval; -- cgit v1.2.3 From f8c4352c1bef308578c2e310a37610db10c81332 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 1 Oct 2019 08:34:55 -0300 Subject: media: smiapp: Move binning configuration to streaming start Only write the binning configuration at stream start. It has no effect otherwise. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index b490761bf174..9986215fed89 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -857,29 +857,8 @@ static void smiapp_update_blanking(struct smiapp_sensor *sensor) static int smiapp_update_mode(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int binning_mode; int rval; - /* Binning has to be set up here; it affects limits */ - if (sensor->binning_horizontal == 1 && - sensor->binning_vertical == 1) { - binning_mode = 0; - } else { - u8 binning_type = - (sensor->binning_horizontal << 4) - | sensor->binning_vertical; - - rval = smiapp_write( - sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); - if (rval < 0) - return rval; - - binning_mode = 1; - } - rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); - if (rval < 0) - return rval; - rval = smiapp_pll_update(sensor); if (rval < 0) return rval; @@ -1351,6 +1330,7 @@ static int smiapp_power_off(struct device *dev) static int smiapp_start_streaming(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; int rval; mutex_lock(&sensor->mutex); @@ -1361,6 +1341,27 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) if (rval) goto out; + /* Binning configuration */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = smiapp_write( + sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + if (rval < 0) + return rval; + + binning_mode = 1; + } + rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + if (rval < 0) + return rval; + + /* Set up PLL */ rval = smiapp_pll_configure(sensor); if (rval) goto out; -- cgit v1.2.3 From da533bb0058b575958291b9296f1de7d1b1b5bc3 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 1 Oct 2019 08:46:51 -0300 Subject: media: smiapp: Don't update sensor configuration during power-on init The sensor configuration since it was previously powered off was not changed, so no need to update the PLL configuration etc. What is necessary though is to re-apply the configuration to the sensor's registers. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 9986215fed89..1fad05583feb 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1267,10 +1267,6 @@ static int smiapp_power_on(struct device *dev) rval = __v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); if (rval) goto out_cci_addr_fail; - - rval = smiapp_update_mode(sensor); - if (rval < 0) - goto out_cci_addr_fail; } mutex_unlock(&sensor->mutex); -- cgit v1.2.3 From 579d1f7d4fb90b7d06be96ac67b4665dc0d1bcb7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 1 Oct 2019 09:00:32 -0300 Subject: media: smiapp: Use non-binned and binned limits correctly Use non-binned limits when binning is disabled and binned when they're enabled. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 1fad05583feb..19d722aa1e9b 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -831,23 +831,36 @@ static void smiapp_update_blanking(struct smiapp_sensor *sensor) { struct v4l2_ctrl *vblank = sensor->vblank; struct v4l2_ctrl *hblank = sensor->hblank; + uint16_t min_fll, max_fll, min_llp, max_llp, min_lbp; int min, max; + if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) { + min_fll = sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN]; + max_fll = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN]; + min_llp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN]; + max_llp = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN]; + min_lbp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]; + } else { + min_fll = sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES]; + max_fll = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES]; + min_llp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK]; + max_llp = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK]; + min_lbp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK]; + } + min = max_t(int, sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + min_fll - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); - max = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; + max = max_fll - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); min = max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + min_llp - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, - sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); - max = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; + min_lbp); + max = max_llp - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); -- cgit v1.2.3 From 90c9e4a4dba9f4de331372e745fb1991c1faa598 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 11 Oct 2019 08:16:02 -0300 Subject: media: smiapp: Register sensor after enabling runtime PM on the device Earlier it was possible that the parts of the driver that assumed runtime PM was enabled were being called before runtime PM was enabled in the driver's probe function. So enable runtime PM before registering the sub-device. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 19d722aa1e9b..6d4b749b48b4 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -3059,19 +3059,23 @@ static int smiapp_probe(struct i2c_client *client) if (rval < 0) goto out_media_entity_cleanup; - rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd); - if (rval < 0) - goto out_media_entity_cleanup; - pm_runtime_set_active(&client->dev); pm_runtime_get_noresume(&client->dev); pm_runtime_enable(&client->dev); + + rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd); + if (rval < 0) + goto out_disable_runtime_pm; + pm_runtime_set_autosuspend_delay(&client->dev, 1000); pm_runtime_use_autosuspend(&client->dev); pm_runtime_put_autosuspend(&client->dev); return 0; +out_disable_runtime_pm: + pm_runtime_disable(&client->dev); + out_media_entity_cleanup: media_entity_cleanup(&sensor->src->sd.entity); -- cgit v1.2.3 From d3bec7fc117922b9cf7a2c5b784efd5357695448 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 Oct 2019 07:51:22 -0300 Subject: media: smiapp: Rename update_mode as pll_blanking_update Rename the confusingly named smiapp_update_mode() function as smiapp_pll_blanking_update(). The function is used to calculate new PLL and blanking configuration after binning or scaling configuration has been changed. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 6d4b749b48b4..77dfce7c3be9 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -867,7 +867,7 @@ static void smiapp_update_blanking(struct smiapp_sensor *sensor) __smiapp_update_exposure_limits(sensor); } -static int smiapp_update_mode(struct smiapp_sensor *sensor) +static int smiapp_pll_blanking_update(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; @@ -2047,7 +2047,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev, smiapp_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return smiapp_update_mode(sensor); + return smiapp_pll_blanking_update(sensor); return 0; } @@ -3044,7 +3044,7 @@ static int smiapp_probe(struct i2c_client *client) } mutex_lock(&sensor->mutex); - rval = smiapp_update_mode(sensor); + rval = smiapp_pll_blanking_update(sensor); mutex_unlock(&sensor->mutex); if (rval) { dev_err(&client->dev, "update mode failed\n"); -- cgit v1.2.3 From 33657a0a3eec844ebca3d12929e2dd2f57a7a1dc Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Sun, 15 Sep 2019 08:14:58 -0300 Subject: media: tuners: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header file related to media Drivers for Analog TV Tuners. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tuner-xc2028-types.h | 2 +- drivers/media/tuners/tuner-xc2028.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/tuners/tuner-xc2028-types.h b/drivers/media/tuners/tuner-xc2028-types.h index 50d017a4822a..fcca39d3e006 100644 --- a/drivers/media/tuners/tuner-xc2028-types.h +++ b/drivers/media/tuners/tuner-xc2028-types.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * tuner-xc2028_types * * This file includes internal tipes to be used inside tuner-xc2028. diff --git a/drivers/media/tuners/tuner-xc2028.h b/drivers/media/tuners/tuner-xc2028.h index 7b58bc06e35c..2dd45d0765d7 100644 --- a/drivers/media/tuners/tuner-xc2028.h +++ b/drivers/media/tuners/tuner-xc2028.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * tuner-xc2028 * * Copyright (c) 2007-2008 Mauro Carvalho Chehab -- cgit v1.2.3 From f3f5ba42c58d56d50f539854d8cc188944e96087 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 16 Oct 2019 14:19:15 -0300 Subject: media: imon: invalid dereference in imon_touch_event The touch timer is set up in intf1. If the second interface does not exist, the timer and touch input device are not setup and we get the following error, when touch events are reported via intf0. kernel BUG at kernel/time/timer.c:956! invalid opcode: 0000 [#1] SMP KASAN CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.0-rc1+ #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:__mod_timer kernel/time/timer.c:956 [inline] RIP: 0010:__mod_timer kernel/time/timer.c:949 [inline] RIP: 0010:mod_timer+0x5a2/0xb50 kernel/time/timer.c:1100 Code: 45 10 c7 44 24 14 ff ff ff ff 48 89 44 24 08 48 8d 45 20 48 c7 44 24 18 00 00 00 00 48 89 04 24 e9 5a fc ff ff e8 ae ce 0e 00 <0f> 0b e8 a7 ce 0e 00 4c 89 74 24 20 e9 37 fe ff ff e8 98 ce 0e 00 RSP: 0018:ffff8881db209930 EFLAGS: 00010006 RAX: ffffffff86c2b200 RBX: 00000000ffffa688 RCX: ffffffff83efc583 RDX: 0000000000000100 RSI: ffffffff812f4d82 RDI: ffff8881d2356200 RBP: ffff8881d23561e8 R08: ffffffff86c2b200 R09: ffffed103a46abeb R10: ffffed103a46abea R11: ffff8881d2355f53 R12: dffffc0000000000 R13: 1ffff1103b64132d R14: ffff8881d2355f50 R15: 0000000000000006 FS: 0000000000000000(0000) GS:ffff8881db200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f75e2799000 CR3: 00000001d3b07000 CR4: 00000000001406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: imon_touch_event drivers/media/rc/imon.c:1348 [inline] imon_incoming_packet.isra.0+0x2546/0x2f10 drivers/media/rc/imon.c:1603 usb_rx_callback_intf0+0x151/0x1e0 drivers/media/rc/imon.c:1734 __usb_hcd_giveback_urb+0x1f2/0x470 drivers/usb/core/hcd.c:1654 usb_hcd_giveback_urb+0x368/0x420 drivers/usb/core/hcd.c:1719 dummy_timer+0x120f/0x2fa2 drivers/usb/gadget/udc/dummy_hcd.c:1965 call_timer_fn+0x179/0x650 kernel/time/timer.c:1404 expire_timers kernel/time/timer.c:1449 [inline] __run_timers kernel/time/timer.c:1773 [inline] __run_timers kernel/time/timer.c:1740 [inline] run_timer_softirq+0x5e3/0x1490 kernel/time/timer.c:1786 __do_softirq+0x221/0x912 kernel/softirq.c:292 invoke_softirq kernel/softirq.c:373 [inline] irq_exit+0x178/0x1a0 kernel/softirq.c:413 exiting_irq arch/x86/include/asm/apic.h:536 [inline] smp_apic_timer_interrupt+0x12f/0x500 arch/x86/kernel/apic/apic.c:1137 apic_timer_interrupt+0xf/0x20 arch/x86/entry/entry_64.S:830 RIP: 0010:default_idle+0x28/0x2e0 arch/x86/kernel/process.c:581 Code: 90 90 41 56 41 55 65 44 8b 2d 44 3a 8f 7a 41 54 55 53 0f 1f 44 00 00 e8 36 ee d0 fb e9 07 00 00 00 0f 00 2d fa dd 4f 00 fb f4 <65> 44 8b 2d 20 3a 8f 7a 0f 1f 44 00 00 5b 5d 41 5c 41 5d 41 5e c3 RSP: 0018:ffffffff86c07da8 EFLAGS: 00000246 ORIG_RAX: ffffffffffffff13 RAX: 0000000000000007 RBX: ffffffff86c2b200 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000006 RDI: ffffffff86c2ba4c RBP: fffffbfff0d85640 R08: ffffffff86c2b200 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 cpuidle_idle_call kernel/sched/idle.c:154 [inline] do_idle+0x3b6/0x500 kernel/sched/idle.c:263 cpu_startup_entry+0x14/0x20 kernel/sched/idle.c:355 start_kernel+0x82a/0x864 init/main.c:784 secondary_startup_64+0xa4/0xb0 arch/x86/kernel/head_64.S:241 Modules linked in: Reported-by: syzbot+f49d12d34f2321cf4df2@syzkaller.appspotmail.com Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index b8d96c50a804..ed95244da894 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1627,8 +1627,7 @@ static void imon_incoming_packet(struct imon_context *ictx, spin_unlock_irqrestore(&ictx->kc_lock, flags); /* send touchscreen events through input subsystem if touchpad data */ - if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 && - buf[7] == 0x86) { + if (ictx->touch && len == 8 && buf[7] == 0x86) { imon_touch_event(ictx, buf); return; -- cgit v1.2.3 From 817d0b3278f52d4d20b9706e41c808287ee909e4 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 7 Oct 2019 10:49:59 -0300 Subject: media: vimc: initialize vim entity pointers to NULL since NULL value for vimc entity pointer indicates that entity creation failed and this is tested, the pointers should be initialized to NULL. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index 2d20a7c10398..65048bd7d80b 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -199,9 +199,8 @@ static int vimc_register_devices(struct vimc_device *vimc) } /* allocate ent_devs */ - vimc->ent_devs = kmalloc_array(vimc->pipe_cfg->num_ents, - sizeof(*vimc->ent_devs), - GFP_KERNEL); + vimc->ent_devs = kcalloc(vimc->pipe_cfg->num_ents, + sizeof(*vimc->ent_devs), GFP_KERNEL); if (!vimc->ent_devs) { ret = -ENOMEM; goto err_v4l2_unregister; -- cgit v1.2.3 From 4996992c4f7410e9a43aa3047e8034756cece21a Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 7 Oct 2019 10:50:00 -0300 Subject: media: vimc: cleanup code that assigns entity in entities array Since the add callback returns NULL on failure and the array is initialized to NULLs, there is no need for the intermediate assignment to local var. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index 65048bd7d80b..97a272f3350a 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -160,19 +160,17 @@ err_rm_links: static int vimc_add_subdevs(struct vimc_device *vimc) { unsigned int i; - struct vimc_ent_device *ved; for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { dev_dbg(&vimc->pdev.dev, "new entity for %s\n", vimc->pipe_cfg->ents[i].name); - ved = vimc->pipe_cfg->ents[i].add(vimc, + vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].add(vimc, vimc->pipe_cfg->ents[i].name); - if (!ved) { + if (!vimc->ent_devs[i]) { dev_err(&vimc->pdev.dev, "add new entity for %s\n", vimc->pipe_cfg->ents[i].name); return -EINVAL; } - vimc->ent_devs[i] = ved; } return 0; } -- cgit v1.2.3 From 291aca4e7bdd87a01e21d90382fd0b231b280272 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 7 Oct 2019 10:50:01 -0300 Subject: media: vimc: sen: register subdevice only after initialization vimc_sen_add function first registers the subdevice and then calls tpg_alloc. If tpg_alloc fails it unregisters the subdevice and then frees vsen, this cause double free since the release callback that follows subdevice unregistration also frees vsen. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-sensor.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 46dc6a535abe..ee2306c08569 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -358,6 +358,13 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, goto err_free_vsen; } + /* Initialize the test pattern generator */ + tpg_init(&vsen->tpg, vsen->mbus_format.width, + vsen->mbus_format.height); + ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH); + if (ret) + goto err_free_hdl; + /* Initialize ved and sd */ ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, vcfg_name, @@ -365,7 +372,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE}, &vimc_sen_int_ops, &vimc_sen_ops); if (ret) - goto err_free_hdl; + goto err_free_tpg; vsen->ved.process_frame = vimc_sen_process_frame; vsen->dev = &vimc->pdev.dev; @@ -373,17 +380,10 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, /* Initialize the frame format */ vsen->mbus_format = fmt_default; - /* Initialize the test pattern generator */ - tpg_init(&vsen->tpg, vsen->mbus_format.width, - vsen->mbus_format.height); - ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH); - if (ret) - goto err_unregister_ent_sd; - return &vsen->ved; -err_unregister_ent_sd: - vimc_ent_sd_unregister(&vsen->ved, &vsen->sd); +err_free_tpg: + tpg_free(&vsen->tpg); err_free_hdl: v4l2_ctrl_handler_free(&vsen->hdl); err_free_vsen: -- cgit v1.2.3 From 9fb82aaa85d3d4b7be80a15dd0e740aab45bc491 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 7 Oct 2019 10:50:02 -0300 Subject: media: vimc: move media_entity_cleanup to release callbacks according to the docs, this function must be called during the cleanup phase after unregistering the entity. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 2 +- drivers/media/platform/vimc/vimc-common.c | 1 - drivers/media/platform/vimc/vimc-debayer.c | 1 + drivers/media/platform/vimc/vimc-scaler.c | 1 + drivers/media/platform/vimc/vimc-sensor.c | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 602f80323031..5f353c20e605 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -330,6 +330,7 @@ static void vimc_cap_release(struct video_device *vdev) struct vimc_cap_device *vcap = container_of(vdev, struct vimc_cap_device, vdev); + media_entity_cleanup(vcap->ved.ent); vimc_pads_cleanup(vcap->ved.pads); kfree(vcap); } @@ -340,7 +341,6 @@ void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) vcap = container_of(ved, struct vimc_cap_device, ved); vb2_queue_release(&vcap->queue); - media_entity_cleanup(ved->ent); video_unregister_device(&vcap->vdev); } diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index a3120f4f7a90..999bc353fb10 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -423,7 +423,6 @@ EXPORT_SYMBOL_GPL(vimc_ent_sd_register); void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) { - media_entity_cleanup(ved->ent); v4l2_device_unregister_subdev(sd); } EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index feac47d79449..e1bad6713cde 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -477,6 +477,7 @@ static void vimc_deb_release(struct v4l2_subdev *sd) struct vimc_deb_device *vdeb = container_of(sd, struct vimc_deb_device, sd); + media_entity_cleanup(vdeb->ved.ent); vimc_pads_cleanup(vdeb->ved.pads); kfree(vdeb); } diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index a6a3cc5be872..1982bc089af5 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -336,6 +336,7 @@ static void vimc_sca_release(struct v4l2_subdev *sd) struct vimc_sca_device *vsca = container_of(sd, struct vimc_sca_device, sd); + media_entity_cleanup(vsca->ved.ent); vimc_pads_cleanup(vsca->ved.pads); kfree(vsca); } diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index ee2306c08569..63fe024ccea5 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -291,6 +291,7 @@ static void vimc_sen_release(struct v4l2_subdev *sd) v4l2_ctrl_handler_free(&vsen->hdl); tpg_free(&vsen->tpg); + media_entity_cleanup(vsen->ved.ent); vimc_pads_cleanup(vsen->ved.pads); kfree(vsen); } -- cgit v1.2.3 From b4aa975cbd8775cd7b0e68531b2ff9a16e215181 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 7 Oct 2019 10:50:03 -0300 Subject: media: vimc: remove the helper function vimc_ent_sd_unregister since this function only calls v4l2_device_unregister_subdev, it is pointless. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 5 ----- drivers/media/platform/vimc/vimc-common.h | 12 ------------ drivers/media/platform/vimc/vimc-debayer.c | 2 +- drivers/media/platform/vimc/vimc-scaler.c | 2 +- drivers/media/platform/vimc/vimc-sensor.c | 2 +- 5 files changed, 3 insertions(+), 20 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 999bc353fb10..67b53dc1849d 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -421,8 +421,3 @@ err_clean_pads: } EXPORT_SYMBOL_GPL(vimc_ent_sd_register); -void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) -{ - v4l2_device_unregister_subdev(sd); -} -EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister); diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 698db7c07645..af5b1166dc1f 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -251,18 +251,6 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, const struct v4l2_subdev_internal_ops *sd_int_ops, const struct v4l2_subdev_ops *sd_ops); -/** - * vimc_ent_sd_unregister - cleanup and unregister a subdev node - * - * @ved: the vimc_ent_device struct to be cleaned up - * @sd: the v4l2_subdev struct to be unregistered - * - * Helper function cleanup and unregister the struct vimc_ent_device and struct - * v4l2_subdev which represents a subdev node in the topology - */ -void vimc_ent_sd_unregister(struct vimc_ent_device *ved, - struct v4l2_subdev *sd); - /** * vimc_link_validate - validates a media link * diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index e1bad6713cde..4e5316c671e0 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -491,7 +491,7 @@ void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) struct vimc_deb_device *vdeb; vdeb = container_of(ved, struct vimc_deb_device, ved); - vimc_ent_sd_unregister(ved, &vdeb->sd); + v4l2_device_unregister_subdev(&vdeb->sd); } struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index 1982bc089af5..4fe2ba578652 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -350,7 +350,7 @@ void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) struct vimc_sca_device *vsca; vsca = container_of(ved, struct vimc_sca_device, ved); - vimc_ent_sd_unregister(ved, &vsca->sd); + v4l2_device_unregister_subdev(&vsca->sd); } struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 63fe024ccea5..14838362d871 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -305,7 +305,7 @@ void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) struct vimc_sen_device *vsen; vsen = container_of(ved, struct vimc_sen_device, ved); - vimc_ent_sd_unregister(ved, &vsen->sd); + v4l2_device_unregister_subdev(&vsen->sd); } /* Image Processing Controls */ -- cgit v1.2.3 From 3b04de4e7a56caf40c6d84994f58a4e5e985a6cd Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 22 Oct 2019 05:46:07 -0300 Subject: media: vimc: remove EXPORT_SYMBOL_GPL declarations vimc is a single kernel module and does not need to export any symbols therefore there is no need for these declarations. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 8 -------- drivers/media/platform/vimc/vimc-streamer.c | 1 - 2 files changed, 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 67b53dc1849d..611f4e0448b8 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -171,7 +171,6 @@ const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i) return &vimc_pix_map_list[i]; } -EXPORT_SYMBOL_GPL(vimc_pix_map_by_index); const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) { @@ -183,7 +182,6 @@ const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) } return NULL; } -EXPORT_SYMBOL_GPL(vimc_pix_map_by_code); const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) { @@ -195,7 +193,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) } return NULL; } -EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat); /* Helper function to allocate and initialize pads */ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) @@ -216,7 +213,6 @@ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) return pads; } -EXPORT_SYMBOL_GPL(vimc_pads_init); int vimc_pipeline_s_stream(struct media_entity *ent, int enable) { @@ -245,7 +241,6 @@ int vimc_pipeline_s_stream(struct media_entity *ent, int enable) return 0; } -EXPORT_SYMBOL_GPL(vimc_pipeline_s_stream); static int vimc_get_mbus_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) @@ -357,7 +352,6 @@ int vimc_link_validate(struct media_link *link) return 0; } -EXPORT_SYMBOL_GPL(vimc_link_validate); static const struct media_entity_operations vimc_ent_sd_mops = { .link_validate = vimc_link_validate, @@ -419,5 +413,3 @@ err_clean_pads: vimc_pads_cleanup(ved->pads); return ret; } -EXPORT_SYMBOL_GPL(vimc_ent_sd_register); - diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index faa2879c25df..092833623ac1 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -216,4 +216,3 @@ int vimc_streamer_s_stream(struct vimc_stream *stream, return 0; } -EXPORT_SYMBOL_GPL(vimc_streamer_s_stream); -- cgit v1.2.3 From af2bdbbe2d4314a0dd85c4f7243d3ac7893078c4 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 22 Oct 2019 05:46:08 -0300 Subject: media: vimc: common: remove unused function 'vimc_pipeline_s_stream' The function 'vimc_pipeline_s_stream' is not used and can be removed. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 28 ---------------------------- drivers/media/platform/vimc/vimc-common.h | 11 ----------- 2 files changed, 39 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 611f4e0448b8..31bd198f16fe 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -214,34 +214,6 @@ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) return pads; } -int vimc_pipeline_s_stream(struct media_entity *ent, int enable) -{ - struct v4l2_subdev *sd; - struct media_pad *pad; - unsigned int i; - int ret; - - for (i = 0; i < ent->num_pads; i++) { - if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE) - continue; - - /* Start the stream in the subdevice direct connected */ - pad = media_entity_remote_pad(&ent->pads[i]); - if (!pad) - continue; - - if (!is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; - - sd = media_entity_to_v4l2_subdev(pad->entity); - ret = v4l2_subdev_call(sd, video, s_stream, enable); - if (ret && ret != -ENOIOCTLCMD) - return ret; - } - - return 0; -} - static int vimc_get_mbus_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) { diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index af5b1166dc1f..c4471e72ad2b 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -192,17 +192,6 @@ static inline void vimc_pads_cleanup(struct media_pad *pads) kfree(pads); } -/** - * vimc_pipeline_s_stream - start stream through the pipeline - * - * @ent: the pointer to struct media_entity for the node - * @enable: 1 to start the stream and 0 to stop - * - * Helper function to call the s_stream of the subdevices connected - * in all the sink pads of the entity - */ -int vimc_pipeline_s_stream(struct media_entity *ent, int enable); - /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index * -- cgit v1.2.3 From ad1cec89db964cc3391fa67b1d1d93482727a439 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Wed, 9 Oct 2019 16:09:26 -0300 Subject: media: vimc: remove unused struct declaration vimc_platform_data the struct vimc_platform_data is not used anymore and can be removed. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.h | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index c4471e72ad2b..1f8da4f8d4db 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -57,21 +57,6 @@ do { \ (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \ } while (0) -/** - * struct vimc_platform_data - platform data to components - * - * @entity_name: The name of the entity to be created - * - * Board setup code will often provide additional information using the device's - * platform_data field to hold additional information. - * When injecting a new platform_device in the component system the core needs - * to provide to the corresponding submodules the name of the entity that should - * be used when registering the subdevice in the Media Controller system. - */ -struct vimc_platform_data { - char entity_name[32]; -}; - /** * struct vimc_pix_map - maps media bus code with v4l2 pixel format * -- cgit v1.2.3 From 23df45d038662da2b1e017cf38165a88dfd7f543 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 3 Oct 2019 09:59:42 -0300 Subject: media: vimc: embed the pads of entities in the entities' structs since the pads array is of known small size, there is no reason to allocate it separately. Instead, it is embedded in the entity struct. This also conforms to the media controller doc: 'Most drivers will embed the pads array in a driver-specific structure, avoiding dynamic allocation.' Signed-off-by: Dafna Hirschfeld [hverkuil-cisco@xs4all.nl: remove unused vimc_pads_init()] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 17 ++++----------- drivers/media/platform/vimc/vimc-common.c | 33 +++--------------------------- drivers/media/platform/vimc/vimc-common.h | 33 +++++------------------------- drivers/media/platform/vimc/vimc-debayer.c | 8 +++++--- drivers/media/platform/vimc/vimc-scaler.c | 8 +++++--- drivers/media/platform/vimc/vimc-sensor.c | 6 +++--- 6 files changed, 25 insertions(+), 80 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 5f353c20e605..936bfb96ebaa 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -30,6 +30,7 @@ struct vimc_cap_device { struct mutex lock; u32 sequence; struct vimc_stream stream; + struct media_pad pad; }; static const struct v4l2_pix_format fmt_default = { @@ -331,7 +332,6 @@ static void vimc_cap_release(struct video_device *vdev) container_of(vdev, struct vimc_cap_device, vdev); media_entity_cleanup(vcap->ved.ent); - vimc_pads_cleanup(vcap->ved.pads); kfree(vcap); } @@ -398,21 +398,14 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, if (!vcap) return NULL; - /* Allocate the pads */ - vcap->ved.pads = - vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK}); - if (IS_ERR(vcap->ved.pads)) { - ret = PTR_ERR(vcap->ved.pads); - goto err_free_vcap; - } - /* Initialize the media entity */ vcap->vdev.entity.name = vcfg_name; vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + vcap->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vcap->vdev.entity, - 1, vcap->ved.pads); + 1, &vcap->pad); if (ret) - goto err_clean_pads; + goto err_free_vcap; /* Initialize the lock */ mutex_init(&vcap->lock); @@ -481,8 +474,6 @@ err_release_queue: vb2_queue_release(q); err_clean_m_ent: media_entity_cleanup(&vcap->vdev.entity); -err_clean_pads: - vimc_pads_cleanup(vcap->ved.pads); err_free_vcap: kfree(vcap); diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 31bd198f16fe..2a0c40e9ae88 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -194,26 +194,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) return NULL; } -/* Helper function to allocate and initialize pads */ -struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) -{ - struct media_pad *pads; - unsigned int i; - - /* Allocate memory for the pads */ - pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL); - if (!pads) - return ERR_PTR(-ENOMEM); - - /* Initialize the pads */ - for (i = 0; i < num_pads; i++) { - pads[i].index = i; - pads[i].flags = pads_flag[i]; - } - - return pads; -} - static int vimc_get_mbus_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) { @@ -335,17 +315,12 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, const char *const name, u32 function, u16 num_pads, - const unsigned long *pads_flag, + struct media_pad *pads, const struct v4l2_subdev_internal_ops *sd_int_ops, const struct v4l2_subdev_ops *sd_ops) { int ret; - /* Allocate the pads. Should be released from the sd_int_op release */ - ved->pads = vimc_pads_init(num_pads, pads_flag); - if (IS_ERR(ved->pads)) - return PTR_ERR(ved->pads); - /* Fill the vimc_ent_device struct */ ved->ent = &sd->entity; @@ -364,9 +339,9 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; /* Initialize the media entity */ - ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); + ret = media_entity_pads_init(&sd->entity, num_pads, pads); if (ret) - goto err_clean_pads; + return ret; /* Register the subdev with the v4l2 and the media framework */ ret = v4l2_device_register_subdev(v4l2_dev, sd); @@ -381,7 +356,5 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, err_clean_m_ent: media_entity_cleanup(&sd->entity); -err_clean_pads: - vimc_pads_cleanup(ved->pads); return ret; } diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 1f8da4f8d4db..ac01182e8b27 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -75,10 +75,10 @@ struct vimc_pix_map { }; /** - * struct vimc_ent_device - core struct that represents a node in the topology + * struct vimc_ent_device - core struct that represents an entity in the + * topology * * @ent: the pointer to struct media_entity for the node - * @pads: the list of pads of the node * @process_frame: callback send a frame to that node * @vdev_get_format: callback that returns the current format a pad, used * only when is_media_entity_v4l2_video_device(ent) returns @@ -94,7 +94,6 @@ struct vimc_pix_map { */ struct vimc_ent_device { struct media_entity *ent; - struct media_pad *pads; void * (*process_frame)(struct vimc_ent_device *ved, const void *frame); void (*vdev_get_format)(struct vimc_ent_device *ved, @@ -154,29 +153,6 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, const char *vcfg_name); void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); -/** - * vimc_pads_init - initialize pads - * - * @num_pads: number of pads to initialize - * @pads_flags: flags to use in each pad - * - * Helper functions to allocate/initialize pads - */ -struct media_pad *vimc_pads_init(u16 num_pads, - const unsigned long *pads_flag); - -/** - * vimc_pads_cleanup - free pads - * - * @pads: pointer to the pads - * - * Helper function to free the pads initialized with vimc_pads_init - */ -static inline void vimc_pads_cleanup(struct media_pad *pads) -{ - kfree(pads); -} - /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index * @@ -208,7 +184,8 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); * unique. * @function: media entity function defined by MEDIA_ENT_F_* macros * @num_pads: number of pads to initialize - * @pads_flag: flags to use in each pad + * @pads: the array of pads of the entity, the caller should set the + flags of the pads * @sd_int_ops: pointer to &struct v4l2_subdev_internal_ops * @sd_ops: pointer to &struct v4l2_subdev_ops. * @@ -221,7 +198,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, const char *const name, u32 function, u16 num_pads, - const unsigned long *pads_flag, + struct media_pad *pads, const struct v4l2_subdev_internal_ops *sd_int_ops, const struct v4l2_subdev_ops *sd_ops); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 4e5316c671e0..a601ca3a0a54 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -44,6 +44,7 @@ struct vimc_deb_device { u8 *src_frame; const struct vimc_deb_pix_map *sink_pix_map; unsigned int sink_bpp; + struct media_pad pads[2]; }; static const struct v4l2_mbus_framefmt sink_fmt_default = { @@ -478,7 +479,6 @@ static void vimc_deb_release(struct v4l2_subdev *sd) container_of(sd, struct vimc_deb_device, sd); media_entity_cleanup(vdeb->ved.ent); - vimc_pads_cleanup(vdeb->ved.pads); kfree(vdeb); } @@ -507,11 +507,13 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, return NULL; /* Initialize ved and sd */ + vdeb->pads[0].flags = MEDIA_PAD_FL_SINK; + vdeb->pads[1].flags = MEDIA_PAD_FL_SOURCE; + ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - (const unsigned long[2]) {MEDIA_PAD_FL_SINK, - MEDIA_PAD_FL_SOURCE}, + vdeb->pads, &vimc_deb_int_ops, &vimc_deb_ops); if (ret) { kfree(vdeb); diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index 4fe2ba578652..88a2f6e3218e 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -30,6 +30,7 @@ struct vimc_sca_device { u8 *src_frame; unsigned int src_line_size; unsigned int bpp; + struct media_pad pads[2]; }; static const struct v4l2_mbus_framefmt sink_fmt_default = { @@ -337,7 +338,6 @@ static void vimc_sca_release(struct v4l2_subdev *sd) container_of(sd, struct vimc_sca_device, sd); media_entity_cleanup(vsca->ved.ent); - vimc_pads_cleanup(vsca->ved.pads); kfree(vsca); } @@ -366,11 +366,13 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, return NULL; /* Initialize ved and sd */ + vsca->pads[0].flags = MEDIA_PAD_FL_SINK; + vsca->pads[1].flags = MEDIA_PAD_FL_SOURCE; + ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - (const unsigned long[2]) {MEDIA_PAD_FL_SINK, - MEDIA_PAD_FL_SOURCE}, + vsca->pads, &vimc_sca_int_ops, &vimc_sca_ops); if (ret) { kfree(vsca); diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 14838362d871..b41e24a7d029 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -24,6 +24,7 @@ struct vimc_sen_device { /* The active format */ struct v4l2_mbus_framefmt mbus_format; struct v4l2_ctrl_handler hdl; + struct media_pad pad; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -292,7 +293,6 @@ static void vimc_sen_release(struct v4l2_subdev *sd) v4l2_ctrl_handler_free(&vsen->hdl); tpg_free(&vsen->tpg); media_entity_cleanup(vsen->ved.ent); - vimc_pads_cleanup(vsen->ved.pads); kfree(vsen); } @@ -367,10 +367,10 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, goto err_free_hdl; /* Initialize ved and sd */ + vsen->pad.flags = MEDIA_PAD_FL_SOURCE; ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, vcfg_name, - MEDIA_ENT_F_CAM_SENSOR, 1, - (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE}, + MEDIA_ENT_F_CAM_SENSOR, 1, &vsen->pad, &vimc_sen_int_ops, &vimc_sen_ops); if (ret) goto err_free_tpg; -- cgit v1.2.3 From b1f8e9316e790bcde517c5312eaaea4f696e0f75 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Wed, 9 Oct 2019 12:53:14 -0300 Subject: media: vimc: move the dev field of each entity to vimc_ent_dev Since the 'struct device *dev' field exists in each of the entity structs, it can be moved to the common struct vimc_ent_devevice. It is then used to replace 'pr_err' with 'dev_err' in the streamer code. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 7 +++---- drivers/media/platform/vimc/vimc-common.h | 2 ++ drivers/media/platform/vimc/vimc-debayer.c | 15 +++++++-------- drivers/media/platform/vimc/vimc-scaler.c | 11 +++++------ drivers/media/platform/vimc/vimc-sensor.c | 5 ++--- drivers/media/platform/vimc/vimc-streamer.c | 4 ++-- 6 files changed, 21 insertions(+), 23 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 936bfb96ebaa..a5d79fb25dff 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -15,7 +15,6 @@ struct vimc_cap_device { struct vimc_ent_device ved; struct video_device vdev; - struct device *dev; struct v4l2_pix_format format; struct vb2_queue queue; struct list_head buf_list; @@ -125,7 +124,7 @@ static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv, if (ret) return ret; - dev_dbg(vcap->dev, "%s: format update: " + dev_dbg(vcap->ved.dev, "%s: format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name, /* old */ @@ -301,7 +300,7 @@ static int vimc_cap_buffer_prepare(struct vb2_buffer *vb) unsigned long size = vcap->format.sizeimage; if (vb2_plane_size(vb, 0) < size) { - dev_err(vcap->dev, "%s: buffer too small (%lu < %lu)\n", + dev_err(vcap->ved.dev, "%s: buffer too small (%lu < %lu)\n", vcap->vdev.name, vb2_plane_size(vb, 0), size); return -EINVAL; } @@ -444,7 +443,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, vcap->ved.ent = &vcap->vdev.entity; vcap->ved.process_frame = vimc_cap_process_frame; vcap->ved.vdev_get_format = vimc_cap_get_format; - vcap->dev = &vimc->pdev.dev; + vcap->ved.dev = &vimc->pdev.dev; /* Initialize the video_device struct */ vdev = &vcap->vdev; diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index ac01182e8b27..348ee3b0fce2 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -78,6 +78,7 @@ struct vimc_pix_map { * struct vimc_ent_device - core struct that represents an entity in the * topology * + * @dev: a pointer of the device struct of the driver * @ent: the pointer to struct media_entity for the node * @process_frame: callback send a frame to that node * @vdev_get_format: callback that returns the current format a pad, used @@ -93,6 +94,7 @@ struct vimc_pix_map { * media_entity */ struct vimc_ent_device { + struct device *dev; struct media_entity *ent; void * (*process_frame)(struct vimc_ent_device *ved, const void *frame); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index a601ca3a0a54..68ae45021830 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -34,7 +34,6 @@ struct vimc_deb_pix_map { struct vimc_deb_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - struct device *dev; /* The active format */ struct v4l2_mbus_framefmt sink_fmt; u32 src_code; @@ -264,7 +263,7 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, /* Set the new format in the sink pad */ vimc_deb_adjust_sink_fmt(&fmt->format); - dev_dbg(vdeb->dev, "%s: sink format update: " + dev_dbg(vdeb->ved.dev, "%s: sink format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdeb->sd.name, /* old */ @@ -387,7 +386,7 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, /* Sum the values of the colors in the mean window */ - dev_dbg(vdeb->dev, + dev_dbg(vdeb->ved.dev, "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n", vdeb->sd.name, lin, col, vdeb->sink_fmt.height, seek); @@ -420,7 +419,7 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, vdeb->sink_fmt.width, vdeb->sink_bpp); - dev_dbg(vdeb->dev, + dev_dbg(vdeb->ved.dev, "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n", vdeb->sd.name, index, wlin, wcol, color); @@ -431,21 +430,21 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, /* Save how many values we already added */ n_rgb[color]++; - dev_dbg(vdeb->dev, "deb: %s: RGB CALC: val %d, n %d\n", + dev_dbg(vdeb->ved.dev, "deb: %s: RGB CALC: val %d, n %d\n", vdeb->sd.name, rgb[color], n_rgb[color]); } } /* Calculate the mean */ for (i = 0; i < 3; i++) { - dev_dbg(vdeb->dev, + dev_dbg(vdeb->ved.dev, "deb: %s: PRE CALC: %dx%d Color %d, val %d, n %d\n", vdeb->sd.name, lin, col, i, rgb[i], n_rgb[i]); if (n_rgb[i]) rgb[i] = rgb[i] / n_rgb[i]; - dev_dbg(vdeb->dev, + dev_dbg(vdeb->ved.dev, "deb: %s: FINAL CALC: %dx%d Color %d, val %d\n", vdeb->sd.name, lin, col, i, rgb[i]); } @@ -521,7 +520,7 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, } vdeb->ved.process_frame = vimc_deb_process_frame; - vdeb->dev = &vimc->pdev.dev; + vdeb->ved.dev = &vimc->pdev.dev; /* Initialize the frame format */ vdeb->sink_fmt = sink_fmt_default; diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index 88a2f6e3218e..2f88a7d9d67b 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -21,7 +21,6 @@ MODULE_PARM_DESC(sca_mult, " the image size multiplier"); struct vimc_sca_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - struct device *dev; /* NOTE: the source fmt is the same as the sink * with the width and hight multiplied by mult */ @@ -172,7 +171,7 @@ static int vimc_sca_set_fmt(struct v4l2_subdev *sd, /* Set the new format in the sink pad */ vimc_sca_adjust_sink_fmt(&fmt->format); - dev_dbg(vsca->dev, "%s: sink format update: " + dev_dbg(vsca->ved.dev, "%s: sink format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsca->sd.name, /* old */ @@ -272,7 +271,7 @@ static void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca, vsca->bpp); pixel = &sink_frame[index]; - dev_dbg(vsca->dev, + dev_dbg(vsca->ved.dev, "sca: %s: --- scale_pix sink pos %dx%d, index %d ---\n", vsca->sd.name, lin, col, index); @@ -282,7 +281,7 @@ static void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca, index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult, vsca->sink_fmt.width * sca_mult, vsca->bpp); - dev_dbg(vsca->dev, "sca: %s: scale_pix src pos %dx%d, index %d\n", + dev_dbg(vsca->ved.dev, "sca: %s: scale_pix src pos %dx%d, index %d\n", vsca->sd.name, lin * sca_mult, col * sca_mult, index); /* Repeat this pixel mult times */ @@ -291,7 +290,7 @@ static void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca, * pixel repetition in a line */ for (j = 0; j < sca_mult * vsca->bpp; j += vsca->bpp) { - dev_dbg(vsca->dev, + dev_dbg(vsca->ved.dev, "sca: %s: sca: scale_pix src pos %d\n", vsca->sd.name, index + j); @@ -380,7 +379,7 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, } vsca->ved.process_frame = vimc_sca_process_frame; - vsca->dev = &vimc->pdev.dev; + vsca->ved.dev = &vimc->pdev.dev; /* Initialize the frame format */ vsca->sink_fmt = sink_fmt_default; diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index b41e24a7d029..25ee89a067f7 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -17,7 +17,6 @@ struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - struct device *dev; struct tpg_data tpg; struct task_struct *kthread_sen; u8 *frame; @@ -159,7 +158,7 @@ static int vimc_sen_set_fmt(struct v4l2_subdev *sd, /* Set the new format */ vimc_sen_adjust_fmt(&fmt->format); - dev_dbg(vsen->dev, "%s: format update: " + dev_dbg(vsen->ved.dev, "%s: format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name, /* old */ @@ -376,7 +375,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, goto err_free_tpg; vsen->ved.process_frame = vimc_sen_process_frame; - vsen->dev = &vimc->pdev.dev; + vsen->ved.dev = &vimc->pdev.dev; /* Initialize the frame format */ vsen->mbus_format = fmt_default; diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index 092833623ac1..1349be188a5b 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -96,8 +96,8 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream, sd = media_entity_to_v4l2_subdev(ved->ent); ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret && ret != -ENOIOCTLCMD) { - pr_err("subdev_call error %s\n", - ved->ent->name); + dev_err(ved->dev, "subdev_call error %s\n", + ved->ent->name); vimc_streamer_pipeline_terminate(stream); return ret; } -- cgit v1.2.3 From 76df2e6c7c782775e41153802489904ce3bd55b8 Mon Sep 17 00:00:00 2001 From: Arthur Moraes do Lago Date: Tue, 1 Oct 2019 21:46:33 -0300 Subject: media: vimc: Implement debayer control for mean window size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add mean window size parameter for debayer filter as a control in vimc-debayer. vimc-debayer was patched to allow changing mean window parameter of the filter without needing to reload the driver. The parameter can now be set using a v4l2-ctl control(mean_window_size). Co-developed-by: Laís Pessine do Carmo Signed-off-by: Laís Pessine do Carmo Signed-off-by: Arthur Moraes do Lago Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.h | 1 + drivers/media/platform/vimc/vimc-debayer.c | 81 +++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 348ee3b0fce2..c75401a36312 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -19,6 +19,7 @@ #define VIMC_CID_VIMC_BASE (0x00f00000 | 0xf000) #define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) #define VIMC_CID_TEST_PATTERN (VIMC_CID_VIMC_BASE + 0) +#define VIMC_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) #define VIMC_FRAME_MAX_WIDTH 4096 #define VIMC_FRAME_MAX_HEIGHT 2160 diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 68ae45021830..5d1b67d684bb 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -9,17 +9,12 @@ #include #include #include +#include +#include #include #include "vimc-common.h" -static unsigned int deb_mean_win_size = 3; -module_param(deb_mean_win_size, uint, 0000); -MODULE_PARM_DESC(deb_mean_win_size, " the window size to calculate the mean.\n" - "NOTE: the window size needs to be an odd number, as the main pixel " - "stays in the center of the window, otherwise the next odd number " - "is considered"); - enum vimc_deb_rgb_colors { VIMC_DEB_RED = 0, VIMC_DEB_GREEN = 1, @@ -43,6 +38,8 @@ struct vimc_deb_device { u8 *src_frame; const struct vimc_deb_pix_map *sink_pix_map; unsigned int sink_bpp; + unsigned int mean_win_size; + struct v4l2_ctrl_handler hdl; struct media_pad pads[2]; }; @@ -344,11 +341,18 @@ static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) return 0; } +static const struct v4l2_subdev_core_ops vimc_deb_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + static const struct v4l2_subdev_video_ops vimc_deb_video_ops = { .s_stream = vimc_deb_s_stream, }; static const struct v4l2_subdev_ops vimc_deb_ops = { + .core = &vimc_deb_core_ops, .pad = &vimc_deb_pad_ops, .video = &vimc_deb_video_ops, }; @@ -382,7 +386,7 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, * the top left corner of the mean window (considering the current * pixel as the center) */ - seek = deb_mean_win_size / 2; + seek = vdeb->mean_win_size / 2; /* Sum the values of the colors in the mean window */ @@ -469,14 +473,33 @@ static void *vimc_deb_process_frame(struct vimc_ent_device *ved, } return vdeb->src_frame; +} + +static int vimc_deb_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vimc_deb_device *vdeb = + container_of(ctrl->handler, struct vimc_deb_device, hdl); + switch (ctrl->id) { + case VIMC_CID_MEAN_WIN_SIZE: + vdeb->mean_win_size = ctrl->val; + break; + default: + return -EINVAL; + } + return 0; } +static const struct v4l2_ctrl_ops vimc_deb_ctrl_ops = { + .s_ctrl = vimc_deb_s_ctrl, +}; + static void vimc_deb_release(struct v4l2_subdev *sd) { struct vimc_deb_device *vdeb = container_of(sd, struct vimc_deb_device, sd); + v4l2_ctrl_handler_free(&vdeb->hdl); media_entity_cleanup(vdeb->ved.ent); kfree(vdeb); } @@ -493,6 +516,24 @@ void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) v4l2_device_unregister_subdev(&vdeb->sd); } +static const struct v4l2_ctrl_config vimc_deb_ctrl_class = { + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, + .id = VIMC_CID_VIMC_CLASS, + .name = "VIMC Controls", + .type = V4L2_CTRL_TYPE_CTRL_CLASS, +}; + +static const struct v4l2_ctrl_config vimc_deb_ctrl_mean_win_size = { + .ops = &vimc_deb_ctrl_ops, + .id = VIMC_CID_MEAN_WIN_SIZE, + .name = "Debayer Mean Window Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 25, + .step = 2, + .def = 3, +}; + struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, const char *vcfg_name) { @@ -505,6 +546,16 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, if (!vdeb) return NULL; + /* Create controls: */ + v4l2_ctrl_handler_init(&vdeb->hdl, 2); + v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_class, NULL); + v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_mean_win_size, NULL); + vdeb->sd.ctrl_handler = &vdeb->hdl; + if (vdeb->hdl.error) { + ret = vdeb->hdl.error; + goto err_free_vdeb; + } + /* Initialize ved and sd */ vdeb->pads[0].flags = MEDIA_PAD_FL_SINK; vdeb->pads[1].flags = MEDIA_PAD_FL_SOURCE; @@ -514,13 +565,12 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, vdeb->pads, &vimc_deb_int_ops, &vimc_deb_ops); - if (ret) { - kfree(vdeb); - return NULL; - } + if (ret) + goto err_free_hdl; vdeb->ved.process_frame = vimc_deb_process_frame; vdeb->ved.dev = &vimc->pdev.dev; + vdeb->mean_win_size = vimc_deb_ctrl_mean_win_size.def; /* Initialize the frame format */ vdeb->sink_fmt = sink_fmt_default; @@ -534,4 +584,11 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, vdeb->set_rgb_src = vimc_deb_set_rgb_mbus_fmt_rgb888_1x24; return &vdeb->ved; + +err_free_hdl: + v4l2_ctrl_handler_free(&vdeb->hdl); +err_free_vdeb: + kfree(vdeb); + + return NULL; } -- cgit v1.2.3 From a4260ea49547aa0c84c353f9de5998a0315d89fe Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 23 Oct 2019 19:13:31 -0300 Subject: media: sun4i: Add H3 deinterlace driver Allwinner H3 SoC contains deinterlace unit, which has several modes of operation - bypass, weave, bob and mixed (advanced) mode. I don't know how mixed mode works, but according to Allwinner it gives best results, so they use it exclusively. Currently this mode is also hardcoded here. For each interleaved frame queued, this driver produces 2 deinterlaced frames. Deinterlaced frames are based on 2 consequtive output buffers, except for the first 2, where same output buffer is given to peripheral as current and previous. There is no documentation for this core, so register layout and fixed values were taken from BSP driver. I'm not sure if maximum size of the image unit is capable to process is governed by size of "flag" buffers, frequency or it really is some HW limitation. Currently driver can process full HD image in ~15ms (7.5ms for each capture buffer), which allows to process 1920x1080@60i video smoothly in real time. Acked-by: Maxime Ripard Signed-off-by: Jernej Skrabec [hverkuil-cisco@xs4all.nl: add static to deinterlace_ioctl_ops] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 13 + drivers/media/platform/sunxi/Makefile | 1 + drivers/media/platform/sunxi/sun8i-di/Makefile | 2 + drivers/media/platform/sunxi/sun8i-di/sun8i-di.c | 1028 ++++++++++++++++++++++ drivers/media/platform/sunxi/sun8i-di/sun8i-di.h | 237 +++++ 5 files changed, 1281 insertions(+) create mode 100644 drivers/media/platform/sunxi/sun8i-di/Makefile create mode 100644 drivers/media/platform/sunxi/sun8i-di/sun8i-di.c create mode 100644 drivers/media/platform/sunxi/sun8i-di/sun8i-di.h (limited to 'drivers/media') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 54ed2787bcc6..e84f35d3a68e 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -494,6 +494,19 @@ config VIDEO_QCOM_VENUS on various Qualcomm SoCs. To compile this driver as a module choose m here. +config VIDEO_SUN8I_DEINTERLACE + tristate "Allwinner Deinterlace driver" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_SUNXI || COMPILE_TEST + depends on COMMON_CLK && OF + depends on PM + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Allwinner deinterlace unit with scaling + capability found on some SoCs, like H3. + To compile this driver as a module choose m here. + endif # V4L_MEM2MEM_DRIVERS # TI VIDEO PORT Helper Modules diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile index a05127529006..3878cb4efdc2 100644 --- a/drivers/media/platform/sunxi/Makefile +++ b/drivers/media/platform/sunxi/Makefile @@ -1,2 +1,3 @@ obj-y += sun4i-csi/ obj-y += sun6i-csi/ +obj-y += sun8i-di/ diff --git a/drivers/media/platform/sunxi/sun8i-di/Makefile b/drivers/media/platform/sunxi/sun8i-di/Makefile new file mode 100644 index 000000000000..109f7e5442b7 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-di/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_SUN8I_DEINTERLACE) += sun8i-di.o diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c new file mode 100644 index 000000000000..aaa1dc159ac2 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -0,0 +1,1028 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner sun8i deinterlacer with scaler driver + * + * Copyright (C) 2019 Jernej Skrabec + * + * Based on vim2m driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sun8i-di.h" + +#define FLAG_SIZE (DEINTERLACE_MAX_WIDTH * DEINTERLACE_MAX_HEIGHT / 4) + +static u32 deinterlace_formats[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, +}; + +static inline u32 deinterlace_read(struct deinterlace_dev *dev, u32 reg) +{ + return readl(dev->base + reg); +} + +static inline void deinterlace_write(struct deinterlace_dev *dev, + u32 reg, u32 value) +{ + writel(value, dev->base + reg); +} + +static inline void deinterlace_set_bits(struct deinterlace_dev *dev, + u32 reg, u32 bits) +{ + writel(readl(dev->base + reg) | bits, dev->base + reg); +} + +static inline void deinterlace_clr_set_bits(struct deinterlace_dev *dev, + u32 reg, u32 clr, u32 set) +{ + u32 val = readl(dev->base + reg); + + val &= ~clr; + val |= set; + + writel(val, dev->base + reg); +} + +static void deinterlace_device_run(void *priv) +{ + struct deinterlace_ctx *ctx = priv; + struct deinterlace_dev *dev = ctx->dev; + u32 size, stride, width, height, val; + struct vb2_v4l2_buffer *src, *dst; + unsigned int hstep, vstep; + dma_addr_t addr; + + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + v4l2_m2m_buf_copy_metadata(src, dst, true); + + deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, + DEINTERLACE_MOD_ENABLE_EN); + + if (ctx->field) { + deinterlace_write(dev, DEINTERLACE_TILE_FLAG0, + ctx->flag1_buf_dma); + deinterlace_write(dev, DEINTERLACE_TILE_FLAG1, + ctx->flag2_buf_dma); + } else { + deinterlace_write(dev, DEINTERLACE_TILE_FLAG0, + ctx->flag2_buf_dma); + deinterlace_write(dev, DEINTERLACE_TILE_FLAG1, + ctx->flag1_buf_dma); + } + deinterlace_write(dev, DEINTERLACE_FLAG_LINE_STRIDE, 0x200); + + width = ctx->src_fmt.width; + height = ctx->src_fmt.height; + stride = ctx->src_fmt.bytesperline; + size = stride * height; + + addr = vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0); + deinterlace_write(dev, DEINTERLACE_BUF_ADDR0, addr); + deinterlace_write(dev, DEINTERLACE_BUF_ADDR1, addr + size); + deinterlace_write(dev, DEINTERLACE_BUF_ADDR2, 0); + + deinterlace_write(dev, DEINTERLACE_LINE_STRIDE0, stride); + deinterlace_write(dev, DEINTERLACE_LINE_STRIDE1, stride); + + deinterlace_write(dev, DEINTERLACE_CH0_IN_SIZE, + DEINTERLACE_SIZE(width, height)); + deinterlace_write(dev, DEINTERLACE_CH1_IN_SIZE, + DEINTERLACE_SIZE(width / 2, height / 2)); + + val = DEINTERLACE_IN_FMT_FMT(DEINTERLACE_IN_FMT_YUV420) | + DEINTERLACE_IN_FMT_MOD(DEINTERLACE_MODE_UV_COMBINED); + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_NV12: + val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_UVUV); + break; + case V4L2_PIX_FMT_NV21: + val |= DEINTERLACE_IN_FMT_PS(DEINTERLACE_PS_VUVU); + break; + } + deinterlace_write(dev, DEINTERLACE_IN_FMT, val); + + if (ctx->prev) + addr = vb2_dma_contig_plane_dma_addr(&ctx->prev->vb2_buf, 0); + + deinterlace_write(dev, DEINTERLACE_PRELUMA, addr); + deinterlace_write(dev, DEINTERLACE_PRECHROMA, addr + size); + + val = DEINTERLACE_OUT_FMT_FMT(DEINTERLACE_OUT_FMT_YUV420SP); + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_NV12: + val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_UVUV); + break; + case V4L2_PIX_FMT_NV21: + val |= DEINTERLACE_OUT_FMT_PS(DEINTERLACE_PS_VUVU); + break; + } + deinterlace_write(dev, DEINTERLACE_OUT_FMT, val); + + width = ctx->dst_fmt.width; + height = ctx->dst_fmt.height; + stride = ctx->dst_fmt.bytesperline; + size = stride * height; + + deinterlace_write(dev, DEINTERLACE_CH0_OUT_SIZE, + DEINTERLACE_SIZE(width, height)); + deinterlace_write(dev, DEINTERLACE_CH1_OUT_SIZE, + DEINTERLACE_SIZE(width / 2, height / 2)); + + deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE0, stride); + deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE1, stride); + + addr = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0); + deinterlace_write(dev, DEINTERLACE_WB_ADDR0, addr); + deinterlace_write(dev, DEINTERLACE_WB_ADDR1, addr + size); + deinterlace_write(dev, DEINTERLACE_WB_ADDR2, 0); + + hstep = (ctx->src_fmt.width << 16) / ctx->dst_fmt.width; + vstep = (ctx->src_fmt.height << 16) / ctx->dst_fmt.height; + deinterlace_write(dev, DEINTERLACE_CH0_HORZ_FACT, hstep); + deinterlace_write(dev, DEINTERLACE_CH0_VERT_FACT, vstep); + deinterlace_write(dev, DEINTERLACE_CH1_HORZ_FACT, hstep); + deinterlace_write(dev, DEINTERLACE_CH1_VERT_FACT, vstep); + + deinterlace_clr_set_bits(dev, DEINTERLACE_FIELD_CTRL, + DEINTERLACE_FIELD_CTRL_FIELD_CNT_MSK, + DEINTERLACE_FIELD_CTRL_FIELD_CNT(ctx->field)); + + deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, + DEINTERLACE_FRM_CTRL_START); + + deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, + DEINTERLACE_FRM_CTRL_REG_READY); + + deinterlace_set_bits(dev, DEINTERLACE_INT_ENABLE, + DEINTERLACE_INT_ENABLE_WB_EN); + + deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, + DEINTERLACE_FRM_CTRL_WB_EN); +} + +static int deinterlace_job_ready(void *priv) +{ + struct deinterlace_ctx *ctx = priv; + + return v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) >= 1 && + v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) >= 2; +} + +static void deinterlace_job_abort(void *priv) +{ + struct deinterlace_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; +} + +static irqreturn_t deinterlace_irq(int irq, void *data) +{ + struct deinterlace_dev *dev = data; + struct vb2_v4l2_buffer *src, *dst; + enum vb2_buffer_state state; + struct deinterlace_ctx *ctx; + unsigned int val; + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + return IRQ_NONE; + } + + val = deinterlace_read(dev, DEINTERLACE_INT_STATUS); + if (!(val & DEINTERLACE_INT_STATUS_WRITEBACK)) + return IRQ_NONE; + + deinterlace_write(dev, DEINTERLACE_INT_ENABLE, 0); + deinterlace_set_bits(dev, DEINTERLACE_INT_STATUS, + DEINTERLACE_INT_STATUS_WRITEBACK); + deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, 0); + deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL, + DEINTERLACE_FRM_CTRL_START, 0); + + val = deinterlace_read(dev, DEINTERLACE_STATUS); + if (val & DEINTERLACE_STATUS_WB_ERROR) + state = VB2_BUF_STATE_ERROR; + else + state = VB2_BUF_STATE_DONE; + + dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(dst, state); + + if (ctx->field != ctx->first_field || ctx->aborting) { + ctx->field = ctx->first_field; + + src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (ctx->prev) + v4l2_m2m_buf_done(ctx->prev, state); + ctx->prev = src; + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + } else { + ctx->field = !ctx->first_field; + deinterlace_device_run(ctx); + } + + return IRQ_HANDLED; +} + +static void deinterlace_init(struct deinterlace_dev *dev) +{ + u32 val; + int i; + + deinterlace_write(dev, DEINTERLACE_BYPASS, + DEINTERLACE_BYPASS_CSC); + deinterlace_write(dev, DEINTERLACE_WB_LINE_STRIDE_CTRL, + DEINTERLACE_WB_LINE_STRIDE_CTRL_EN); + deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, + DEINTERLACE_FRM_CTRL_OUT_CTRL); + deinterlace_write(dev, DEINTERLACE_AGTH_SEL, + DEINTERLACE_AGTH_SEL_LINEBUF); + + val = DEINTERLACE_CTRL_EN | + DEINTERLACE_CTRL_MODE_MIXED | + DEINTERLACE_CTRL_DIAG_INTP_EN | + DEINTERLACE_CTRL_TEMP_DIFF_EN; + deinterlace_write(dev, DEINTERLACE_CTRL, val); + + deinterlace_clr_set_bits(dev, DEINTERLACE_LUMA_TH, + DEINTERLACE_LUMA_TH_MIN_LUMA_MSK, + DEINTERLACE_LUMA_TH_MIN_LUMA(4)); + + deinterlace_clr_set_bits(dev, DEINTERLACE_SPAT_COMP, + DEINTERLACE_SPAT_COMP_TH2_MSK, + DEINTERLACE_SPAT_COMP_TH2(5)); + + deinterlace_clr_set_bits(dev, DEINTERLACE_TEMP_DIFF, + DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH_MSK, + DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH(5)); + + val = DEINTERLACE_DIAG_INTP_TH0(60) | + DEINTERLACE_DIAG_INTP_TH1(0) | + DEINTERLACE_DIAG_INTP_TH3(30); + deinterlace_write(dev, DEINTERLACE_DIAG_INTP, val); + + deinterlace_clr_set_bits(dev, DEINTERLACE_CHROMA_DIFF, + DEINTERLACE_CHROMA_DIFF_TH_MSK, + DEINTERLACE_CHROMA_DIFF_TH(5)); + + /* neutral filter coefficients */ + deinterlace_set_bits(dev, DEINTERLACE_FRM_CTRL, + DEINTERLACE_FRM_CTRL_COEF_ACCESS); + readl_poll_timeout(dev->base + DEINTERLACE_STATUS, val, + val & DEINTERLACE_STATUS_COEF_STATUS, 2, 40); + + for (i = 0; i < 32; i++) { + deinterlace_write(dev, DEINTERLACE_CH0_HORZ_COEF0 + i * 4, + DEINTERLACE_IDENTITY_COEF); + deinterlace_write(dev, DEINTERLACE_CH0_VERT_COEF + i * 4, + DEINTERLACE_IDENTITY_COEF); + deinterlace_write(dev, DEINTERLACE_CH1_HORZ_COEF0 + i * 4, + DEINTERLACE_IDENTITY_COEF); + deinterlace_write(dev, DEINTERLACE_CH1_VERT_COEF + i * 4, + DEINTERLACE_IDENTITY_COEF); + } + + deinterlace_clr_set_bits(dev, DEINTERLACE_FRM_CTRL, + DEINTERLACE_FRM_CTRL_COEF_ACCESS, 0); +} + +static inline struct deinterlace_ctx *deinterlace_file2ctx(struct file *file) +{ + return container_of(file->private_data, struct deinterlace_ctx, fh); +} + +static bool deinterlace_check_format(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(deinterlace_formats); i++) + if (deinterlace_formats[i] == pixelformat) + return true; + + return false; +} + +static void deinterlace_prepare_format(struct v4l2_pix_format *pix_fmt) +{ + unsigned int height = pix_fmt->height; + unsigned int width = pix_fmt->width; + unsigned int bytesperline; + unsigned int sizeimage; + + width = clamp(width, DEINTERLACE_MIN_WIDTH, + DEINTERLACE_MAX_WIDTH); + height = clamp(height, DEINTERLACE_MIN_HEIGHT, + DEINTERLACE_MAX_HEIGHT); + + bytesperline = ALIGN(width, 2); + /* luma */ + sizeimage = bytesperline * height; + /* chroma */ + sizeimage += bytesperline * height / 2; + + pix_fmt->width = width; + pix_fmt->height = height; + pix_fmt->bytesperline = bytesperline; + pix_fmt->sizeimage = sizeimage; +} + +static int deinterlace_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, DEINTERLACE_NAME, sizeof(cap->driver)); + strscpy(cap->card, DEINTERLACE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", DEINTERLACE_NAME); + + return 0; +} + +static int deinterlace_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index < ARRAY_SIZE(deinterlace_formats)) { + f->pixelformat = deinterlace_formats[f->index]; + + return 0; + } + + return -EINVAL; +} + +static int deinterlace_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index != 0) + return -EINVAL; + + if (!deinterlace_check_format(fsize->pixel_format)) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = DEINTERLACE_MIN_WIDTH; + fsize->stepwise.min_height = DEINTERLACE_MIN_HEIGHT; + fsize->stepwise.max_width = DEINTERLACE_MAX_WIDTH; + fsize->stepwise.max_height = DEINTERLACE_MAX_HEIGHT; + fsize->stepwise.step_width = 2; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int deinterlace_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + + f->fmt.pix = ctx->dst_fmt; + + return 0; +} + +static int deinterlace_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + + f->fmt.pix = ctx->src_fmt; + + return 0; +} + +static int deinterlace_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + if (!deinterlace_check_format(f->fmt.pix.pixelformat)) + f->fmt.pix.pixelformat = deinterlace_formats[0]; + + if (f->fmt.pix.field != V4L2_FIELD_NONE) + f->fmt.pix.field = V4L2_FIELD_NONE; + + deinterlace_prepare_format(&f->fmt.pix); + + return 0; +} + +static int deinterlace_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + if (!deinterlace_check_format(f->fmt.pix.pixelformat)) + f->fmt.pix.pixelformat = deinterlace_formats[0]; + + if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB && + f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT && + f->fmt.pix.field != V4L2_FIELD_INTERLACED) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + deinterlace_prepare_format(&f->fmt.pix); + + return 0; +} + +static int deinterlace_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + struct vb2_queue *vq; + int ret; + + ret = deinterlace_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + ctx->dst_fmt = f->fmt.pix; + + return 0; +} + +static int deinterlace_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + struct vb2_queue *vq; + int ret; + + ret = deinterlace_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + ctx->src_fmt = f->fmt.pix; + + /* Propagate colorspace information to capture. */ + ctx->dst_fmt.colorspace = f->fmt.pix.colorspace; + ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func; + ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->dst_fmt.quantization = f->fmt.pix.quantization; + + return 0; +} + +static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { + .vidioc_querycap = deinterlace_querycap, + + .vidioc_enum_framesizes = deinterlace_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = deinterlace_enum_fmt, + .vidioc_g_fmt_vid_cap = deinterlace_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = deinterlace_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = deinterlace_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = deinterlace_enum_fmt, + .vidioc_g_fmt_vid_out = deinterlace_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = deinterlace_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = deinterlace_s_fmt_vid_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, +}; + +static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + + if (*nplanes) { + if (sizes[0] < pix_fmt->sizeimage) + return -EINVAL; + } else { + sizes[0] = pix_fmt->sizeimage; + *nplanes = 1; + } + + return 0; +} + +static int deinterlace_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + + if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage); + + return 0; +} + +static void deinterlace_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void deinterlace_queue_cleanup(struct vb2_queue *vq, u32 state) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf; + + do { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (vbuf) + v4l2_m2m_buf_done(vbuf, state); + } while (vbuf); + + if (V4L2_TYPE_IS_OUTPUT(vq->type) && ctx->prev) + v4l2_m2m_buf_done(ctx->prev, state); +} + +static int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct device *dev = ctx->dev->dev; + int ret; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to enable module\n"); + + goto err_runtime_get; + } + + ctx->first_field = + ctx->src_fmt.field == V4L2_FIELD_INTERLACED_BT; + ctx->field = ctx->first_field; + + ctx->prev = NULL; + ctx->aborting = 0; + + ctx->flag1_buf = dma_alloc_coherent(dev, FLAG_SIZE, + &ctx->flag1_buf_dma, + GFP_KERNEL); + if (!ctx->flag1_buf) { + ret = -ENOMEM; + + goto err_no_mem1; + } + + ctx->flag2_buf = dma_alloc_coherent(dev, FLAG_SIZE, + &ctx->flag2_buf_dma, + GFP_KERNEL); + if (!ctx->flag2_buf) { + ret = -ENOMEM; + + goto err_no_mem2; + } + } + + return 0; + +err_no_mem2: + dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, + ctx->flag1_buf_dma); +err_no_mem1: + pm_runtime_put(dev); +err_runtime_get: + deinterlace_queue_cleanup(vq, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void deinterlace_stop_streaming(struct vb2_queue *vq) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + struct device *dev = ctx->dev->dev; + + dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, + ctx->flag1_buf_dma); + dma_free_coherent(dev, FLAG_SIZE, ctx->flag2_buf, + ctx->flag2_buf_dma); + + pm_runtime_put(dev); + } + + deinterlace_queue_cleanup(vq, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops deinterlace_qops = { + .queue_setup = deinterlace_queue_setup, + .buf_prepare = deinterlace_buf_prepare, + .buf_queue = deinterlace_buf_queue, + .start_streaming = deinterlace_start_streaming, + .stop_streaming = deinterlace_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct deinterlace_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->min_buffers_needed = 1; + src_vq->ops = &deinterlace_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = ctx->dev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->min_buffers_needed = 2; + dst_vq->ops = &deinterlace_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = ctx->dev->dev; + + ret = vb2_queue_init(dst_vq); + if (ret) + return ret; + + return 0; +} + +static int deinterlace_open(struct file *file) +{ + struct deinterlace_dev *dev = video_drvdata(file); + struct deinterlace_ctx *ctx = NULL; + int ret; + + if (mutex_lock_interruptible(&dev->dev_mutex)) + return -ERESTARTSYS; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + mutex_unlock(&dev->dev_mutex); + return -ENOMEM; + } + + /* default output format */ + ctx->src_fmt.pixelformat = deinterlace_formats[0]; + ctx->src_fmt.field = V4L2_FIELD_INTERLACED; + ctx->src_fmt.width = 640; + ctx->src_fmt.height = 480; + deinterlace_prepare_format(&ctx->src_fmt); + + /* default capture format */ + ctx->dst_fmt.pixelformat = deinterlace_formats[0]; + ctx->dst_fmt.field = V4L2_FIELD_NONE; + ctx->dst_fmt.width = 640; + ctx->dst_fmt.height = 480; + deinterlace_prepare_format(&ctx->dst_fmt); + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dev = dev; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + &deinterlace_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_free; + } + + v4l2_fh_add(&ctx->fh); + + mutex_unlock(&dev->dev_mutex); + + return 0; + +err_free: + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int deinterlace_release(struct file *file) +{ + struct deinterlace_dev *dev = video_drvdata(file); + struct deinterlace_ctx *ctx = container_of(file->private_data, + struct deinterlace_ctx, fh); + + mutex_lock(&dev->dev_mutex); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + kfree(ctx); + + mutex_unlock(&dev->dev_mutex); + + return 0; +} + +static const struct v4l2_file_operations deinterlace_fops = { + .owner = THIS_MODULE, + .open = deinterlace_open, + .release = deinterlace_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device deinterlace_video_device = { + .name = DEINTERLACE_NAME, + .vfl_dir = VFL_DIR_M2M, + .fops = &deinterlace_fops, + .ioctl_ops = &deinterlace_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_m2m_ops deinterlace_m2m_ops = { + .device_run = deinterlace_device_run, + .job_ready = deinterlace_job_ready, + .job_abort = deinterlace_job_abort, +}; + +static int deinterlace_probe(struct platform_device *pdev) +{ + struct deinterlace_dev *dev; + struct video_device *vfd; + struct resource *res; + int irq, ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->vfd = deinterlace_video_device; + dev->dev = &pdev->dev; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev->dev, "Failed to get IRQ\n"); + + return irq; + } + + ret = devm_request_irq(dev->dev, irq, deinterlace_irq, + 0, dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "Failed to request IRQ\n"); + + return ret; + } + + ret = of_dma_configure(dev->dev, dev->dev->of_node, true); + if (ret) + return ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->base)) { + dev_err(dev->dev, "Failed to map registers\n"); + + return PTR_ERR(dev->base); + } + + dev->bus_clk = devm_clk_get(dev->dev, "bus"); + if (IS_ERR(dev->bus_clk)) { + dev_err(dev->dev, "Failed to get bus clock\n"); + + return PTR_ERR(dev->bus_clk); + } + + dev->mod_clk = devm_clk_get(dev->dev, "mod"); + if (IS_ERR(dev->mod_clk)) { + dev_err(dev->dev, "Failed to get mod clock\n"); + + return PTR_ERR(dev->mod_clk); + } + + dev->ram_clk = devm_clk_get(dev->dev, "ram"); + if (IS_ERR(dev->ram_clk)) { + dev_err(dev->dev, "Failed to get ram clock\n"); + + return PTR_ERR(dev->ram_clk); + } + + dev->rstc = devm_reset_control_get(dev->dev, NULL); + if (IS_ERR(dev->rstc)) { + dev_err(dev->dev, "Failed to get reset control\n"); + + return PTR_ERR(dev->rstc); + } + + mutex_init(&dev->dev_mutex); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(dev->dev, "Failed to register V4L2 device\n"); + + return ret; + } + + vfd = &dev->vfd; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + snprintf(vfd->name, sizeof(vfd->name), "%s", + deinterlace_video_device.name); + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + + goto err_v4l2; + } + + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + + dev->m2m_dev = v4l2_m2m_init(&deinterlace_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, + "Failed to initialize V4L2 M2M device\n"); + ret = PTR_ERR(dev->m2m_dev); + + goto err_video; + } + + platform_set_drvdata(pdev, dev); + + pm_runtime_enable(dev->dev); + + return 0; + +err_video: + video_unregister_device(&dev->vfd); +err_v4l2: + v4l2_device_unregister(&dev->v4l2_dev); + + return ret; +} + +static int deinterlace_remove(struct platform_device *pdev) +{ + struct deinterlace_dev *dev = platform_get_drvdata(pdev); + + v4l2_m2m_release(dev->m2m_dev); + video_unregister_device(&dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + pm_runtime_force_suspend(&pdev->dev); + + return 0; +} + +static int deinterlace_runtime_resume(struct device *device) +{ + struct deinterlace_dev *dev = dev_get_drvdata(device); + int ret; + + ret = clk_set_rate_exclusive(dev->mod_clk, 300000000); + if (ret) { + dev_err(dev->dev, "Failed to set exclusive mod clock rate\n"); + + return ret; + } + + ret = clk_prepare_enable(dev->bus_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable bus clock\n"); + + goto err_exlusive_rate; + } + + ret = clk_prepare_enable(dev->mod_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable mod clock\n"); + + goto err_bus_clk; + } + + ret = clk_prepare_enable(dev->ram_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable ram clock\n"); + + goto err_mod_clk; + } + + ret = reset_control_deassert(dev->rstc); + if (ret) { + dev_err(dev->dev, "Failed to apply reset\n"); + + goto err_ram_clk; + } + + deinterlace_init(dev); + + return 0; + +err_exlusive_rate: + clk_rate_exclusive_put(dev->mod_clk); +err_ram_clk: + clk_disable_unprepare(dev->ram_clk); +err_mod_clk: + clk_disable_unprepare(dev->mod_clk); +err_bus_clk: + clk_disable_unprepare(dev->bus_clk); + + return ret; +} + +static int deinterlace_runtime_suspend(struct device *device) +{ + struct deinterlace_dev *dev = dev_get_drvdata(device); + + reset_control_assert(dev->rstc); + + clk_disable_unprepare(dev->ram_clk); + clk_disable_unprepare(dev->mod_clk); + clk_disable_unprepare(dev->bus_clk); + clk_rate_exclusive_put(dev->mod_clk); + + return 0; +} + +static const struct of_device_id deinterlace_dt_match[] = { + { .compatible = "allwinner,sun8i-h3-deinterlace" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, deinterlace_dt_match); + +static const struct dev_pm_ops deinterlace_pm_ops = { + .runtime_resume = deinterlace_runtime_resume, + .runtime_suspend = deinterlace_runtime_suspend, +}; + +static struct platform_driver deinterlace_driver = { + .probe = deinterlace_probe, + .remove = deinterlace_remove, + .driver = { + .name = DEINTERLACE_NAME, + .of_match_table = deinterlace_dt_match, + .pm = &deinterlace_pm_ops, + }, +}; +module_platform_driver(deinterlace_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jernej Skrabec "); +MODULE_DESCRIPTION("Allwinner Deinterlace driver"); diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.h b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.h new file mode 100644 index 000000000000..0254251d8687 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Allwinner Deinterlace driver + * + * Copyright (C) 2019 Jernej Skrabec + */ + +#ifndef _SUN8I_DEINTERLACE_H_ +#define _SUN8I_DEINTERLACE_H_ + +#include +#include +#include +#include + +#include + +#define DEINTERLACE_NAME "sun8i-di" + +#define DEINTERLACE_MOD_ENABLE 0x00 +#define DEINTERLACE_MOD_ENABLE_EN BIT(0) + +#define DEINTERLACE_FRM_CTRL 0x04 +#define DEINTERLACE_FRM_CTRL_REG_READY BIT(0) +#define DEINTERLACE_FRM_CTRL_WB_EN BIT(2) +#define DEINTERLACE_FRM_CTRL_OUT_CTRL BIT(11) +#define DEINTERLACE_FRM_CTRL_START BIT(16) +#define DEINTERLACE_FRM_CTRL_COEF_ACCESS BIT(23) + +#define DEINTERLACE_BYPASS 0x08 +#define DEINTERLACE_BYPASS_CSC BIT(1) + +#define DEINTERLACE_AGTH_SEL 0x0c +#define DEINTERLACE_AGTH_SEL_LINEBUF BIT(8) + +#define DEINTERLACE_LINT_CTRL 0x10 +#define DEINTERLACE_TRD_PRELUMA 0x1c +#define DEINTERLACE_BUF_ADDR0 0x20 +#define DEINTERLACE_BUF_ADDR1 0x24 +#define DEINTERLACE_BUF_ADDR2 0x28 + +#define DEINTERLACE_FIELD_CTRL 0x2c +#define DEINTERLACE_FIELD_CTRL_FIELD_CNT(v) ((v) & 0xff) +#define DEINTERLACE_FIELD_CTRL_FIELD_CNT_MSK (0xff) + +#define DEINTERLACE_TB_OFFSET0 0x30 +#define DEINTERLACE_TB_OFFSET1 0x34 +#define DEINTERLACE_TB_OFFSET2 0x38 +#define DEINTERLACE_TRD_PRECHROMA 0x3c +#define DEINTERLACE_LINE_STRIDE0 0x40 +#define DEINTERLACE_LINE_STRIDE1 0x44 +#define DEINTERLACE_LINE_STRIDE2 0x48 + +#define DEINTERLACE_IN_FMT 0x4c +#define DEINTERLACE_IN_FMT_PS(v) ((v) & 3) +#define DEINTERLACE_IN_FMT_FMT(v) (((v) & 7) << 4) +#define DEINTERLACE_IN_FMT_MOD(v) (((v) & 7) << 8) + +#define DEINTERLACE_WB_ADDR0 0x50 +#define DEINTERLACE_WB_ADDR1 0x54 +#define DEINTERLACE_WB_ADDR2 0x58 + +#define DEINTERLACE_OUT_FMT 0x5c +#define DEINTERLACE_OUT_FMT_FMT(v) ((v) & 0xf) +#define DEINTERLACE_OUT_FMT_PS(v) (((v) & 3) << 5) + +#define DEINTERLACE_INT_ENABLE 0x60 +#define DEINTERLACE_INT_ENABLE_WB_EN BIT(7) + +#define DEINTERLACE_INT_STATUS 0x64 +#define DEINTERLACE_INT_STATUS_WRITEBACK BIT(7) + +#define DEINTERLACE_STATUS 0x68 +#define DEINTERLACE_STATUS_COEF_STATUS BIT(11) +#define DEINTERLACE_STATUS_WB_ERROR BIT(12) + +#define DEINTERLACE_CSC_COEF 0x70 /* 12 registers */ + +#define DEINTERLACE_CTRL 0xa0 +#define DEINTERLACE_CTRL_EN BIT(0) +#define DEINTERLACE_CTRL_FLAG_OUT_EN BIT(8) +#define DEINTERLACE_CTRL_MODE_PASSTROUGH (0 << 16) +#define DEINTERLACE_CTRL_MODE_WEAVE (1 << 16) +#define DEINTERLACE_CTRL_MODE_BOB (2 << 16) +#define DEINTERLACE_CTRL_MODE_MIXED (3 << 16) +#define DEINTERLACE_CTRL_DIAG_INTP_EN BIT(24) +#define DEINTERLACE_CTRL_TEMP_DIFF_EN BIT(25) + +#define DEINTERLACE_DIAG_INTP 0xa4 +#define DEINTERLACE_DIAG_INTP_TH0(v) ((v) & 0x7f) +#define DEINTERLACE_DIAG_INTP_TH0_MSK (0x7f) +#define DEINTERLACE_DIAG_INTP_TH1(v) (((v) & 0x7f) << 8) +#define DEINTERLACE_DIAG_INTP_TH1_MSK (0x7f << 8) +#define DEINTERLACE_DIAG_INTP_TH3(v) (((v) & 0xff) << 24) +#define DEINTERLACE_DIAG_INTP_TH3_MSK (0xff << 24) + +#define DEINTERLACE_TEMP_DIFF 0xa8 +#define DEINTERLACE_TEMP_DIFF_SAD_CENTRAL_TH(v) ((v) & 0x7f) +#define DEINTERLACE_TEMP_DIFF_SAD_CENTRAL_TH_MSK (0x7f) +#define DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH(v) (((v) & 0x7f) << 8) +#define DEINTERLACE_TEMP_DIFF_AMBIGUITY_TH_MSK (0x7f << 8) +#define DEINTERLACE_TEMP_DIFF_DIRECT_DITHER_TH(v) (((v) & 0x7ff) << 16) +#define DEINTERLACE_TEMP_DIFF_DIRECT_DITHER_TH_MSK (0x7ff << 16) + +#define DEINTERLACE_LUMA_TH 0xac +#define DEINTERLACE_LUMA_TH_MIN_LUMA(v) ((v) & 0xff) +#define DEINTERLACE_LUMA_TH_MIN_LUMA_MSK (0xff) +#define DEINTERLACE_LUMA_TH_MAX_LUMA(v) (((v) & 0xff) << 8) +#define DEINTERLACE_LUMA_TH_MAX_LUMA_MSK (0xff << 8) +#define DEINTERLACE_LUMA_TH_AVG_LUMA_SHIFT(v) (((v) & 0xff) << 16) +#define DEINTERLACE_LUMA_TH_AVG_LUMA_SHIFT_MSK (0xff << 16) +#define DEINTERLACE_LUMA_TH_PIXEL_STATIC(v) (((v) & 3) << 24) +#define DEINTERLACE_LUMA_TH_PIXEL_STATIC_MSK (3 << 24) + +#define DEINTERLACE_SPAT_COMP 0xb0 +#define DEINTERLACE_SPAT_COMP_TH2(v) ((v) & 0xff) +#define DEINTERLACE_SPAT_COMP_TH2_MSK (0xff) +#define DEINTERLACE_SPAT_COMP_TH3(v) (((v) & 0xff) << 16) +#define DEINTERLACE_SPAT_COMP_TH3_MSK (0xff << 16) + +#define DEINTERLACE_CHROMA_DIFF 0xb4 +#define DEINTERLACE_CHROMA_DIFF_TH(v) ((v) & 0xff) +#define DEINTERLACE_CHROMA_DIFF_TH_MSK (0xff) +#define DEINTERLACE_CHROMA_DIFF_LUMA(v) (((v) & 0x3f) << 16) +#define DEINTERLACE_CHROMA_DIFF_LUMA_MSK (0x3f << 16) +#define DEINTERLACE_CHROMA_DIFF_CHROMA(v) (((v) & 0x3f) << 24) +#define DEINTERLACE_CHROMA_DIFF_CHROMA_MSK (0x3f << 24) + +#define DEINTERLACE_PRELUMA 0xb8 +#define DEINTERLACE_PRECHROMA 0xbc +#define DEINTERLACE_TILE_FLAG0 0xc0 +#define DEINTERLACE_TILE_FLAG1 0xc4 +#define DEINTERLACE_FLAG_LINE_STRIDE 0xc8 +#define DEINTERLACE_FLAG_SEQ 0xcc + +#define DEINTERLACE_WB_LINE_STRIDE_CTRL 0xd0 +#define DEINTERLACE_WB_LINE_STRIDE_CTRL_EN BIT(0) + +#define DEINTERLACE_WB_LINE_STRIDE0 0xd4 +#define DEINTERLACE_WB_LINE_STRIDE1 0xd8 +#define DEINTERLACE_WB_LINE_STRIDE2 0xdc +#define DEINTERLACE_TRD_CTRL 0xe0 +#define DEINTERLACE_TRD_BUF_ADDR0 0xe4 +#define DEINTERLACE_TRD_BUF_ADDR1 0xe8 +#define DEINTERLACE_TRD_BUF_ADDR2 0xec +#define DEINTERLACE_TRD_TB_OFF0 0xf0 +#define DEINTERLACE_TRD_TB_OFF1 0xf4 +#define DEINTERLACE_TRD_TB_OFF2 0xf8 +#define DEINTERLACE_TRD_WB_STRIDE 0xfc +#define DEINTERLACE_CH0_IN_SIZE 0x100 +#define DEINTERLACE_CH0_OUT_SIZE 0x104 +#define DEINTERLACE_CH0_HORZ_FACT 0x108 +#define DEINTERLACE_CH0_VERT_FACT 0x10c +#define DEINTERLACE_CH0_HORZ_PHASE 0x110 +#define DEINTERLACE_CH0_VERT_PHASE0 0x114 +#define DEINTERLACE_CH0_VERT_PHASE1 0x118 +#define DEINTERLACE_CH0_HORZ_TAP0 0x120 +#define DEINTERLACE_CH0_HORZ_TAP1 0x124 +#define DEINTERLACE_CH0_VERT_TAP 0x128 +#define DEINTERLACE_CH1_IN_SIZE 0x200 +#define DEINTERLACE_CH1_OUT_SIZE 0x204 +#define DEINTERLACE_CH1_HORZ_FACT 0x208 +#define DEINTERLACE_CH1_VERT_FACT 0x20c +#define DEINTERLACE_CH1_HORZ_PHASE 0x210 +#define DEINTERLACE_CH1_VERT_PHASE0 0x214 +#define DEINTERLACE_CH1_VERT_PHASE1 0x218 +#define DEINTERLACE_CH1_HORZ_TAP0 0x220 +#define DEINTERLACE_CH1_HORZ_TAP1 0x224 +#define DEINTERLACE_CH1_VERT_TAP 0x228 +#define DEINTERLACE_CH0_HORZ_COEF0 0x400 /* 32 registers */ +#define DEINTERLACE_CH0_HORZ_COEF1 0x480 /* 32 registers */ +#define DEINTERLACE_CH0_VERT_COEF 0x500 /* 32 registers */ +#define DEINTERLACE_CH1_HORZ_COEF0 0x600 /* 32 registers */ +#define DEINTERLACE_CH1_HORZ_COEF1 0x680 /* 32 registers */ +#define DEINTERLACE_CH1_VERT_COEF 0x700 /* 32 registers */ +#define DEINTERLACE_CH3_HORZ_COEF0 0x800 /* 32 registers */ +#define DEINTERLACE_CH3_HORZ_COEF1 0x880 /* 32 registers */ +#define DEINTERLACE_CH3_VERT_COEF 0x900 /* 32 registers */ + +#define DEINTERLACE_MIN_WIDTH 2U +#define DEINTERLACE_MIN_HEIGHT 2U +#define DEINTERLACE_MAX_WIDTH 2048U +#define DEINTERLACE_MAX_HEIGHT 1100U + +#define DEINTERLACE_MODE_UV_COMBINED 2 + +#define DEINTERLACE_IN_FMT_YUV420 2 + +#define DEINTERLACE_OUT_FMT_YUV420SP 13 + +#define DEINTERLACE_PS_UVUV 0 +#define DEINTERLACE_PS_VUVU 1 + +#define DEINTERLACE_IDENTITY_COEF 0x4000 + +#define DEINTERLACE_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) + +struct deinterlace_ctx { + struct v4l2_fh fh; + struct deinterlace_dev *dev; + + struct v4l2_pix_format src_fmt; + struct v4l2_pix_format dst_fmt; + + void *flag1_buf; + dma_addr_t flag1_buf_dma; + + void *flag2_buf; + dma_addr_t flag2_buf_dma; + + struct vb2_v4l2_buffer *prev; + + unsigned int first_field; + unsigned int field; + + int aborting; +}; + +struct deinterlace_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd; + struct device *dev; + struct v4l2_m2m_dev *m2m_dev; + + /* Device file mutex */ + struct mutex dev_mutex; + + void __iomem *base; + + struct clk *bus_clk; + struct clk *mod_clk; + struct clk *ram_clk; + + struct reset_control *rstc; +}; + +#endif -- cgit v1.2.3 From 17f74b145af3db5823c1a5d6ac9e34f983b789d4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 29 Oct 2019 15:24:01 -0300 Subject: media: smiapp: unlock on error in smiapp_start_streaming() We added two new error paths to smiapp_start_streaming(), but we can't return directly without dropping the "sensor->mutex" lock. Fixes: f8c4352c1bef ("media: smiapp: Move binning configuration to streaming start") Signed-off-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 77dfce7c3be9..84f9771b5fed 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1362,13 +1362,13 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) rval = smiapp_write( sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); if (rval < 0) - return rval; + goto out; binning_mode = 1; } rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); if (rval < 0) - return rval; + goto out; /* Set up PLL */ rval = smiapp_pll_configure(sensor); -- cgit v1.2.3 From 1438d3c1c35faf16c86dfa6c2a55030777cd4084 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 3 Sep 2019 14:06:39 -0300 Subject: media: cx231xx: remove redundant assignment to variable status Variable status is being initialized with a value that is never read and is being re-assigned a later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-avcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index d417b5fe4093..0974965e848f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -1240,7 +1240,7 @@ int cx231xx_init_ctrl_pin_status(struct cx231xx *dev) int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, u8 analog_or_digital) { - int status = 0; + int status; /* first set the direction to output */ status = cx231xx_set_gpio_direction(dev, -- cgit v1.2.3 From 5c2a99480eb8d0fd8775847e31651740b56ba64b Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Sat, 7 Sep 2019 11:21:36 -0300 Subject: media: xilinx: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files related to Video drivers for Xilinx devices. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/xilinx-dma.h | 2 +- drivers/media/platform/xilinx/xilinx-vip.h | 2 +- drivers/media/platform/xilinx/xilinx-vipp.h | 2 +- drivers/media/platform/xilinx/xilinx-vtc.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h index 5aec4d17eb21..2378bdae57ae 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.h +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Xilinx Video DMA * diff --git a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h index f71e2b650453..a528a32ea1dc 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.h +++ b/drivers/media/platform/xilinx/xilinx-vip.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Xilinx Video IP Core * diff --git a/drivers/media/platform/xilinx/xilinx-vipp.h b/drivers/media/platform/xilinx/xilinx-vipp.h index e65fce9538f9..cc52c1854dbd 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.h +++ b/drivers/media/platform/xilinx/xilinx-vipp.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Xilinx Video IP Composite Device * diff --git a/drivers/media/platform/xilinx/xilinx-vtc.h b/drivers/media/platform/xilinx/xilinx-vtc.h index 90cf44245283..855845911ffc 100644 --- a/drivers/media/platform/xilinx/xilinx-vtc.h +++ b/drivers/media/platform/xilinx/xilinx-vtc.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Xilinx Video Timing Controller * -- cgit v1.2.3 From 9ecb6718c679a25880acf598b520d6217b2ef30c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 6 Oct 2019 12:04:29 -0300 Subject: media: vpx3220: make array input_vals static, makes object smaller Don't populate the array input_vals on the stack but instead make it static. Makes the object code smaller by 106 bytes. Before: text data bss dec hex filename 11744 3536 128 15408 3c30 drivers/media/i2c/vpx3220.o After: text data bss dec hex filename 11574 3600 128 15302 3bc6 drivers/media/i2c/vpx3220.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/vpx3220.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index 39f66e7a0e42..8be03fe5928c 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -375,7 +375,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd, input = 1: COMPOSITE input input = 2: SVHS input */ - const int input_vals[3][2] = { + static const int input_vals[3][2] = { {0x0c, 0}, {0x0d, 0}, {0x0e, 1} -- cgit v1.2.3 From d973933858eea01dcaa150da581b11933802e415 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Wed, 9 Oct 2019 11:55:25 -0300 Subject: media: v4l2-dv-timings: Use DIV_ROUND_CLOSEST directly to make it readable The kernel.h macro DIV_ROUND_CLOSEST performs the computation (x + d/2)/d but is perhaps more readable. Signed-off-by: zhong jiang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 0607a5d0d051..230d65a64217 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -757,7 +757,7 @@ bool v4l2_detect_gtf(unsigned frame_height, pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; hsync = (frame_width * 8 + 50) / 100; - hsync = ((hsync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN) * GTF_CELL_GRAN; + hsync = DIV_ROUND_CLOSEST(hsync, GTF_CELL_GRAN) * GTF_CELL_GRAN; h_fp = h_blank / 2 - hsync; -- cgit v1.2.3 From 11609a7e21f8cea42630350aa57662928fa4dc63 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 10 Oct 2019 10:13:31 -0300 Subject: media: bdisp: fix memleak on release If a process is interrupted while accessing the video device and the device lock is contended, release() could return early and fail to free related resources. Note that the return value of the v4l2 release file operation is ignored. Fixes: 28ffeebbb7bd ("[media] bdisp: 2D blitter driver using v4l2 mem2mem framework") Cc: stable # 4.2 Signed-off-by: Johan Hovold Reviewed-by: Fabien Dessenne Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/bdisp/bdisp-v4l2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index e90f1ba30574..675b5f2b4c2e 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -651,8 +651,7 @@ static int bdisp_release(struct file *file) dev_dbg(bdisp->dev, "%s\n", __func__); - if (mutex_lock_interruptible(&bdisp->lock)) - return -ERESTARTSYS; + mutex_lock(&bdisp->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); -- cgit v1.2.3 From 1091eb830627625dcf79958d99353c2391f41708 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 10 Oct 2019 10:13:32 -0300 Subject: media: radio: wl1273: fix interrupt masking on release If a process is interrupted while accessing the radio device and the core lock is contended, release() could return early and fail to update the interrupt mask. Note that the return value of the v4l2 release file operation is ignored. Fixes: 87d1a50ce451 ("[media] V4L2: WL1273 FM Radio: TI WL1273 FM radio driver") Cc: stable # 2.6.38 Cc: Matti Aaltonen Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-wl1273.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 104ac41c6f96..112376873167 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1148,8 +1148,7 @@ static int wl1273_fm_fops_release(struct file *file) if (radio->rds_users > 0) { radio->rds_users--; if (radio->rds_users == 0) { - if (mutex_lock_interruptible(&core->lock)) - return -EINTR; + mutex_lock(&core->lock); radio->irq_flags &= ~WL1273_RDS_EVENT; -- cgit v1.2.3 From 8f490061747218ae3dd595637bfb6f2905e79fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Sun, 13 Oct 2019 21:07:50 -0300 Subject: media: rcar-vin: Do not enumerate unsupported pixel formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a pixel format is not supported by the hardware NULL is returned by rvin_format_from_pixel() for that fourcc. Verify that the pixel format is supported using this or skip it when enumerating. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 9a9b89c0dc0b..13b7cd5d2e40 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -296,12 +296,22 @@ static int rvin_g_fmt_vid_cap(struct file *file, void *priv, static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index >= ARRAY_SIZE(rvin_formats)) - return -EINVAL; - - f->pixelformat = rvin_formats[f->index].fourcc; + struct rvin_dev *vin = video_drvdata(file); + unsigned int i; + int matched; + + matched = -1; + for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) { + if (rvin_format_from_pixel(vin, rvin_formats[i].fourcc)) + matched++; + + if (matched == f->index) { + f->pixelformat = rvin_formats[i].fourcc; + return 0; + } + } - return 0; + return -EINVAL; } static int rvin_g_selection(struct file *file, void *fh, -- cgit v1.2.3 From f8fe466aa727951f226a99e58508fd908b715d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Sun, 13 Oct 2019 21:16:14 -0300 Subject: media: rcar-vin: Define which hardware supports NV12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most but not all Gen3 hardware support outputting NV12, add a flag to indicate which SoCs do support it. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 6 ++++++ drivers/media/platform/rcar-vin/rcar-vin.h | 2 ++ 2 files changed, 8 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 334c62805959..dcb539711151 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -983,6 +983,7 @@ static const struct rvin_group_route rcar_info_r8a7795_routes[] = { static const struct rvin_info rcar_info_r8a7795 = { .model = RCAR_GEN3, .use_mc = true, + .nv12 = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a7795_routes, @@ -1077,6 +1078,7 @@ static const struct rvin_group_route rcar_info_r8a7796_routes[] = { static const struct rvin_info rcar_info_r8a7796 = { .model = RCAR_GEN3, .use_mc = true, + .nv12 = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a7796_routes, @@ -1121,6 +1123,7 @@ static const struct rvin_group_route rcar_info_r8a77965_routes[] = { static const struct rvin_info rcar_info_r8a77965 = { .model = RCAR_GEN3, .use_mc = true, + .nv12 = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a77965_routes, @@ -1168,6 +1171,7 @@ static const struct rvin_group_route rcar_info_r8a77980_routes[] = { static const struct rvin_info rcar_info_r8a77980 = { .model = RCAR_GEN3, .use_mc = true, + .nv12 = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a77980_routes, @@ -1184,6 +1188,7 @@ static const struct rvin_group_route rcar_info_r8a77990_routes[] = { static const struct rvin_info rcar_info_r8a77990 = { .model = RCAR_GEN3, .use_mc = true, + .nv12 = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a77990_routes, @@ -1196,6 +1201,7 @@ static const struct rvin_group_route rcar_info_r8a77995_routes[] = { static const struct rvin_info rcar_info_r8a77995 = { .model = RCAR_GEN3, .use_mc = true, + .nv12 = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a77995_routes, diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 86e9bad44484..a36b0824f81d 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -126,6 +126,7 @@ struct rvin_group_route { * struct rvin_info - Information about the particular VIN implementation * @model: VIN model * @use_mc: use media controller instead of controlling subdevice + * @nv12: support outputing NV12 pixel format * @max_width: max input width the VIN supports * @max_height: max input height the VIN supports * @routes: list of possible routes from the CSI-2 recivers to @@ -134,6 +135,7 @@ struct rvin_group_route { struct rvin_info { enum model_id model; bool use_mc; + bool nv12; unsigned int max_width; unsigned int max_height; -- cgit v1.2.3 From 9b744a3ec812fd3ef0364f3cb397ed89302c7957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Sun, 13 Oct 2019 21:16:15 -0300 Subject: media: rcar-vin: Add support for outputting NV12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most Gen3 boards can output frames in NV12 format, add support for this with a runtime check that the running hardware supports it. Signed-off-by: Niklas Söderlund Reviewed-by: Simon Horman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 5 +++- drivers/media/platform/rcar-vin/rcar-v4l2.c | 39 ++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index af4f774149f0..cf9029efeb04 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -118,6 +118,7 @@ #define VNDMR_ABIT (1 << 2) #define VNDMR_DTMD_YCSEP (1 << 1) #define VNDMR_DTMD_ARGB (1 << 0) +#define VNDMR_DTMD_YCSEP_420 (3 << 0) /* Video n Data Mode Register 2 bits */ #define VNDMR2_VPS (1 << 30) @@ -701,11 +702,13 @@ static int rvin_setup(struct rvin_dev *vin) * Output format */ switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: rvin_write(vin, ALIGN(vin->format.bytesperline * vin->format.height, 0x80), VNUVAOF_REG); - dmr = VNDMR_DTMD_YCSEP; + dmr = vin->format.pixelformat == V4L2_PIX_FMT_NV12 ? + VNDMR_DTMD_YCSEP_420 : VNDMR_DTMD_YCSEP; output_is_yuv = true; break; case V4L2_PIX_FMT_YUYV: diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 13b7cd5d2e40..9e2e63ffcc47 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -30,6 +30,10 @@ */ static const struct rvin_video_format rvin_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .bpp = 1, + }, { .fourcc = V4L2_PIX_FMT_NV16, .bpp = 1, @@ -72,6 +76,9 @@ const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, if (vin->info->model == RCAR_M1 && pixelformat == V4L2_PIX_FMT_XBGR32) return NULL; + if (pixelformat == V4L2_PIX_FMT_NV12 && !vin->info->nv12) + return NULL; + for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) if (rvin_formats[i].fourcc == pixelformat) return rvin_formats + i; @@ -90,17 +97,29 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin, if (WARN_ON(!fmt)) return -EINVAL; - align = pix->pixelformat == V4L2_PIX_FMT_NV16 ? 0x20 : 0x10; + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + align = 0x20; + break; + default: + align = 0x10; + break; + } return ALIGN(pix->width, align) * fmt->bpp; } static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) { - if (pix->pixelformat == V4L2_PIX_FMT_NV16) + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV12: + return pix->bytesperline * pix->height * 3 / 2; + case V4L2_PIX_FMT_NV16: return pix->bytesperline * pix->height * 2; - - return pix->bytesperline * pix->height; + default: + return pix->bytesperline * pix->height; + } } static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) @@ -124,8 +143,16 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) break; } - /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ - walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + /* HW limit width to a multiple of 32 (2^5) for NV12/16 else 2 (2^1) */ + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + walign = 5; + break; + default: + walign = 1; + break; + } /* Limit to VIN capabilities */ v4l_bound_align_image(&pix->width, 2, vin->info->max_width, walign, -- cgit v1.2.3 From 3f9402a09f212c321f65eda926555e8448b9c365 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 15 Oct 2019 07:57:57 -0300 Subject: media: rcar-vin: Enable support for R8A774B1 Add the SoC specific information for RZ/G2N(R8A774B1) SoC. The VIN module of RZ/G2N is similar to R-Car M3-N. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index dcb539711151..7440c8965d27 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1212,6 +1212,10 @@ static const struct of_device_id rvin_of_id_table[] = { .compatible = "renesas,vin-r8a774a1", .data = &rcar_info_r8a7796, }, + { + .compatible = "renesas,vin-r8a774b1", + .data = &rcar_info_r8a77965, + }, { .compatible = "renesas,vin-r8a774c0", .data = &rcar_info_r8a77990, -- cgit v1.2.3 From 5ebc4b2eb4338e3c936bf095d688141bf916daf2 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 15 Oct 2019 07:57:58 -0300 Subject: media: rcar-csi2: Enable support for R8A774B1 Add the MIPI CSI-2 driver support for RZ/G2N(R8A774B1) SoC. The CSI-2 module of RZ/G2N is similar to R-Car M3-N. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index c14af1b929df..faa9fb23a2e9 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -1081,6 +1081,10 @@ static const struct of_device_id rcar_csi2_of_table[] = { .compatible = "renesas,r8a774a1-csi2", .data = &rcar_csi2_info_r8a7796, }, + { + .compatible = "renesas,r8a774b1-csi2", + .data = &rcar_csi2_info_r8a77965, + }, { .compatible = "renesas,r8a774c0-csi2", .data = &rcar_csi2_info_r8a77990, -- cgit v1.2.3 From d39083234c60519724c6ed59509a2129fd2aed41 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Fri, 18 Oct 2019 01:47:00 -0300 Subject: media: rcar_drif: fix a memory disclosure "f->fmt.sdr.reserved" is uninitialized. As other peer drivers like msi2500 and airspy do, the fix initializes it to avoid memory disclosures. Signed-off-by: Kangjie Lu Reviewed-by: Geert Uytterhoeven Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_drif.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index 608e5217ccd5..0f267a237b42 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -912,6 +912,7 @@ static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv, { struct rcar_drif_sdr *sdr = video_drvdata(file); + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); f->fmt.sdr.pixelformat = sdr->fmt->pixelformat; f->fmt.sdr.buffersize = sdr->fmt->buffersize; -- cgit v1.2.3 From 704c6c80fb471d1bb0ef0d61a94617d1d55743cd Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Fri, 18 Oct 2019 07:20:52 -0300 Subject: media: exynos4-is: Fix recursive locking in isp_video_release() >From isp_video_release(), &isp->video_lock is held and subsequent vb2_fop_release() tries to lock vdev->lock which is same with the previous one. Replace vb2_fop_release() with _vb2_fop_release() to fix the recursive locking. Fixes: 1380f5754cb0 ("[media] videobuf2: Add missing lock held on vb2_fop_release") Signed-off-by: Seung-Woo Kim Reviewed-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-isp-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 378cc302e1f8..d2cbcdca0463 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -313,7 +313,7 @@ static int isp_video_release(struct file *file) ivc->streaming = 0; } - vb2_fop_release(file); + _vb2_fop_release(file, NULL); if (v4l2_fh_is_singular_file(file)) { fimc_pipeline_call(&ivc->ve, close); -- cgit v1.2.3 From 3cbd3d99fd85096da2175b9ae0111893b91b7ad0 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Fri, 18 Oct 2019 12:31:40 -0300 Subject: media: v4l2-subdev: Don't use __u32 internally Commit a8fa55078a77 ("media: v4l2-subdev: Verify arguments in v4l2_subdev_call()") and commit 374d62e7aa50 ("media: v4l2-subdev: Verify v4l2_subdev_call() pad config argument") introduced a few local functions, unfortunately with arguments of type __u32, reserved for use in Linux uAPI. Use u32 instead. Suggested-by: Sakari Ailus Signed-off-by: Janusz Krzysztofik Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index f725cd9b66b9..9e987c0f840e 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -112,7 +112,7 @@ static int subdev_close(struct file *file) return 0; } -static inline int check_which(__u32 which) +static inline int check_which(u32 which) { if (which != V4L2_SUBDEV_FORMAT_TRY && which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -121,7 +121,7 @@ static inline int check_which(__u32 which) return 0; } -static inline int check_pad(struct v4l2_subdev *sd, __u32 pad) +static inline int check_pad(struct v4l2_subdev *sd, u32 pad) { #if defined(CONFIG_MEDIA_CONTROLLER) if (sd->entity.num_pads) { @@ -136,7 +136,7 @@ static inline int check_pad(struct v4l2_subdev *sd, __u32 pad) return 0; } -static int check_cfg(__u32 which, struct v4l2_subdev_pad_config *cfg) +static int check_cfg(u32 which, struct v4l2_subdev_pad_config *cfg) { if (which == V4L2_SUBDEV_FORMAT_TRY && !cfg) return -EINVAL; -- cgit v1.2.3 From 545b618cfb5cadacd00c25066b9a36540e5ca9e9 Mon Sep 17 00:00:00 2001 From: Vandana BN Date: Tue, 22 Oct 2019 04:51:40 -0300 Subject: media: v4l2-core: fix touch support in v4l_g_fmt v4l_s_fmt, for VFL_TYPE_TOUCH, sets unneeded members of the v4l2_pix_format structure to default values.This was missing in v4l_g_fmt, which would lead to failures in v4l2-compliance tests. Signed-off-by: Vandana BN Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index d26c83d4c255..315ac12c3e0a 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1478,10 +1478,26 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, return ret; } +static void v4l_pix_format_touch(struct v4l2_pix_format *p) +{ + /* + * The v4l2_pix_format structure contains fields that make no sense for + * touch. Set them to default values in this case. + */ + + p->field = V4L2_FIELD_NONE; + p->colorspace = V4L2_COLORSPACE_RAW; + p->flags = 0; + p->ycbcr_enc = 0; + p->quantization = 0; + p->xfer_func = 0; +} + static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_format *p = arg; + struct video_device *vfd = video_devdata(file); int ret = check_fmt(file, p->type); if (ret) @@ -1519,6 +1535,8 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + if (vfd->vfl_type == VFL_TYPE_TOUCH) + v4l_pix_format_touch(&p->fmt.pix); return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg); @@ -1556,21 +1574,6 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, return -EINVAL; } -static void v4l_pix_format_touch(struct v4l2_pix_format *p) -{ - /* - * The v4l2_pix_format structure contains fields that make no sense for - * touch. Set them to default values in this case. - */ - - p->field = V4L2_FIELD_NONE; - p->colorspace = V4L2_COLORSPACE_RAW; - p->flags = 0; - p->ycbcr_enc = 0; - p->quantization = 0; - p->xfer_func = 0; -} - static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { -- cgit v1.2.3 From a3fd80198de6ab98a205cf7fb148d88e9e1c44bb Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 24 Oct 2019 07:32:11 -0300 Subject: media: coda: fix deadlock between decoder picture run and start command The BIT decoder picture run temporarily locks the bitstream mutex while the coda device mutex is locked, to refill the bitstream ring buffer. Consequently, the decoder start command, which locks both mutexes when flushing the bitstream ring buffer, must lock the coda device mutex first as well, to avoid an ABBA deadlock. Fixes: e7fd95849b3c ("media: coda: flush bitstream ring buffer on decoder restart") Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 73222c0615c0..834f11fe9dc2 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1084,16 +1084,16 @@ static int coda_decoder_cmd(struct file *file, void *fh, switch (dc->cmd) { case V4L2_DEC_CMD_START: - mutex_lock(&ctx->bitstream_mutex); mutex_lock(&dev->coda_mutex); + mutex_lock(&ctx->bitstream_mutex); coda_bitstream_flush(ctx); - mutex_unlock(&dev->coda_mutex); dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); vb2_clear_last_buffer_dequeued(dst_vq); ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; coda_fill_bitstream(ctx, NULL); mutex_unlock(&ctx->bitstream_mutex); + mutex_unlock(&dev->coda_mutex); break; case V4L2_DEC_CMD_STOP: stream_end = false; -- cgit v1.2.3 From 3b299d9abf159c5e40864f8ece97d914b6a9f4b1 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Thu, 24 Oct 2019 07:32:49 -0300 Subject: media: coda: request to skip kernel mapping for decoded buffers The kernel driver never touches the decoded buffers with the CPU. All accesses are either done by hardware DMA masters or userspace mapping the buffers. This means we don't need a kernel virtual address mapping for those buffers at all. As those buffers are usually quite large, we can save a good deal of kernel vmalloc space by requesting to not have a kernel mapping set up for them. Signed-off-by: Lucas Stach Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 834f11fe9dc2..287dc1692286 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2387,6 +2387,7 @@ int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->dma_attrs = DMA_ATTR_NO_KERNEL_MAPPING; dst_vq->mem_ops = &vb2_dma_contig_memops; return coda_queue_init(priv, dst_vq); -- cgit v1.2.3 From fa7662aad7dc033a61ff2f705c33cbb301d0552d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 24 Oct 2019 09:23:04 -0300 Subject: media: bt819: Reduce amount of F* words in the world Replace F* word with something less offensive. [hverkuil: dropped 'Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")' tag since nothing was broken] Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/bt819.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 43336175c7d9..73bc50c919d7 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -157,7 +157,7 @@ static int bt819_init(struct v4l2_subdev *sd) 0x12, 0x04, /* 0x12 Output Format */ 0x13, 0x20, /* 0x13 Vertical Scaling msb 0x00 chroma comb OFF, line drop scaling, interlace scaling - BUG? Why does turning the chroma comb on fuck up color? + BUG? Why does turning the chroma comb on screw up color? Bug in the bt819 stepping on my board? */ 0x14, 0x00, /* 0x14 Vertical Scaling lsb */ -- cgit v1.2.3 From c05b9d7b9f3ece2831e4e4829f10e904df296df8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 24 Oct 2019 10:09:16 -0300 Subject: media: fdp1: Fix R-Car M3-N naming in debug message The official name is "R-Car M3-N", not "R-Car M3N". Fixes: 4e8c120de9268fc2 ("media: fdp1: Support M3N and E3 platforms") Signed-off-by: Geert Uytterhoeven Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_fdp1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index cb93a13e1777..97bed45360f0 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -2369,7 +2369,7 @@ static int fdp1_probe(struct platform_device *pdev) dprintk(fdp1, "FDP1 Version R-Car H3\n"); break; case FD1_IP_M3N: - dprintk(fdp1, "FDP1 Version R-Car M3N\n"); + dprintk(fdp1, "FDP1 Version R-Car M3-N\n"); break; case FD1_IP_E3: dprintk(fdp1, "FDP1 Version R-Car E3\n"); -- cgit v1.2.3 From cb639a6f4a0ca7b8ada51a1ced71583b2f3a4bef Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 24 Oct 2019 10:35:44 -0300 Subject: media: s5p-jpeg: drop unused components from s5p_jpeg_q_data The number of components are only set, and never used. Signed-off-by: Philipp Zabel Reviewed-by: Jacek Anaszewski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.c | 1 - drivers/media/platform/s5p-jpeg/jpeg-core.h | 2 -- 2 files changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 8dbbd5f2a40a..ac2162235cef 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1236,7 +1236,6 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, } result->sof = sof; result->sof_len = sof_len; - result->components = components; return true; } diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 3bc52f83f5bc..4407fe775afa 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -190,7 +190,6 @@ struct s5p_jpeg_marker { * @dqt: DQT markers' positions relative to the buffer beginning * @sof: SOF0 marker's position relative to the buffer beginning * @sof_len: SOF0 marker's payload length (without length field itself) - * @components: number of image components * @size: image buffer size in bytes */ struct s5p_jpeg_q_data { @@ -202,7 +201,6 @@ struct s5p_jpeg_q_data { struct s5p_jpeg_marker dqt; u32 sof; u32 sof_len; - u32 components; u32 size; }; -- cgit v1.2.3 From e62138403a841e4f5c08fe338cfe9d83eb3e5e0f Mon Sep 17 00:00:00 2001 From: Shawn Tu Date: Fri, 1 Nov 2019 07:44:31 -0300 Subject: media: hi556: Add support for Hi-556 sensor Add a V4L2 sub-device driver for Hynix Hi-556 image sensor. This is a camera sensor using the I2C bus for control and the CSI-2 bus for data. This driver supports following features: - manual exposure and analog/digital gain control support - vblank/hblank control support - test pattern support - media controller support - runtime PM support - support following resolutions: + 2592x1944 at 30FPS + 1296x972 at 30FPS [sakari.ailus@linux.intel.com: Remove MEDIA_CAMERA_SUPPORT from Kconfig dependencies] Signed-off-by: Shawn Tu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/hi556.c | 1200 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1213 insertions(+) create mode 100644 drivers/media/i2c/hi556.c (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 992f60825ccd..c68e002d26ea 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -568,6 +568,18 @@ config VIDEO_SMIAPP_PLL if MEDIA_CAMERA_SUPPORT +config VIDEO_HI556 + tristate "Hynix Hi-556 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CONTROLLER + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Hynix + Hi-556 camera. + + To compile this driver as a module, choose M here: the + module will be called hi556. + config VIDEO_IMX214 tristate "Sony IMX214 sensor support" depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 6a80a353393f..c147bb9d28db 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -109,6 +109,7 @@ obj-$(CONFIG_VIDEO_I2C) += video-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c new file mode 100644 index 000000000000..c66cd1446c0f --- /dev/null +++ b/drivers/media/i2c/hi556.c @@ -0,0 +1,1200 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HI556_REG_VALUE_08BIT 1 +#define HI556_REG_VALUE_16BIT 2 +#define HI556_REG_VALUE_24BIT 3 + +#define HI556_LINK_FREQ_437MHZ 437000000ULL +#define HI556_MCLK 19200000 +#define HI556_DATA_LANES 2 +#define HI556_RGB_DEPTH 10 + +#define HI556_REG_CHIP_ID 0x0f16 +#define HI556_CHIP_ID 0x0556 + +#define HI556_REG_MODE_SELECT 0x0a00 +#define HI556_MODE_STANDBY 0x0000 +#define HI556_MODE_STREAMING 0x0100 + +/* vertical-timings from sensor */ +#define HI556_REG_FLL 0x0006 +#define HI556_FLL_30FPS 0x0814 +#define HI556_FLL_30FPS_MIN 0x0814 +#define HI556_FLL_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define HI556_REG_LLP 0x0008 + +/* Exposure controls from sensor */ +#define HI556_REG_EXPOSURE 0x0074 +#define HI556_EXPOSURE_MIN 6 +#define HI556_EXPOSURE_MAX_MARGIN 2 +#define HI556_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define HI556_REG_ANALOG_GAIN 0x0077 +#define HI556_ANAL_GAIN_MIN 0 +#define HI556_ANAL_GAIN_MAX 240 +#define HI556_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define HI556_REG_MWB_GR_GAIN 0x0078 +#define HI556_REG_MWB_GB_GAIN 0x007a +#define HI556_REG_MWB_R_GAIN 0x007c +#define HI556_REG_MWB_B_GAIN 0x007e +#define HI556_DGTL_GAIN_MIN 0 +#define HI556_DGTL_GAIN_MAX 2048 +#define HI556_DGTL_GAIN_STEP 1 +#define HI556_DGTL_GAIN_DEFAULT 256 + +/* Test Pattern Control */ +#define HI556_REG_ISP 0X0a05 +#define HI556_REG_ISP_TPG_EN 0x01 +#define HI556_REG_TEST_PATTERN 0x0201 + +enum { + HI556_LINK_FREQ_437MHZ_INDEX, +}; + +struct hi556_reg { + u16 address; + u16 val; +}; + +struct hi556_reg_list { + u32 num_of_regs; + const struct hi556_reg *regs; +}; + +struct hi556_link_freq_config { + const struct hi556_reg_list reg_list; +}; + +struct hi556_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 llp; + + /* Default vertical timining size */ + u32 fll_def; + + /* Min vertical timining size */ + u32 fll_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct hi556_reg_list reg_list; +}; + +#define to_hi556(_sd) container_of(_sd, struct hi556, sd) + +//SENSOR_INITIALIZATION +static const struct hi556_reg mipi_data_rate_874mbps[] = { + {0x0e00, 0x0102}, + {0x0e02, 0x0102}, + {0x0e0c, 0x0100}, + {0x2000, 0x7400}, + {0x2002, 0x001c}, + {0x2004, 0x0242}, + {0x2006, 0x0942}, + {0x2008, 0x7007}, + {0x200a, 0x0fd9}, + {0x200c, 0x0259}, + {0x200e, 0x7008}, + {0x2010, 0x160e}, + {0x2012, 0x0047}, + {0x2014, 0x2118}, + {0x2016, 0x0041}, + {0x2018, 0x00d8}, + {0x201a, 0x0145}, + {0x201c, 0x0006}, + {0x201e, 0x0181}, + {0x2020, 0x13cc}, + {0x2022, 0x2057}, + {0x2024, 0x7001}, + {0x2026, 0x0fca}, + {0x2028, 0x00cb}, + {0x202a, 0x009f}, + {0x202c, 0x7002}, + {0x202e, 0x13cc}, + {0x2030, 0x019b}, + {0x2032, 0x014d}, + {0x2034, 0x2987}, + {0x2036, 0x2766}, + {0x2038, 0x0020}, + {0x203a, 0x2060}, + {0x203c, 0x0e5d}, + {0x203e, 0x181d}, + {0x2040, 0x2066}, + {0x2042, 0x20c4}, + {0x2044, 0x5000}, + {0x2046, 0x0005}, + {0x2048, 0x0000}, + {0x204a, 0x01db}, + {0x204c, 0x025a}, + {0x204e, 0x00c0}, + {0x2050, 0x0005}, + {0x2052, 0x0006}, + {0x2054, 0x0ad9}, + {0x2056, 0x0259}, + {0x2058, 0x0618}, + {0x205a, 0x0258}, + {0x205c, 0x2266}, + {0x205e, 0x20c8}, + {0x2060, 0x2060}, + {0x2062, 0x707b}, + {0x2064, 0x0fdd}, + {0x2066, 0x81b8}, + {0x2068, 0x5040}, + {0x206a, 0x0020}, + {0x206c, 0x5060}, + {0x206e, 0x3143}, + {0x2070, 0x5081}, + {0x2072, 0x025c}, + {0x2074, 0x7800}, + {0x2076, 0x7400}, + {0x2078, 0x001c}, + {0x207a, 0x0242}, + {0x207c, 0x0942}, + {0x207e, 0x0bd9}, + {0x2080, 0x0259}, + {0x2082, 0x7008}, + {0x2084, 0x160e}, + {0x2086, 0x0047}, + {0x2088, 0x2118}, + {0x208a, 0x0041}, + {0x208c, 0x00d8}, + {0x208e, 0x0145}, + {0x2090, 0x0006}, + {0x2092, 0x0181}, + {0x2094, 0x13cc}, + {0x2096, 0x2057}, + {0x2098, 0x7001}, + {0x209a, 0x0fca}, + {0x209c, 0x00cb}, + {0x209e, 0x009f}, + {0x20a0, 0x7002}, + {0x20a2, 0x13cc}, + {0x20a4, 0x019b}, + {0x20a6, 0x014d}, + {0x20a8, 0x2987}, + {0x20aa, 0x2766}, + {0x20ac, 0x0020}, + {0x20ae, 0x2060}, + {0x20b0, 0x0e5d}, + {0x20b2, 0x181d}, + {0x20b4, 0x2066}, + {0x20b6, 0x20c4}, + {0x20b8, 0x50a0}, + {0x20ba, 0x0005}, + {0x20bc, 0x0000}, + {0x20be, 0x01db}, + {0x20c0, 0x025a}, + {0x20c2, 0x00c0}, + {0x20c4, 0x0005}, + {0x20c6, 0x0006}, + {0x20c8, 0x0ad9}, + {0x20ca, 0x0259}, + {0x20cc, 0x0618}, + {0x20ce, 0x0258}, + {0x20d0, 0x2266}, + {0x20d2, 0x20c8}, + {0x20d4, 0x2060}, + {0x20d6, 0x707b}, + {0x20d8, 0x0fdd}, + {0x20da, 0x86b8}, + {0x20dc, 0x50e0}, + {0x20de, 0x0020}, + {0x20e0, 0x5100}, + {0x20e2, 0x3143}, + {0x20e4, 0x5121}, + {0x20e6, 0x7800}, + {0x20e8, 0x3140}, + {0x20ea, 0x01c4}, + {0x20ec, 0x01c1}, + {0x20ee, 0x01c0}, + {0x20f0, 0x01c4}, + {0x20f2, 0x2700}, + {0x20f4, 0x3d40}, + {0x20f6, 0x7800}, + {0x20f8, 0xffff}, + {0x27fe, 0xe000}, + {0x3000, 0x60f8}, + {0x3002, 0x187f}, + {0x3004, 0x7060}, + {0x3006, 0x0114}, + {0x3008, 0x60b0}, + {0x300a, 0x1473}, + {0x300c, 0x0013}, + {0x300e, 0x140f}, + {0x3010, 0x0040}, + {0x3012, 0x100f}, + {0x3014, 0x60f8}, + {0x3016, 0x187f}, + {0x3018, 0x7060}, + {0x301a, 0x0114}, + {0x301c, 0x60b0}, + {0x301e, 0x1473}, + {0x3020, 0x0013}, + {0x3022, 0x140f}, + {0x3024, 0x0040}, + {0x3026, 0x000f}, + + {0x0b00, 0x0000}, + {0x0b02, 0x0045}, + {0x0b04, 0xb405}, + {0x0b06, 0xc403}, + {0x0b08, 0x0081}, + {0x0b0a, 0x8252}, + {0x0b0c, 0xf814}, + {0x0b0e, 0xc618}, + {0x0b10, 0xa828}, + {0x0b12, 0x004c}, + {0x0b14, 0x4068}, + {0x0b16, 0x0000}, + {0x0f30, 0x5b15}, + {0x0f32, 0x7067}, + {0x0954, 0x0009}, + {0x0956, 0x0000}, + {0x0958, 0xbb80}, + {0x095a, 0x5140}, + {0x0c00, 0x1110}, + {0x0c02, 0x0011}, + {0x0c04, 0x0000}, + {0x0c06, 0x0200}, + {0x0c10, 0x0040}, + {0x0c12, 0x0040}, + {0x0c14, 0x0040}, + {0x0c16, 0x0040}, + {0x0a10, 0x4000}, + {0x3068, 0xf800}, + {0x306a, 0xf876}, + {0x006c, 0x0000}, + {0x005e, 0x0200}, + {0x000e, 0x0100}, + {0x0e0a, 0x0001}, + {0x004a, 0x0100}, + {0x004c, 0x0000}, + {0x004e, 0x0100}, + {0x000c, 0x0022}, + {0x0008, 0x0b00}, + {0x005a, 0x0202}, + {0x0012, 0x000e}, + {0x0018, 0x0a33}, + {0x0022, 0x0008}, + {0x0028, 0x0017}, + {0x0024, 0x0028}, + {0x002a, 0x002d}, + {0x0026, 0x0030}, + {0x002c, 0x07c9}, + {0x002e, 0x1111}, + {0x0030, 0x1111}, + {0x0032, 0x1111}, + {0x0006, 0x07bc}, + {0x0a22, 0x0000}, + {0x0a12, 0x0a20}, + {0x0a14, 0x0798}, + {0x003e, 0x0000}, + {0x0074, 0x080e}, + {0x0070, 0x0407}, + {0x0002, 0x0000}, + {0x0a02, 0x0100}, + {0x0a24, 0x0100}, + {0x0046, 0x0000}, + {0x0076, 0x0000}, + {0x0060, 0x0000}, + {0x0062, 0x0530}, + {0x0064, 0x0500}, + {0x0066, 0x0530}, + {0x0068, 0x0500}, + {0x0122, 0x0300}, + {0x015a, 0xff08}, + {0x0804, 0x0300}, + {0x0806, 0x0100}, + {0x005c, 0x0102}, + {0x0a1a, 0x0800}, +}; + +static const struct hi556_reg mode_2592x1944_regs[] = { + {0x0a00, 0x0000}, + {0x0b0a, 0x8252}, + {0x0f30, 0x5b15}, + {0x0f32, 0x7067}, + {0x004a, 0x0100}, + {0x004c, 0x0000}, + {0x004e, 0x0100}, + {0x000c, 0x0022}, + {0x0008, 0x0b00}, + {0x005a, 0x0202}, + {0x0012, 0x000e}, + {0x0018, 0x0a33}, + {0x0022, 0x0008}, + {0x0028, 0x0017}, + {0x0024, 0x0028}, + {0x002a, 0x002d}, + {0x0026, 0x0030}, + {0x002c, 0x07c9}, + {0x002e, 0x1111}, + {0x0030, 0x1111}, + {0x0032, 0x1111}, + {0x0006, 0x0814}, + {0x0a22, 0x0000}, + {0x0a12, 0x0a20}, + {0x0a14, 0x0798}, + {0x003e, 0x0000}, + {0x0074, 0x0812}, + {0x0070, 0x0409}, + {0x0804, 0x0300}, + {0x0806, 0x0100}, + {0x0a04, 0x014a}, + {0x090c, 0x0fdc}, + {0x090e, 0x002d}, + + {0x0902, 0x4319}, + {0x0914, 0xc10a}, + {0x0916, 0x071f}, + {0x0918, 0x0408}, + {0x091a, 0x0c0d}, + {0x091c, 0x0f09}, + {0x091e, 0x0a00}, + {0x0958, 0xbb80}, +}; + +static const struct hi556_reg mode_1296x972_regs[] = { + {0x0a00, 0x0000}, + {0x0b0a, 0x8259}, + {0x0f30, 0x5b15}, + {0x0f32, 0x7167}, + {0x004a, 0x0100}, + {0x004c, 0x0000}, + {0x004e, 0x0100}, + {0x000c, 0x0122}, + {0x0008, 0x0b00}, + {0x005a, 0x0404}, + {0x0012, 0x000c}, + {0x0018, 0x0a33}, + {0x0022, 0x0008}, + {0x0028, 0x0017}, + {0x0024, 0x0022}, + {0x002a, 0x002b}, + {0x0026, 0x0030}, + {0x002c, 0x07c9}, + {0x002e, 0x3311}, + {0x0030, 0x3311}, + {0x0032, 0x3311}, + {0x0006, 0x0814}, + {0x0a22, 0x0000}, + {0x0a12, 0x0510}, + {0x0a14, 0x03cc}, + {0x003e, 0x0000}, + {0x0074, 0x0812}, + {0x0070, 0x0409}, + {0x0804, 0x0308}, + {0x0806, 0x0100}, + {0x0a04, 0x016a}, + {0x090e, 0x0010}, + {0x090c, 0x09c0}, + + {0x0902, 0x4319}, + {0x0914, 0xc106}, + {0x0916, 0x040e}, + {0x0918, 0x0304}, + {0x091a, 0x0708}, + {0x091c, 0x0e06}, + {0x091e, 0x0300}, + {0x0958, 0xbb80}, +}; + +static const char * const hi556_test_pattern_menu[] = { + "Disabled", + "Solid Colour", + "100% Colour Bars", + "Fade To Grey Colour Bars", + "PN9", + "Gradient Horizontal", + "Gradient Vertical", + "Check Board", + "Slant Pattern", +}; + +static const s64 link_freq_menu_items[] = { + HI556_LINK_FREQ_437MHZ, +}; + +static const struct hi556_link_freq_config link_freq_configs[] = { + [HI556_LINK_FREQ_437MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_874mbps), + .regs = mipi_data_rate_874mbps, + } + } +}; + +static const struct hi556_mode supported_modes[] = { + { + .width = 2592, + .height = 1944, + .fll_def = HI556_FLL_30FPS, + .fll_min = HI556_FLL_30FPS_MIN, + .llp = 0x0b00, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), + .regs = mode_2592x1944_regs, + }, + .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, + }, + { + .width = 1296, + .height = 972, + .fll_def = HI556_FLL_30FPS, + .fll_min = HI556_FLL_30FPS_MIN, + .llp = 0x0b00, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x972_regs), + .regs = mode_1296x972_regs, + }, + .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, + } +}; + +struct hi556 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct hi556_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * HI556_DATA_LANES; + + do_div(pixel_rate, HI556_RGB_DEPTH); + + return pixel_rate; +} + +static int hi556_read_reg(struct hi556 *hi556, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int hi556_write_reg(struct hi556 *hi556, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int hi556_write_reg_list(struct hi556 *hi556, + const struct hi556_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = hi556_write_reg(hi556, r_list->regs[i].address, + HI556_REG_VALUE_16BIT, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "failed to write reg 0x%4.4x. error = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int hi556_update_digital_gain(struct hi556 *hi556, u32 d_gain) +{ + int ret; + + ret = hi556_write_reg(hi556, HI556_REG_MWB_GR_GAIN, + HI556_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_MWB_GB_GAIN, + HI556_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_MWB_R_GAIN, + HI556_REG_VALUE_16BIT, d_gain); + if (ret) + return ret; + + return hi556_write_reg(hi556, HI556_REG_MWB_B_GAIN, + HI556_REG_VALUE_16BIT, d_gain); +} + +static int hi556_test_pattern(struct hi556 *hi556, u32 pattern) +{ + int ret; + u32 val; + + if (pattern) { + ret = hi556_read_reg(hi556, HI556_REG_ISP, + HI556_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_ISP, + HI556_REG_VALUE_08BIT, + val | HI556_REG_ISP_TPG_EN); + if (ret) + return ret; + } + + return hi556_write_reg(hi556, HI556_REG_TEST_PATTERN, + HI556_REG_VALUE_08BIT, pattern); +} + +static int hi556_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hi556 *hi556 = container_of(ctrl->handler, + struct hi556, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = hi556->cur_mode->height + ctrl->val - + HI556_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(hi556->exposure, + hi556->exposure->minimum, + exposure_max, hi556->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = hi556_write_reg(hi556, HI556_REG_ANALOG_GAIN, + HI556_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = hi556_update_digital_gain(hi556, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + ret = hi556_write_reg(hi556, HI556_REG_EXPOSURE, + HI556_REG_VALUE_16BIT, ctrl->val); + break; + + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = hi556_write_reg(hi556, HI556_REG_FLL, + HI556_REG_VALUE_16BIT, + hi556->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = hi556_test_pattern(hi556, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops hi556_ctrl_ops = { + .s_ctrl = hi556_set_ctrl, +}; + +static int hi556_init_controls(struct hi556 *hi556) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &hi556->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &hi556->mutex; + hi556->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, link_freq_menu_items); + if (hi556->link_freq) + hi556->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + hi556->pixel_rate = v4l2_ctrl_new_std + (ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + to_pixel_rate(HI556_LINK_FREQ_437MHZ_INDEX), + 1, + to_pixel_rate(HI556_LINK_FREQ_437MHZ_INDEX)); + hi556->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_VBLANK, + hi556->cur_mode->fll_min - + hi556->cur_mode->height, + HI556_FLL_MAX - + hi556->cur_mode->height, 1, + hi556->cur_mode->fll_def - + hi556->cur_mode->height); + + h_blank = hi556->cur_mode->llp - hi556->cur_mode->width; + + hi556->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (hi556->hblank) + hi556->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + HI556_ANAL_GAIN_MIN, HI556_ANAL_GAIN_MAX, + HI556_ANAL_GAIN_STEP, HI556_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + HI556_DGTL_GAIN_MIN, HI556_DGTL_GAIN_MAX, + HI556_DGTL_GAIN_STEP, HI556_DGTL_GAIN_DEFAULT); + exposure_max = hi556->cur_mode->fll_def - HI556_EXPOSURE_MAX_MARGIN; + hi556->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_EXPOSURE, + HI556_EXPOSURE_MIN, exposure_max, + HI556_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hi556_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(hi556_test_pattern_menu) - 1, + 0, 0, hi556_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + hi556->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void hi556_assign_pad_format(const struct hi556_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int hi556_start_streaming(struct hi556 *hi556) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + const struct hi556_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = hi556->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = hi556_write_reg_list(hi556, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &hi556->cur_mode->reg_list; + ret = hi556_write_reg_list(hi556, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(hi556->sd.ctrl_handler); + if (ret) + return ret; + + ret = hi556_write_reg(hi556, HI556_REG_MODE_SELECT, + HI556_REG_VALUE_16BIT, HI556_MODE_STREAMING); + + if (ret) { + dev_err(&client->dev, "failed to set stream"); + return ret; + } + + return 0; +} + +static void hi556_stop_streaming(struct hi556 *hi556) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + + if (hi556_write_reg(hi556, HI556_REG_MODE_SELECT, + HI556_REG_VALUE_16BIT, HI556_MODE_STANDBY)) + dev_err(&client->dev, "failed to set stream"); +} + +static int hi556_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct hi556 *hi556 = to_hi556(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (hi556->streaming == enable) + return 0; + + mutex_lock(&hi556->mutex); + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&hi556->mutex); + return ret; + } + + ret = hi556_start_streaming(hi556); + if (ret) { + enable = 0; + hi556_stop_streaming(hi556); + pm_runtime_put(&client->dev); + } + } else { + hi556_stop_streaming(hi556); + pm_runtime_put(&client->dev); + } + + hi556->streaming = enable; + mutex_unlock(&hi556->mutex); + + return ret; +} + +static int __maybe_unused hi556_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + + mutex_lock(&hi556->mutex); + if (hi556->streaming) + hi556_stop_streaming(hi556); + + mutex_unlock(&hi556->mutex); + + return 0; +} + +static int __maybe_unused hi556_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + int ret; + + mutex_lock(&hi556->mutex); + if (hi556->streaming) { + ret = hi556_start_streaming(hi556); + if (ret) + goto error; + } + + mutex_unlock(&hi556->mutex); + + return 0; + +error: + hi556_stop_streaming(hi556); + hi556->streaming = 0; + mutex_unlock(&hi556->mutex); + return ret; +} + +static int hi556_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct hi556 *hi556 = to_hi556(sd); + const struct hi556_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&hi556->mutex); + hi556_assign_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + hi556->cur_mode = mode; + __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(hi556->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->fll_def - mode->height; + __v4l2_ctrl_modify_range(hi556->vblank, + mode->fll_min - mode->height, + HI556_FLL_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(hi556->vblank, vblank_def); + + h_blank = hi556->cur_mode->llp - hi556->cur_mode->width; + + __v4l2_ctrl_modify_range(hi556->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&hi556->mutex); + + return 0; +} + +static int hi556_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct hi556 *hi556 = to_hi556(sd); + + mutex_lock(&hi556->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, cfg, + fmt->pad); + else + hi556_assign_pad_format(hi556->cur_mode, &fmt->format); + + mutex_unlock(&hi556->mutex); + + return 0; +} + +static int hi556_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int hi556_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct hi556 *hi556 = to_hi556(sd); + + mutex_lock(&hi556->mutex); + hi556_assign_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); + mutex_unlock(&hi556->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops hi556_video_ops = { + .s_stream = hi556_set_stream, +}; + +static const struct v4l2_subdev_pad_ops hi556_pad_ops = { + .set_fmt = hi556_set_format, + .get_fmt = hi556_get_format, + .enum_mbus_code = hi556_enum_mbus_code, + .enum_frame_size = hi556_enum_frame_size, +}; + +static const struct v4l2_subdev_ops hi556_subdev_ops = { + .video = &hi556_video_ops, + .pad = &hi556_pad_ops, +}; + +static const struct media_entity_operations hi556_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops hi556_internal_ops = { + .open = hi556_open, +}; + +static int hi556_identify_module(struct hi556 *hi556) +{ + struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + int ret; + u32 val; + + ret = hi556_read_reg(hi556, HI556_REG_CHIP_ID, + HI556_REG_VALUE_16BIT, &val); + if (ret) + return ret; + + if (val != HI556_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + HI556_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int hi556_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret = 0; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (ret) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + if (mclk != HI556_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "number of CSI2 data lanes %d is not supported", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto check_hwcfg_error; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int hi556_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&hi556->mutex); + + return 0; +} + +static int hi556_probe(struct i2c_client *client) +{ + struct hi556 *hi556; + int ret; + + ret = hi556_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + hi556 = devm_kzalloc(&client->dev, sizeof(*hi556), GFP_KERNEL); + if (!hi556) + return -ENOMEM; + + v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); + ret = hi556_identify_module(hi556); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&hi556->mutex); + hi556->cur_mode = &supported_modes[0]; + ret = hi556_init_controls(hi556); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + hi556->sd.internal_ops = &hi556_internal_ops; + hi556->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + hi556->sd.entity.ops = &hi556_subdev_entity_ops; + hi556->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + hi556->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&hi556->sd.entity, 1, &hi556->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&hi556->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&hi556->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(hi556->sd.ctrl_handler); + mutex_destroy(&hi556->mutex); + + return ret; +} + +static const struct dev_pm_ops hi556_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(hi556_suspend, hi556_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id hi556_acpi_ids[] = { + {"INT3537"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, hi556_acpi_ids); +#endif + +static struct i2c_driver hi556_i2c_driver = { + .driver = { + .name = "hi556", + .pm = &hi556_pm_ops, + .acpi_match_table = ACPI_PTR(hi556_acpi_ids), + }, + .probe_new = hi556_probe, + .remove = hi556_remove, +}; + +module_i2c_driver(hi556_i2c_driver); + +MODULE_AUTHOR("Shawn Tu "); +MODULE_DESCRIPTION("Hynix HI556 sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 31172e520b688524acaac61de1f1f9fc89665ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Wed, 30 Oct 2019 09:30:40 -0300 Subject: media: vimc: Make capture devices and subdevices use different link_validates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of validating the links to capture devices and subdevices with the same function, use the default v4l function for links between subdevices and only use a different function for validating between capture device and subdevice. This change should also ease future work to associate multiple mbus codes for the same pixelformat in vimc_pix_map. These changes were tested with v4l2-compliance SHA: 3f806630e2ecbcebe31872b865c5c4b42f111a99, 64 bits and passed all tests: Grand Total for vimc device /dev/media0: 451, Succeeded: 451, Failed: 0, Warnings: 0 Signed-off-by: Nícolas F. R. A. Prado Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 2 +- drivers/media/platform/vimc/vimc-common.c | 85 +++++++++++++++--------------- drivers/media/platform/vimc/vimc-common.h | 4 +- 3 files changed, 46 insertions(+), 45 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index a5d79fb25dff..76c015898cfd 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -322,7 +322,7 @@ static const struct vb2_ops vimc_cap_qops = { }; static const struct media_entity_operations vimc_cap_mops = { - .link_validate = vimc_link_validate, + .link_validate = vimc_vdev_link_validate, }; static void vimc_cap_release(struct video_device *vdev) diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 2a0c40e9ae88..43e6fa5886da 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -194,35 +194,36 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) return NULL; } -static int vimc_get_mbus_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) +static int vimc_get_pix_format(struct media_pad *pad, + struct v4l2_pix_format *fmt) { if (is_media_entity_v4l2_subdev(pad->entity)) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); + struct v4l2_subdev_format sd_fmt; + const struct vimc_pix_map *pix_map; int ret; - fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt->pad = pad->index; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = pad->index; - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret; + v4l2_fill_pix_format(fmt, &sd_fmt.format); + pix_map = vimc_pix_map_by_code(sd_fmt.format.code); + fmt->pixelformat = pix_map->pixelformat; } else if (is_media_entity_v4l2_video_device(pad->entity)) { struct video_device *vdev = container_of(pad->entity, struct video_device, entity); struct vimc_ent_device *ved = video_get_drvdata(vdev); - const struct vimc_pix_map *vpix; - struct v4l2_pix_format vdev_fmt; if (!ved->vdev_get_format) return -ENOIOCTLCMD; - ved->vdev_get_format(ved, &vdev_fmt); - vpix = vimc_pix_map_by_pixelformat(vdev_fmt.pixelformat); - v4l2_fill_mbus_format(&fmt->format, &vdev_fmt, vpix->code); + ved->vdev_get_format(ved, fmt); } else { return -EINVAL; } @@ -230,16 +231,16 @@ static int vimc_get_mbus_format(struct media_pad *pad, return 0; } -int vimc_link_validate(struct media_link *link) +int vimc_vdev_link_validate(struct media_link *link) { - struct v4l2_subdev_format source_fmt, sink_fmt; + struct v4l2_pix_format source_fmt, sink_fmt; int ret; - ret = vimc_get_mbus_format(link->source, &source_fmt); + ret = vimc_get_pix_format(link->source, &source_fmt); if (ret) return ret; - ret = vimc_get_mbus_format(link->sink, &sink_fmt); + ret = vimc_get_pix_format(link->sink, &sink_fmt); if (ret) return ret; @@ -248,21 +249,21 @@ int vimc_link_validate(struct media_link *link) "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n", /* src */ link->source->entity->name, - source_fmt.format.width, source_fmt.format.height, - source_fmt.format.code, source_fmt.format.colorspace, - source_fmt.format.quantization, source_fmt.format.xfer_func, - source_fmt.format.ycbcr_enc, + source_fmt.width, source_fmt.height, + source_fmt.pixelformat, source_fmt.colorspace, + source_fmt.quantization, source_fmt.xfer_func, + source_fmt.ycbcr_enc, /* sink */ link->sink->entity->name, - sink_fmt.format.width, sink_fmt.format.height, - sink_fmt.format.code, sink_fmt.format.colorspace, - sink_fmt.format.quantization, sink_fmt.format.xfer_func, - sink_fmt.format.ycbcr_enc); - - /* The width, height and code must match. */ - if (source_fmt.format.width != sink_fmt.format.width - || source_fmt.format.height != sink_fmt.format.height - || source_fmt.format.code != sink_fmt.format.code) + sink_fmt.width, sink_fmt.height, + sink_fmt.pixelformat, sink_fmt.colorspace, + sink_fmt.quantization, sink_fmt.xfer_func, + sink_fmt.ycbcr_enc); + + /* The width, height and pixelformat must match. */ + if (source_fmt.width != sink_fmt.width || + source_fmt.height != sink_fmt.height || + source_fmt.pixelformat != sink_fmt.pixelformat) return -EPIPE; /* @@ -270,43 +271,43 @@ int vimc_link_validate(struct media_link *link) * to support interlaced hardware connected to bridges that support * progressive formats only. */ - if (source_fmt.format.field != sink_fmt.format.field && - sink_fmt.format.field != V4L2_FIELD_NONE) + if (source_fmt.field != sink_fmt.field && + sink_fmt.field != V4L2_FIELD_NONE) return -EPIPE; /* * If colorspace is DEFAULT, then assume all the colorimetry is also * DEFAULT, return 0 to skip comparing the other colorimetry parameters */ - if (source_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT - || sink_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT) + if (source_fmt.colorspace == V4L2_COLORSPACE_DEFAULT || + sink_fmt.colorspace == V4L2_COLORSPACE_DEFAULT) return 0; /* Colorspace must match. */ - if (source_fmt.format.colorspace != sink_fmt.format.colorspace) + if (source_fmt.colorspace != sink_fmt.colorspace) return -EPIPE; /* Colorimetry must match if they are not set to DEFAULT */ - if (source_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT - && sink_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT - && source_fmt.format.ycbcr_enc != sink_fmt.format.ycbcr_enc) + if (source_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && + sink_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && + source_fmt.ycbcr_enc != sink_fmt.ycbcr_enc) return -EPIPE; - if (source_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT - && sink_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT - && source_fmt.format.quantization != sink_fmt.format.quantization) + if (source_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && + sink_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && + source_fmt.quantization != sink_fmt.quantization) return -EPIPE; - if (source_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT - && sink_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT - && source_fmt.format.xfer_func != sink_fmt.format.xfer_func) + if (source_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && + sink_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && + source_fmt.xfer_func != sink_fmt.xfer_func) return -EPIPE; return 0; } static const struct media_entity_operations vimc_ent_sd_mops = { - .link_validate = vimc_link_validate, + .link_validate = v4l2_subdev_link_validate, }; int vimc_ent_sd_register(struct vimc_ent_device *ved, diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index c75401a36312..bf729fcde6a9 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -206,12 +206,12 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, const struct v4l2_subdev_ops *sd_ops); /** - * vimc_link_validate - validates a media link + * vimc_vdev_link_validate - validates a media link * * @link: pointer to &struct media_link * * This function calls validates if a media link is valid for streaming. */ -int vimc_link_validate(struct media_link *link); +int vimc_vdev_link_validate(struct media_link *link); #endif -- cgit v1.2.3 From 8ffd573c25e5fac1daeeffc592e2ed6bc6a3d947 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 2 Nov 2019 14:35:41 -0300 Subject: media: vivid: media_device_cleanup was called too early Running the contrib/test/test-media script in v4l-utils with the vivid argument will cause this kernel warning: [ 104.748720] videodev: v4l2_release [ 104.748731] ------------[ cut here ]------------ [ 104.748750] DEBUG_LOCKS_WARN_ON(lock->magic != lock) [ 104.748790] WARNING: CPU: 6 PID: 1823 at kernel/locking/mutex.c:938 __mutex_lock+0x919/0xc10 [ 104.748800] Modules linked in: rc_cec vivid v4l2_tpg videobuf2_dma_contig cec rc_core v4l2_dv_timings videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc vmw_balloon vmw_vmci button vmwgfx [ 104.748845] CPU: 6 PID: 1823 Comm: sleep Not tainted 5.4.0-rc1-test-no #150 [ 104.748853] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/29/2019 [ 104.748867] RIP: 0010:__mutex_lock+0x919/0xc10 [ 104.748878] Code: 59 83 e8 9a fc 16 ff 44 8b 05 23 61 38 01 45 85 c0 0f 85 ef f7 ff ff 48 c7 c6 a0 1f 87 82 48 c7 c7 a0 1e 87 82 e8 cd bb f7 fe <0f> 0b e9 d5 f7 ff ff f6 c3 04 0f 84 3b fd ff ff 49 89 df 41 83 e7 [ 104.748886] RSP: 0018:ffff88811a357b80 EFLAGS: 00010286 [ 104.748895] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 [ 104.748902] RDX: 0000000000000003 RSI: 0000000000000004 RDI: ffffed102346af62 [ 104.748910] RBP: ffff88811a357cf0 R08: ffffffff81217c91 R09: fffffbfff061c271 [ 104.748917] R10: fffffbfff061c270 R11: ffffffff830e1383 R12: ffff8881a46103c0 [ 104.748924] R13: 0000000000000000 R14: ffff8881a4614f90 R15: ffff8881a46153d0 [ 104.748933] FS: 0000000000000000(0000) GS:ffff8881b6780000(0000) knlGS:0000000000000000 [ 104.748940] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 104.748949] CR2: 00007f163fc9ca20 CR3: 0000000003013004 CR4: 00000000001606e0 [ 104.749036] Call Trace: [ 104.749051] ? _raw_spin_unlock+0x1f/0x30 [ 104.749067] ? llist_add_batch+0x33/0x50 [ 104.749081] ? tick_nohz_tick_stopped+0x19/0x30 [ 104.749130] ? v4l2_release.cold+0x6c/0xd6 [videodev] [ 104.749143] ? mutex_lock_io_nested+0xb80/0xb80 [ 104.749153] ? vprintk_emit+0xf2/0x220 [ 104.749191] ? vivid_req_validate+0x40/0x40 [vivid] [ 104.749201] ? printk+0xad/0xde [ 104.749211] ? kmsg_dump_rewind_nolock+0x54/0x54 [ 104.749226] ? locks_remove_file+0x78/0x2b0 [ 104.749248] ? __fsnotify_update_child_dentry_flags.part.0+0x170/0x170 [ 104.749281] ? vivid_req_validate+0x40/0x40 [vivid] [ 104.749321] ? v4l2_release.cold+0x6c/0xd6 [videodev] [ 104.749361] v4l2_release.cold+0x6c/0xd6 [videodev] [ 104.749378] __fput+0x15a/0x390 [ 104.749393] task_work_run+0xb2/0xe0 [ 104.749407] do_exit+0x4d0/0x1200 [ 104.749422] ? do_user_addr_fault+0x367/0x610 [ 104.749431] ? release_task+0x990/0x990 [ 104.749449] ? rwsem_spin_on_owner+0x170/0x170 [ 104.749463] ? vmacache_find+0xb2/0x100 [ 104.749476] do_group_exit+0x85/0x130 [ 104.749487] __x64_sys_exit_group+0x23/0x30 [ 104.749500] do_syscall_64+0x5e/0x1c0 [ 104.749511] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 104.749520] RIP: 0033:0x7f163fc5c9d6 [ 104.749536] Code: Bad RIP value. [ 104.749543] RSP: 002b:00007ffe6f3bec58 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 [ 104.749553] RAX: ffffffffffffffda RBX: 00007f163fd4d760 RCX: 00007f163fc5c9d6 [ 104.749560] RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000 [ 104.749567] RBP: 0000000000000000 R08: 00000000000000e7 R09: ffffffffffffff80 [ 104.749574] R10: 00007ffe6f3beb24 R11: 0000000000000246 R12: 00007f163fd4d760 [ 104.749581] R13: 0000000000000002 R14: 00007f163fd56428 R15: 0000000000000000 [ 104.749597] ---[ end trace 66f20f73fc0daf79 ]--- This is caused by media_device_cleanup() which destroys v4l2_dev->mdev->req_queue_mutex. But v4l2_release() tries to lock that mutex after media_device_cleanup() is called. By moving media_device_cleanup() to the v4l2_device's release function it is guaranteed that the mutex is valid whenever v4l2_release is called. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index dadfc59c92c5..b204c636dba6 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -648,6 +648,9 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev) vivid_free_controls(dev); v4l2_device_unregister(&dev->v4l2_dev); +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_cleanup(&dev->mdev); +#endif vfree(dev->scaled_line); vfree(dev->blended_line); vfree(dev->edid); @@ -1755,7 +1758,6 @@ static int vivid_remove(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER media_device_unregister(&dev->mdev); - media_device_cleanup(&dev->mdev); #endif if (dev->has_vid_cap) { -- cgit v1.2.3 From 6a8c521c529eb0c806780886c7092d7f7fdcca7b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 08:23:31 -0300 Subject: media: am437x: fix smatch warning Fixes this warning: drivers/media/platform/am437x/am437x-vpfe.c:288 vpfe_ccdc_validate_param() warn: unsigned 'ccdcparam->alaw.gamma_wd' is never less than zero. by dropping the gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 check since VPFE_CCDC_GAMMA_BITS_15_6 is 0. Signed-off-by: Hans Verkuil Reviewed-by: Benoit Parrot Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 447610b67db4..09104304bd06 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -285,7 +285,6 @@ vpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc, max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 || - ccdcparam->alaw.gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 || max_gamma > max_data) { vpfe_dbg(1, vpfe, "Invalid data line select\n"); return -EINVAL; -- cgit v1.2.3 From c4abb192caca96a53c2e833d25b1f7f12b454bf4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 12:23:33 +0100 Subject: media: mtk-vpu: fix two smatch warnings Drop the "id >= 0" test in two conditions to fix these warnings: drivers/media/platform/mtk-vpu/mtk_vpu.c:276 vpu_ipi_register() warn: always true condition '(id >= 0) => (0-u32max >= 0)' drivers/media/platform/mtk-vpu/mtk_vpu.c:401 vpu_wdt_reg_handler() warn: always true condition '(id >= 0) => (0-u32max >= 0)' Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vpu/mtk_vpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index cc2ff40d060d..a768707abb94 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -273,7 +273,7 @@ int vpu_ipi_register(struct platform_device *pdev, return -EPROBE_DEFER; } - if (id >= 0 && id < IPI_MAX && handler) { + if (id < IPI_MAX && handler) { ipi_desc = vpu->ipi_desc; ipi_desc[id].name = name; ipi_desc[id].handler = handler; @@ -398,7 +398,7 @@ int vpu_wdt_reg_handler(struct platform_device *pdev, handler = vpu->wdt.handler; - if (id >= 0 && id < VPU_RST_MAX && wdt_reset) { + if (id < VPU_RST_MAX && wdt_reset) { dev_dbg(vpu->dev, "wdt register id %d\n", id); mutex_lock(&vpu->vpu_mutex); handler[id].reset_func = wdt_reset; -- cgit v1.2.3 From 492b53a50a1c565d067f48de96b85336f1ea601e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 12:23:34 +0100 Subject: media: rc/ite-cir: fix smatch warning Use sizeof instead of ARRAY_SIZE to fix this smatch warning: drivers/media/rc/ite-cir.c:385 ite_tx_ir() warn: calling memset(x, y, ARRAY_SIZE()); Signed-off-by: Hans Verkuil Acked-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ite-cir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 3ab6cec0dc3b..07667c04c1d2 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -382,7 +382,7 @@ static int ite_tx_ir(struct rc_dev *rcdev, unsigned *txbuf, unsigned n) ite_dbg("%s called", __func__); /* clear the array just in case */ - memset(last_sent, 0, ARRAY_SIZE(last_sent)); + memset(last_sent, 0, sizeof(last_sent)); spin_lock_irqsave(&dev->lock, flags); -- cgit v1.2.3 From 2df34d3a27c1f3b043689edb017a6572ac8a7934 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 12:23:35 +0100 Subject: media: em28xx: fix two smatch warnings Use sizeof instead of ARRAY_SIZE to fix this smatch warning: drivers/media/usb/em28xx/em28xx-i2c.c:952 em28xx_do_i2c_scan() warn: calling memset(x, y, ARRAY_SIZE()); Do the same for the em28xx_hash_mem() call in the same function. smatch didn't pick that up, but there too it should use sizeof instead of ARRAY_SIZE. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index a3155ec196cc..592b98b3643a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -949,7 +949,7 @@ void em28xx_do_i2c_scan(struct em28xx *dev, unsigned int bus) unsigned char buf; int i, rc; - memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist)); + memset(i2c_devicelist, 0, sizeof(i2c_devicelist)); for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { dev->i2c_client[bus].addr = i; @@ -964,7 +964,7 @@ void em28xx_do_i2c_scan(struct em28xx *dev, unsigned int bus) if (bus == dev->def_i2c_bus) dev->i2c_hash = em28xx_hash_mem(i2c_devicelist, - ARRAY_SIZE(i2c_devicelist), 32); + sizeof(i2c_devicelist), 32); } /* -- cgit v1.2.3 From 2c3e42bf78c7ad646856089eba9839f3942a5165 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 12:23:36 +0100 Subject: media: ti-vpe: fix smatch error This patch fixes this error: drivers/media/platform/ti-vpe/vpdma.c:767 dump_dtd() error: '%pad' expects argument of type 'dma_addr_t*', argument 2 has type 'uint*' dtd->start_addr is a u32, so no need for %pad. Signed-off-by: Hans Verkuil Reviewed-by: Benoit Parrot Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index 817d287c8138..2e5148ae7a0f 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -764,7 +764,7 @@ static void dump_dtd(struct vpdma_dtd *dtd) pr_debug("word1: line_length = %d, xfer_height = %d\n", dtd_get_line_length(dtd), dtd_get_xfer_height(dtd)); - pr_debug("word2: start_addr = %pad\n", &dtd->start_addr); + pr_debug("word2: start_addr = %x\n", dtd->start_addr); pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, pri = %d, next_chan = %d\n", dtd_get_pkt_type(dtd), -- cgit v1.2.3 From b5f72a17b1b2f5363f61075d501367ac7461581e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 12:23:37 +0100 Subject: media: ov6650: fix smatch warning Initialize ret to 0 to fix this smatch error: drivers/media/i2c/ov6650.c:853 ov6650_video_probe() error: uninitialized symbol 'ret'. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov6650.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 3c8f50f8619f..91906b94f978 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -819,7 +819,7 @@ static int ov6650_video_probe(struct v4l2_subdev *sd) const struct ov6650_xclk *xclk = NULL; unsigned long rate; u8 pidh, pidl, midh, midl; - int i, ret; + int i, ret = 0; priv->clk = v4l2_clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { -- cgit v1.2.3 From 61b8584a2f3a212283667af887d4aa05973569df Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 12:23:38 +0100 Subject: media: mantis: fix two smatch errors Drop two dprintk's that relied on a non-NULL mantis pointer when it was in fact a NULL pointer. Fix those warnings: drivers/media/pci/mantis/mantis_cards.c:73 mantis_irq_handler() error: we previously assumed 'mantis' could be null (see line 72) drivers/media/pci/mantis/hopper_cards.c:64 hopper_irq_handler() error: we previously assumed 'mantis' could be null (see line 63) Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/mantis/hopper_cards.c | 4 +--- drivers/media/pci/mantis/mantis_cards.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c index 67aebe759232..c0bd5d7e148b 100644 --- a/drivers/media/pci/mantis/hopper_cards.c +++ b/drivers/media/pci/mantis/hopper_cards.c @@ -60,10 +60,8 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id) struct mantis_ca *ca; mantis = (struct mantis_pci *) dev_id; - if (unlikely(!mantis)) { - dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); + if (unlikely(!mantis)) return IRQ_NONE; - } ca = mantis->mantis_ca; stat = mmread(MANTIS_INT_STAT); diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c index deadd0b92233..906e4500d87d 100644 --- a/drivers/media/pci/mantis/mantis_cards.c +++ b/drivers/media/pci/mantis/mantis_cards.c @@ -69,10 +69,8 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id) struct mantis_ca *ca; mantis = (struct mantis_pci *) dev_id; - if (unlikely(mantis == NULL)) { - dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); + if (unlikely(!mantis)) return IRQ_NONE; - } ca = mantis->mantis_ca; stat = mmread(MANTIS_INT_STAT); -- cgit v1.2.3 From 1318372450954b4fcd4b3886703286103b040ded Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 3 Nov 2019 14:07:20 +0100 Subject: media: siano: fix spelling mistake "ENBALE" -> "ENABLE" Macros MSG_SMS_ENBALE_TS_INTERFACE_REQ and MSG_SMS_ENBALE_TS_INTERFACE_RES contain a spelling mistake. Fix these by replacing ENBALE with ENABLE. Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/siano/smscoreapi.c | 4 ++-- drivers/media/common/siano/smscoreapi.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index 0ba51dacc580..c1511094fdc7 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -230,8 +230,8 @@ static char *siano_msgs[] = { [MSG_SMS_FLASH_DL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_FLASH_DL_REQ", [MSG_SMS_EXEC_TEST_1_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_REQ", [MSG_SMS_EXEC_TEST_1_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_RES", - [MSG_SMS_ENBALE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENBALE_TS_INTERFACE_REQ", - [MSG_SMS_ENBALE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENBALE_TS_INTERFACE_RES", + [MSG_SMS_ENABLE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_TS_INTERFACE_REQ", + [MSG_SMS_ENABLE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_TS_INTERFACE_RES", [MSG_SMS_SPI_SET_BUS_WIDTH_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_REQ", [MSG_SMS_SPI_SET_BUS_WIDTH_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_RES", [MSG_SMS_SEND_EMM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_EMM_REQ", diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index a2f95f4899c2..b3b793b5caf3 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -434,8 +434,8 @@ enum msg_types { MSG_SMS_FLASH_DL_REQ = 732, MSG_SMS_EXEC_TEST_1_REQ = 734, MSG_SMS_EXEC_TEST_1_RES = 735, - MSG_SMS_ENBALE_TS_INTERFACE_REQ = 736, - MSG_SMS_ENBALE_TS_INTERFACE_RES = 737, + MSG_SMS_ENABLE_TS_INTERFACE_REQ = 736, + MSG_SMS_ENABLE_TS_INTERFACE_RES = 737, MSG_SMS_SPI_SET_BUS_WIDTH_REQ = 738, MSG_SMS_SPI_SET_BUS_WIDTH_RES = 739, MSG_SMS_SEND_EMM_REQ = 740, -- cgit v1.2.3 From 0c90f649d2f5b9c6c585cab293a4e40172ddaa73 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Nov 2019 15:01:42 +0100 Subject: media: vivid: add vivid_create_queue() helper Refactor some of the vivid_create_instance code by using a new vivid_create_queue() helper function. Also add some sanity checks for the node_types vs input/output_types module options. This patch resolves these two smatch parse errors: drivers/media/platform/vivid/vivid-core.c:1679 vivid_create_instance() parse error: OOM: 3002600Kb sm_state_count = 6160113 drivers/media/platform/vivid/vivid-core.c: drivers/media/platform/vivid/vivid-core.c:1679 vivid_create_instance() parse error: __split_smt: function too hairy. Giving up after 33 seconds Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-cec.c | 7 +- drivers/media/platform/vivid/vivid-core.c | 257 +++++++++++++----------------- drivers/media/platform/vivid/vivid-core.h | 1 + 3 files changed, 116 insertions(+), 149 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c index 4d822dbed972..4d2413e87730 100644 --- a/drivers/media/platform/vivid/vivid-cec.c +++ b/drivers/media/platform/vivid/vivid-cec.c @@ -276,12 +276,11 @@ struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev, unsigned int idx, bool is_source) { - char name[sizeof(dev->vid_out_dev.name) + 2]; u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; + char name[32]; - snprintf(name, sizeof(name), "%s%d", - is_source ? dev->vid_out_dev.name : dev->vid_cap_dev.name, - idx); + snprintf(name, sizeof(name), "vivid-%03d-vid-%s%d", + dev->inst, is_source ? "out" : "cap", idx); return cec_allocate_adapter(&vivid_cec_adap_ops, dev, name, caps, 1); } diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index b204c636dba6..c184f9b0be69 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -680,14 +680,44 @@ static const struct media_device_ops vivid_media_ops = { }; #endif +static int vivid_create_queue(struct vivid_dev *dev, + struct vb2_queue *q, + u32 buf_type, + unsigned int min_buffers_needed, + const struct vb2_ops *ops) +{ + if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->multiplanar) + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else if (buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT && dev->multiplanar) + buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + else if (buf_type == V4L2_BUF_TYPE_VBI_CAPTURE && !dev->has_raw_vbi_cap) + buf_type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + else if (buf_type == V4L2_BUF_TYPE_VBI_OUTPUT && !dev->has_raw_vbi_out) + buf_type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + + q->type = buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->io_modes |= V4L2_TYPE_IS_OUTPUT(buf_type) ? VB2_WRITE : VB2_READ; + if (allocators[dev->inst] != 1) + q->io_modes |= VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = ops; + q->mem_ops = allocators[dev->inst] == 1 ? &vb2_dma_contig_memops : + &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = min_buffers_needed; + q->lock = &dev->mutex; + q->dev = dev->v4l2_dev.dev; + q->supports_requests = true; + + return vb2_queue_init(q); +} + static int vivid_create_instance(struct platform_device *pdev, int inst) { static const struct v4l2_dv_timings def_dv_timings = V4L2_DV_BT_CEA_1280X720P60; - static const struct vb2_mem_ops * const vivid_mem_ops[2] = { - &vb2_vmalloc_memops, - &vb2_dma_contig_memops, - }; unsigned in_type_counter[4] = { 0, 0, 0, 0 }; unsigned out_type_counter[4] = { 0, 0, 0, 0 }; int ccs_cap = ccs_cap_mode[inst]; @@ -696,9 +726,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) bool has_modulator; struct vivid_dev *dev; struct video_device *vfd; - struct vb2_queue *q; unsigned node_type = node_types[inst]; - unsigned int allocator = allocators[inst]; v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; int ret; int i; @@ -793,6 +821,25 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap; } + /* do we create a meta capture device */ + dev->has_meta_cap = node_type & 0x20000; + + /* sanity checks */ + if ((in_type_counter[WEBCAM] || in_type_counter[HDMI]) && + !dev->has_vid_cap && !dev->has_meta_cap) { + v4l2_warn(&dev->v4l2_dev, + "Webcam or HDMI input without video or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + if ((in_type_counter[TV] || in_type_counter[SVID]) && + !dev->has_vid_cap && !dev->has_vbi_cap && !dev->has_meta_cap) { + v4l2_warn(&dev->v4l2_dev, + "TV or S-Video input without video, VBI or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + /* do we create a video output device? */ dev->has_vid_out = node_type & 0x0100; @@ -803,6 +850,24 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out; } + /* do we create a metadata output device */ + dev->has_meta_out = node_type & 0x40000; + + /* sanity checks */ + if (out_type_counter[SVID] && + !dev->has_vid_out && !dev->has_vbi_out && !dev->has_meta_out) { + v4l2_warn(&dev->v4l2_dev, + "S-Video output without video, VBI or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + if (out_type_counter[HDMI] && !dev->has_vid_out && !dev->has_meta_out) { + v4l2_warn(&dev->v4l2_dev, + "HDMI output without video or metadata nodes\n"); + kfree(dev); + return -EINVAL; + } + /* do we create a radio receiver device? */ dev->has_radio_rx = node_type & 0x0010; @@ -812,6 +877,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* do we create a software defined radio capture device? */ dev->has_sdr_cap = node_type & 0x0020; + /* do we have a TV tuner? */ + dev->has_tv_tuner = in_type_counter[TV]; + /* do we have a tuner? */ has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) || dev->has_radio_rx || dev->has_sdr_cap; @@ -853,12 +921,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->has_scaler_out ? 'Y' : 'N'); } - /* do we create a meta capture device */ - dev->has_meta_cap = node_type & 0x20000; - - /* do we create a metadata output device */ - dev->has_meta_out = node_type & 0x40000; - /* end detecting feature set */ if (dev->has_vid_cap) { @@ -869,7 +931,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->vid_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; if (dev->has_audio_inputs) dev->vid_cap_caps |= V4L2_CAP_AUDIO; - if (in_type_counter[TV]) + if (dev->has_tv_tuner) dev->vid_cap_caps |= V4L2_CAP_TUNER; } if (dev->has_vid_out) { @@ -890,7 +952,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->vbi_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; if (dev->has_audio_inputs) dev->vbi_cap_caps |= V4L2_CAP_AUDIO; - if (in_type_counter[TV]) + if (dev->has_tv_tuner) dev->vbi_cap_caps |= V4L2_CAP_TUNER; } if (dev->has_vbi_out) { @@ -922,7 +984,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; if (dev->has_audio_inputs) dev->meta_cap_caps |= V4L2_CAP_AUDIO; - if (in_type_counter[TV]) + if (dev->has_tv_tuner) dev->meta_cap_caps |= V4L2_CAP_TUNER; } /* set up the capabilities of meta output device */ @@ -1165,181 +1227,82 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto unreg_dev; } - if (allocator == 1) + if (allocators[inst] == 1) dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - else if (allocator >= ARRAY_SIZE(vivid_mem_ops)) - allocator = 0; /* start creating the vb2 queues */ if (dev->has_vid_cap) { - snprintf(dev->vid_cap_dev.name, sizeof(dev->vid_cap_dev.name), - "vivid-%03d-vid-cap", inst); /* initialize vid_cap queue */ - q = &dev->vb_vid_cap_q; - q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : - V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - if (!allocator) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = &vivid_vid_cap_qops; - q->mem_ops = vivid_mem_ops[allocator]; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - - ret = vb2_queue_init(q); + ret = vivid_create_queue(dev, &dev->vb_vid_cap_q, + V4L2_BUF_TYPE_VIDEO_CAPTURE, 2, + &vivid_vid_cap_qops); if (ret) goto unreg_dev; } if (dev->has_vid_out) { - snprintf(dev->vid_out_dev.name, sizeof(dev->vid_out_dev.name), - "vivid-%03d-vid-out", inst); /* initialize vid_out queue */ - q = &dev->vb_vid_out_q; - q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : - V4L2_BUF_TYPE_VIDEO_OUTPUT; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; - if (!allocator) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = &vivid_vid_out_qops; - q->mem_ops = vivid_mem_ops[allocator]; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - - ret = vb2_queue_init(q); + ret = vivid_create_queue(dev, &dev->vb_vid_out_q, + V4L2_BUF_TYPE_VIDEO_OUTPUT, 2, + &vivid_vid_out_qops); if (ret) goto unreg_dev; } if (dev->has_vbi_cap) { /* initialize vbi_cap queue */ - q = &dev->vb_vbi_cap_q; - q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE : - V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - if (!allocator) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = &vivid_vbi_cap_qops; - q->mem_ops = vivid_mem_ops[allocator]; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - - ret = vb2_queue_init(q); + ret = vivid_create_queue(dev, &dev->vb_vbi_cap_q, + V4L2_BUF_TYPE_VBI_CAPTURE, 2, + &vivid_vbi_cap_qops); if (ret) goto unreg_dev; } if (dev->has_vbi_out) { /* initialize vbi_out queue */ - q = &dev->vb_vbi_out_q; - q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT : - V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; - if (!allocator) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = &vivid_vbi_out_qops; - q->mem_ops = vivid_mem_ops[allocator]; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - - ret = vb2_queue_init(q); + ret = vivid_create_queue(dev, &dev->vb_vbi_out_q, + V4L2_BUF_TYPE_VBI_OUTPUT, 2, + &vivid_vbi_out_qops); if (ret) goto unreg_dev; } if (dev->has_sdr_cap) { /* initialize sdr_cap queue */ - q = &dev->vb_sdr_cap_q; - q->type = V4L2_BUF_TYPE_SDR_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - if (!allocator) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = &vivid_sdr_cap_qops; - q->mem_ops = vivid_mem_ops[allocator]; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 8; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - - ret = vb2_queue_init(q); + ret = vivid_create_queue(dev, &dev->vb_sdr_cap_q, + V4L2_BUF_TYPE_SDR_CAPTURE, 8, + &vivid_sdr_cap_qops); if (ret) goto unreg_dev; } - if (dev->has_fb) { - /* Create framebuffer for testing capture/output overlay */ - ret = vivid_fb_init(dev); - if (ret) - goto unreg_dev; - v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n", - dev->fb_info.node); - } - if (dev->has_meta_cap) { /* initialize meta_cap queue */ - q = &dev->vb_meta_cap_q; - q->type = V4L2_BUF_TYPE_META_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - if (!allocator) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = &vivid_meta_cap_qops; - q->mem_ops = vivid_mem_ops[allocator]; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - ret = vb2_queue_init(q); + ret = vivid_create_queue(dev, &dev->vb_meta_cap_q, + V4L2_BUF_TYPE_META_CAPTURE, 2, + &vivid_meta_cap_qops); if (ret) goto unreg_dev; } if (dev->has_meta_out) { /* initialize meta_out queue */ - q = &dev->vb_meta_out_q; - q->type = V4L2_BUF_TYPE_META_OUTPUT; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; - if (!allocator) - q->io_modes |= VB2_USERPTR; - q->drv_priv = dev; - q->buf_struct_size = sizeof(struct vivid_buffer); - q->ops = &vivid_meta_out_qops; - q->mem_ops = vivid_mem_ops[allocator]; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; - q->lock = &dev->mutex; - q->dev = dev->v4l2_dev.dev; - q->supports_requests = true; - ret = vb2_queue_init(q); + ret = vivid_create_queue(dev, &dev->vb_meta_out_q, + V4L2_BUF_TYPE_META_OUTPUT, 1, + &vivid_meta_out_qops); if (ret) goto unreg_dev; } + if (dev->has_fb) { + /* Create framebuffer for testing capture/output overlay */ + ret = vivid_fb_init(dev); + if (ret) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n", + dev->fb_info.node); + } + #ifdef CONFIG_VIDEO_VIVID_CEC if (dev->has_vid_cap && in_type_counter[HDMI]) { struct cec_adapter *adap; @@ -1386,6 +1349,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* finally start creating the device nodes */ if (dev->has_vid_cap) { vfd = &dev->vid_cap_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-vid-cap", inst); vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; vfd->device_caps = dev->vid_cap_caps; @@ -1431,6 +1396,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (dev->has_vid_out) { vfd = &dev->vid_out_dev; + snprintf(vfd->name, sizeof(vfd->name), + "vivid-%03d-vid-out", inst); vfd->vfl_dir = VFL_DIR_TX; vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index d57066ed31f0..59192b67231c 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -200,6 +200,7 @@ struct vivid_dev { bool has_fb; bool has_meta_cap; bool has_meta_out; + bool has_tv_tuner; bool can_loop_video; -- cgit v1.2.3 From 6dcd5d7a7a29c1e4b8016a06aed78cd650cd8c27 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 3 Nov 2019 23:17:19 +0100 Subject: media: vivid: Fix wrong locking that causes race conditions on streaming stop There is the same incorrect approach to locking implemented in vivid_stop_generating_vid_cap(), vivid_stop_generating_vid_out() and sdr_cap_stop_streaming(). These functions are called during streaming stopping with vivid_dev.mutex locked. And they all do the same mistake while stopping their kthreads, which need to lock this mutex as well. See the example from vivid_stop_generating_vid_cap(): /* shutdown control thread */ vivid_grab_controls(dev, false); mutex_unlock(&dev->mutex); kthread_stop(dev->kthread_vid_cap); dev->kthread_vid_cap = NULL; mutex_lock(&dev->mutex); But when this mutex is unlocked, another vb2_fop_read() can lock it instead of vivid_thread_vid_cap() and manipulate the buffer queue. That causes a use-after-free access later. To fix those issues let's: 1. avoid unlocking the mutex in vivid_stop_generating_vid_cap(), vivid_stop_generating_vid_out() and sdr_cap_stop_streaming(); 2. use mutex_trylock() with schedule_timeout_uninterruptible() in the loops of the vivid kthread handlers. Signed-off-by: Alexander Popov Acked-by: Linus Torvalds Tested-by: Hans Verkuil Signed-off-by: Hans Verkuil Cc: # for v3.18 and up Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-kthread-cap.c | 8 +++++--- drivers/media/platform/vivid/vivid-kthread-out.c | 8 +++++--- drivers/media/platform/vivid/vivid-sdr-cap.c | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index 9f981e8bae6e..01a9d671b947 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -818,7 +818,11 @@ static int vivid_thread_vid_cap(void *data) if (kthread_should_stop()) break; - mutex_lock(&dev->mutex); + if (!mutex_trylock(&dev->mutex)) { + schedule_timeout_uninterruptible(1); + continue; + } + cur_jiffies = jiffies; if (dev->cap_seq_resync) { dev->jiffies_vid_cap = cur_jiffies; @@ -998,8 +1002,6 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) /* shutdown control thread */ vivid_grab_controls(dev, false); - mutex_unlock(&dev->mutex); kthread_stop(dev->kthread_vid_cap); dev->kthread_vid_cap = NULL; - mutex_lock(&dev->mutex); } diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c index c974235d7de3..6780687978f9 100644 --- a/drivers/media/platform/vivid/vivid-kthread-out.c +++ b/drivers/media/platform/vivid/vivid-kthread-out.c @@ -166,7 +166,11 @@ static int vivid_thread_vid_out(void *data) if (kthread_should_stop()) break; - mutex_lock(&dev->mutex); + if (!mutex_trylock(&dev->mutex)) { + schedule_timeout_uninterruptible(1); + continue; + } + cur_jiffies = jiffies; if (dev->out_seq_resync) { dev->jiffies_vid_out = cur_jiffies; @@ -344,8 +348,6 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) /* shutdown control thread */ vivid_grab_controls(dev, false); - mutex_unlock(&dev->mutex); kthread_stop(dev->kthread_vid_out); dev->kthread_vid_out = NULL; - mutex_lock(&dev->mutex); } diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c index 9acc709b0740..2b7522e16efc 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.c +++ b/drivers/media/platform/vivid/vivid-sdr-cap.c @@ -141,7 +141,11 @@ static int vivid_thread_sdr_cap(void *data) if (kthread_should_stop()) break; - mutex_lock(&dev->mutex); + if (!mutex_trylock(&dev->mutex)) { + schedule_timeout_uninterruptible(1); + continue; + } + cur_jiffies = jiffies; if (dev->sdr_cap_seq_resync) { dev->jiffies_sdr_cap = cur_jiffies; @@ -303,10 +307,8 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq) } /* shutdown control thread */ - mutex_unlock(&dev->mutex); kthread_stop(dev->kthread_sdr_cap); dev->kthread_sdr_cap = NULL; - mutex_lock(&dev->mutex); } static void sdr_cap_buf_request_complete(struct vb2_buffer *vb) -- cgit v1.2.3 From 4d741cbd58bf889c8a68cf6e592a7892b5c2802e Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 4 Nov 2019 10:46:32 +0100 Subject: media: exynos4-is: fix wrong mdev and v4l2 dev order in error path When driver is built as module and probe during insmod is deferred because of sensor subdevs, there is NULL pointer deference because mdev is cleaned up and then access it from v4l2_device_unregister(). Fix the wrong mdev and v4l2 dev order in error path of probe. This fixes below null pointer deference: Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = ca026f68 [00000000] *pgd=00000000 Internal error: Oops: 5 [#1] PREEMPT SMP ARM [...] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) PC is at ida_free+0x7c/0x160 LR is at xas_start+0x44/0x204 [...] [] (ida_free) from [] (__media_device_unregister_entity+0x18/0xc0) [] (__media_device_unregister_entity) from [] (media_device_unregister_entity+0x2c/0x38) [] (media_device_unregister_entity) from [] (v4l2_device_release+0xd0/0x104) [] (v4l2_device_release) from [] (device_release+0x28/0x98) [] (device_release) from [] (kobject_put+0xa4/0x208) [] (kct_put) from [] (fimc_capture_subdev_unregistered+0x58/0x6c [s5p_fimc]) [] (fimc_capture_subdev_unregistered [s5p_fimc]) from [] (v4l2_device_unregister_subdev+0x6c/0xa8) [] (v4l2_device_unregister_subdev) from [] (v4l2_device_unregister+0x64/0x94) [] (v4l2_device_unregister) from [] (fimc_md_probe+0x4ec/0xaf8 [s5p_fimc]) [...] Signed-off-by: Seung-Woo Kim Reviewed-by: Sylwester Nawrocki Fixes: 9832e155f1ed ("[media] media-device: split media initialization and registration") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index a838189d4490..9aaf3b8060d5 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1457,12 +1457,12 @@ static int fimc_md_probe(struct platform_device *pdev) ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); - return ret; + goto err_md; } ret = fimc_md_get_clocks(fmd); if (ret) - goto err_md; + goto err_v4l2dev; ret = fimc_md_get_pinctrl(fmd); if (ret < 0) { @@ -1519,9 +1519,10 @@ err_m_ent: fimc_md_unregister_entities(fmd); err_clk: fimc_md_put_clocks(fmd); +err_v4l2dev: + v4l2_device_unregister(&fmd->v4l2_dev); err_md: media_device_cleanup(&fmd->media_dev); - v4l2_device_unregister(&fmd->v4l2_dev); return ret; } -- cgit v1.2.3 From 3b98a21a3202aeb9a47c0b57cab3d3ed420c1e05 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 4 Nov 2019 14:09:18 +0100 Subject: media: v4l2_ctrl: Add p_def to v4l2_ctrl_config This allows setting the default value on compound controls created via v4l2_ctrl_new_custom. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index b4caf2d4d076..73d99c3561ce 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2690,7 +2690,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, type, min, max, is_menu ? cfg->menu_skip_mask : step, def, cfg->dims, cfg->elem_size, - flags, qmenu, qmenu_int, ptr_null, priv); + flags, qmenu, qmenu_int, cfg->p_def, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; -- cgit v1.2.3 From 483b2217dbb05f1326578767c6d7fb5ec2dc1c51 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 4 Nov 2019 14:09:20 +0100 Subject: media: vivid: Add an area control This control represents a generic read/write area. Suggested-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivid/vivid-ctrls.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index b250fc3764e2..68e8124c7973 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -32,6 +32,7 @@ #define VIVID_CID_U32_ARRAY (VIVID_CID_CUSTOM_BASE + 8) #define VIVID_CID_U16_MATRIX (VIVID_CID_CUSTOM_BASE + 9) #define VIVID_CID_U8_4D_ARRAY (VIVID_CID_CUSTOM_BASE + 10) +#define VIVID_CID_AREA (VIVID_CID_CUSTOM_BASE + 11) #define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000) #define VIVID_CID_VIVID_CLASS (0x00f00000 | 1) @@ -266,6 +267,18 @@ static const struct v4l2_ctrl_config vivid_ctrl_disconnect = { .type = V4L2_CTRL_TYPE_BUTTON, }; +static const struct v4l2_area area = { + .width = 1000, + .height = 2000, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_area = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_AREA, + .name = "Area", + .type = V4L2_CTRL_TYPE_AREA, + .p_def.p_const = &area, +}; /* Framebuffer Controls */ @@ -1574,6 +1587,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->string = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_string, NULL); dev->bitmask = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_bitmask, NULL); dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL); + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_area, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_array, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_4d_array, NULL); -- cgit v1.2.3 From 57d024f8db65ca3188d23487ca6980150356b7a3 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Mon, 4 Nov 2019 14:09:23 +0100 Subject: media: v4l2-ctrl: Use p_const when possible After adding a const pointer to ctrl_ptr, lets use it where it make sense. Suggested-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 73d99c3561ce..a565ae3ba7e4 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1556,7 +1556,8 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, if (ctrl->is_int) return ptr1.p_s32[idx] == ptr2.p_s32[idx]; idx *= ctrl->elem_size; - return !memcmp(ptr1.p + idx, ptr2.p + idx, ctrl->elem_size); + return !memcmp(ptr1.p_const + idx, ptr2.p_const + idx, + ctrl->elem_size); } } @@ -1566,8 +1567,8 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; void *p = ptr.p + idx * ctrl->elem_size; - if (ctrl->p_def.p) - memcpy(p, ctrl->p_def.p, ctrl->elem_size); + if (ctrl->p_def.p_const) + memcpy(p, ctrl->p_def.p_const, ctrl->elem_size); else memset(p, 0, ctrl->elem_size); @@ -1954,7 +1955,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, u32 len; if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ptr.p, c->size) ? + return copy_to_user(c->ptr, ptr.p_const, c->size) ? -EFAULT : 0; switch (ctrl->type) { @@ -2069,7 +2070,7 @@ static void ptr_to_ptr(struct v4l2_ctrl *ctrl, { if (ctrl == NULL) return; - memcpy(to.p, from.p, ctrl->elems * ctrl->elem_size); + memcpy(to.p, from.p_const, ctrl->elems * ctrl->elem_size); } /* Copy the new value to the current value. */ @@ -2587,7 +2588,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, is_array) sz_extra += 2 * tot_ctrl_size; - if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p) + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) sz_extra += elem_size; ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); @@ -2634,9 +2635,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->p_cur.p = &ctrl->cur.val; } - if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p) { + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) { ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size; - memcpy(ctrl->p_def.p, p_def.p, elem_size); + memcpy(ctrl->p_def.p, p_def.p_const, elem_size); } for (idx = 0; idx < elems; idx++) { -- cgit v1.2.3 From 3ea35d5db448c27807acbcc7a2306cf65c5e6397 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 5 Nov 2019 18:53:17 +0100 Subject: media: vimc: sen: remove unused kthread_sen field The field kthread_sen in the vimc_sen_device is not set and used. So remove the field and the code that check if it is non NULL Signed-off-by: Dafna Hirschfeld Cc: # for v5.4 and up Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-sensor.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 25ee89a067f7..32380f504591 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -18,7 +18,6 @@ struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; - struct task_struct *kthread_sen; u8 *frame; /* The active format */ struct v4l2_mbus_framefmt mbus_format; @@ -202,10 +201,6 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) const struct vimc_pix_map *vpix; unsigned int frame_size; - if (vsen->kthread_sen) - /* tpg is already executing */ - return 0; - /* Calculate the frame size */ vpix = vimc_pix_map_by_code(vsen->mbus_format.code); frame_size = vsen->mbus_format.width * vpix->bpp * -- cgit v1.2.3 From 1076df3a77b490d33429560a9e0603b3673223e2 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Wed, 6 Nov 2019 08:02:53 +0100 Subject: media: v4l2-mem2mem: Fix hold buf flag checks Hold buf flag is set on output queue, not capture. Fix that. Fixes: f07602ac3887 ("media: v4l2-mem2mem: add new_frame detection") Signed-off-by: Jernej Skrabec Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index db07ef3bf3d0..1afd9c6ad908 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -335,7 +335,7 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, } } - if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags & + if (src && dst && (m2m_ctx->out_q_ctx.q.subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)) m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp || dst->vb2_buf.timestamp != src->vb2_buf.timestamp; @@ -474,7 +474,7 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, * holding capture buffers. Those should use * v4l2_m2m_buf_done_and_job_finish() instead. */ - WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags & + WARN_ON(m2m_ctx->out_q_ctx.q.subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF); spin_lock_irqsave(&m2m_dev->job_spinlock, flags); schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx); -- cgit v1.2.3 From 4adc0423de92cf850d1ef5c0e7cb28fd7a38219e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 6 Nov 2019 10:06:54 +0100 Subject: media: venus: remove invalid compat_ioctl32 handler v4l2_compat_ioctl32() is the function that calls into v4l2_file_operations->compat_ioctl32(), so setting that back to the same function leads to a trivial endless loop, followed by a kernel stack overrun. Remove the incorrect assignment. Cc: stable@vger.kernel.org Fixes: 7472c1c69138 ("[media] media: venus: vdec: add video decoder files") Fixes: aaaa93eda64b ("[media] media: venus: venc: add video encoder files") Signed-off-by: Arnd Bergmann Acked-by: Stanimir Varbanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/vdec.c | 3 --- drivers/media/platform/qcom/venus/venc.c | 3 --- 2 files changed, 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 3bd6d5030598..8feaf5daece9 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1416,9 +1416,6 @@ static const struct v4l2_file_operations vdec_fops = { .unlocked_ioctl = video_ioctl2, .poll = v4l2_m2m_fop_poll, .mmap = v4l2_m2m_fop_mmap, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = v4l2_compat_ioctl32, -#endif }; static int vdec_probe(struct platform_device *pdev) diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 501fb8ca55fb..453edf966d4f 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1239,9 +1239,6 @@ static const struct v4l2_file_operations venc_fops = { .unlocked_ioctl = video_ioctl2, .poll = v4l2_m2m_fop_poll, .mmap = v4l2_m2m_fop_mmap, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = v4l2_compat_ioctl32, -#endif }; static int venc_probe(struct platform_device *pdev) -- cgit v1.2.3 From 7f404ae9cf2a285f73b3c18ab9303d54b7a3d8e1 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Wed, 6 Nov 2019 12:11:14 +0100 Subject: media: pvrusb2: Fix oops on tear-down when radio support is not present In some device configurations there's no radio or radio support in the driver. That's OK, as the driver sets itself up accordingly. However on tear-down in these caes it's still trying to tear down radio related context when there isn't anything there, leading to dereferences through a null pointer and chaos follows. How this bug survived unfixed for 11 years in the pvrusb2 driver is a mystery to me. [hverkuil: fix two checkpatch warnings] Signed-off-by: Mike Isely Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index a34717eba409..eaa08c7999d4 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -898,8 +898,12 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) pvr2_v4l2_dev_disassociate_parent(vp->dev_video); pvr2_v4l2_dev_disassociate_parent(vp->dev_radio); if (!list_empty(&vp->dev_video->devbase.fh_list) || - !list_empty(&vp->dev_radio->devbase.fh_list)) + (vp->dev_radio && + !list_empty(&vp->dev_radio->devbase.fh_list))) { + pvr2_trace(PVR2_TRACE_STRUCT, + "pvr2_v4l2 internal_check exit-empty id=%p", vp); return; + } pvr2_v4l2_destroy_no_lock(vp); } @@ -935,7 +939,8 @@ static int pvr2_v4l2_release(struct file *file) kfree(fhp); if (vp->channel.mc_head->disconnect_flag && list_empty(&vp->dev_video->devbase.fh_list) && - list_empty(&vp->dev_radio->devbase.fh_list)) { + (!vp->dev_radio || + list_empty(&vp->dev_radio->devbase.fh_list))) { pvr2_v4l2_destroy_no_lock(vp); } return 0; -- cgit v1.2.3 From d52741728a518afe536d22dc6e9b60193c5fa942 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 6 Nov 2019 12:38:36 +0100 Subject: media: coda: drop unused irqlock The irqlock spinlock has been unused from the start. Remove it. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 2 -- drivers/media/platform/coda/coda.h | 1 - 2 files changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 287dc1692286..5a38808c8695 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2960,8 +2960,6 @@ static int coda_probe(struct platform_device *pdev) else return -EINVAL; - spin_lock_init(&dev->irqlock); - dev->dev = &pdev->dev; dev->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(dev->clk_per)) { diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 848bf1da401e..9f226140b486 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -86,7 +86,6 @@ struct coda_dev { struct gen_pool *iram_pool; struct coda_aux_buf iram; - spinlock_t irqlock; struct mutex dev_mutex; struct mutex coda_mutex; struct workqueue_struct *workqueue; -- cgit v1.2.3 From 1b976fc6d684e3282914cdbe7a8d68fdce19095c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 30 Jul 2019 09:48:27 +0200 Subject: media: b2c2-flexcop-usb: add sanity checking The driver needs an isochronous endpoint to be present. It will oops in its absence. Add checking for it. Reported-by: syzbot+d93dff37e6a89431c158@syzkaller.appspotmail.com Signed-off-by: Oliver Neukum Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/b2c2/flexcop-usb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index 4bf85e9b78b8..d1331f828108 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -544,6 +544,9 @@ static int flexcop_usb_probe(struct usb_interface *intf, struct flexcop_device *fc = NULL; int ret; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { err("out of memory\n"); return -ENOMEM; -- cgit v1.2.3 From 74a96b51a36de4d86660fbc56b05d86668162d6b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 25 Oct 2019 15:33:39 +0200 Subject: media: flexcop-usb: ensure -EIO is returned on error condition An earlier commit hard coded a return 0 to function flexcop_usb_i2c_req even though the an -EIO was intended to be returned in the case where ret != buflen. Fix this by replacing the return 0 with the return of ret to return the error return code. Addresses-Coverity: ("Unused value") Fixes: b430eaba0be5 ("[media] flexcop-usb: don't use stack for DMA") Signed-off-by: Colin Ian King Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/b2c2/flexcop-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index d1331f828108..039963a7765b 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -295,7 +295,7 @@ static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c, mutex_unlock(&fc_usb->data_mutex); - return 0; + return ret; } /* actual bus specific access functions, -- cgit v1.2.3 From 130ec3dfa5b8f34d29a0b3adf2dfeaebf7627cab Mon Sep 17 00:00:00 2001 From: Jan Pieter van Woerkom Date: Tue, 22 Oct 2019 14:07:37 +0200 Subject: media: dvbsky: remove unused code remove unused code Signed-off-by: Jan Pieter van Woerkom Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvbsky.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 199ba6a8201f..356fd8e66834 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -592,16 +592,7 @@ static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name) static int dvbsky_init(struct dvb_usb_device *d) { struct dvbsky_state *state = d_to_priv(d); - - /* use default interface */ - /* - ret = usb_set_interface(d->udev, 0, 0); - if (ret) - return ret; - */ - state->last_lock = 0; - return 0; } -- cgit v1.2.3 From 4f0fac3b1aa5f356e0625f7d767ec71e1c198a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=A9ron?= Date: Sun, 27 Oct 2019 21:07:37 +0100 Subject: media: rc: add keymap for Beelink GS1 remote control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beelink GS1 Andoid TV Box ships with a simple NEC remote. Signed-off-by: Clément Péron Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-beelink-gs1.c | 84 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-beelink-gs1.c (limited to 'drivers/media') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 4ab4af062abf..63261ef6380a 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-avermedia-rm-ks.o \ rc-avertv-303.o \ rc-azurewave-ad-tu700.o \ + rc-beelink-gs1.o \ rc-behold.o \ rc-behold-columbus.o \ rc-budget-ci-old.o \ diff --git a/drivers/media/rc/keymaps/rc-beelink-gs1.c b/drivers/media/rc/keymaps/rc-beelink-gs1.c new file mode 100644 index 000000000000..cedbd5d20bc7 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-beelink-gs1.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2019 Clément Péron + +#include +#include + +/* + * Keymap for the Beelink GS1 remote control + */ + +static struct rc_map_table beelink_gs1_table[] = { + /* + * TV Keys (Power, Learn and Volume) + * { 0x40400d, KEY_TV }, + * { 0x80f1, KEY_TV }, + * { 0x80f3, KEY_TV }, + * { 0x80f4, KEY_TV }, + */ + + { 0x8051, KEY_POWER }, + { 0x804d, KEY_MUTE }, + { 0x8040, KEY_CONFIG }, + + { 0x8026, KEY_UP }, + { 0x8028, KEY_DOWN }, + { 0x8025, KEY_LEFT }, + { 0x8027, KEY_RIGHT }, + { 0x800d, KEY_OK }, + + { 0x8053, KEY_HOME }, + { 0x80bc, KEY_MEDIA }, + { 0x801b, KEY_BACK }, + { 0x8049, KEY_MENU }, + + { 0x804e, KEY_VOLUMEUP }, + { 0x8056, KEY_VOLUMEDOWN }, + + { 0x8054, KEY_SUBTITLE }, /* Web */ + { 0x8052, KEY_EPG }, /* Media */ + + { 0x8041, KEY_CHANNELUP }, + { 0x8042, KEY_CHANNELDOWN }, + + { 0x8031, KEY_1 }, + { 0x8032, KEY_2 }, + { 0x8033, KEY_3 }, + + { 0x8034, KEY_4 }, + { 0x8035, KEY_5 }, + { 0x8036, KEY_6 }, + + { 0x8037, KEY_7 }, + { 0x8038, KEY_8 }, + { 0x8039, KEY_9 }, + + { 0x8044, KEY_DELETE }, + { 0x8030, KEY_0 }, + { 0x8058, KEY_MODE }, /* # Input Method */ +}; + +static struct rc_map_list beelink_gs1_map = { + .map = { + .scan = beelink_gs1_table, + .size = ARRAY_SIZE(beelink_gs1_table), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_BEELINK_GS1, + } +}; + +static int __init init_rc_map_beelink_gs1(void) +{ + return rc_map_register(&beelink_gs1_map); +} + +static void __exit exit_rc_map_beelink_gs1(void) +{ + rc_map_unregister(&beelink_gs1_map); +} + +module_init(init_rc_map_beelink_gs1) +module_exit(exit_rc_map_beelink_gs1) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clément Péron "); -- cgit v1.2.3 From 40ee7524722836bd5fd40d45f8ee872a41f6ea8e Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Sun, 10 Nov 2019 07:22:17 +0100 Subject: media: v4l2: Use FIELD_SIZEOF directly It's more clear to use FIELD_SIZEOF instead of its implementation. Signed-off-by: zhong jiang Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 315ac12c3e0a..60453b21a855 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2652,7 +2652,7 @@ struct v4l2_ioctl_info { /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ - sizeof(((struct v4l2_struct *)0)->field)) << 16) + FIELD_SIZEOF(struct v4l2_struct, field)) << 16) #define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) #define DEFINE_V4L_STUB_FUNC(_vidioc) \ -- cgit v1.2.3 From c3df30a01da4955e04fa068c503cd784b31dad92 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Sun, 10 Nov 2019 07:23:38 +0100 Subject: media: aspeed-video: Fix memory leaks in aspeed_video_probe In the implementation of aspeed_video_probe() the allocated memory for video should be released if either devm_ioremap_resource() or aspeed_video_init() or aspeed_video_setup_video() fails. Replace kzalloc() with devm_kzalloc to avoid explicit release for video. Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") Signed-off-by: Navid Emamdoost Reviewed-by: Jae Hyun Yoo Reviewed-by: Eddie James Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index db45502774b1..d8593cb2ae84 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1657,7 +1657,8 @@ static int aspeed_video_probe(struct platform_device *pdev) { int rc; struct resource *res; - struct aspeed_video *video = kzalloc(sizeof(*video), GFP_KERNEL); + struct aspeed_video *video = + devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL); if (!video) return -ENOMEM; -- cgit v1.2.3 From c20df61861b702605046a7b61b1da6143c05fc4d Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Sun, 10 Nov 2019 07:25:03 +0100 Subject: media: vimc: upon streaming, check that the pipeline starts with a source entity Userspace can disable links and create pipelines that do not start with a source entity. Trying to stream from such a pipeline should fail with -EPIPE currently this is not handled and cause kernel crash. Reproducing the crash: media-ctl -d0 -l "5:1->21:0[0]" -v v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video2 Panic message: [ 39.078841][ T248] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 39.079338][ T248] #PF: supervisor read access in kernel mode [ 39.079704][ T248] #PF: error_code(0x0000) - not-present page [ 39.080071][ T248] PGD 0 P4D 0 [ 39.080279][ T248] Oops: 0000 [#1] SMP PTI [ 39.080546][ T248] CPU: 0 PID: 248 Comm: vimc-streamer t Not tainted 5.4.0-rc1+ #17 [ 39.081030][ T248] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org 04/01/2014 [ 39.081779][ T248] RIP: 0010:vimc_sca_process_frame+0xdb/0x210 [vimc] [ 39.082191][ T248] Code: 44 8d 0c 28 8b 93 a4 01 00 00 48 8b 8b 98 01 00 00 85 d2 74 40 48 8b 74 24 10 8d 7a ff 4c 01 c9 31 d2 4c 01 fe eb 03 4c 89 c2 <44> 0f b6 04 16 44 88 04 11 4c 8d 42 01 48 39 fa 75 eb 8b 93 a4 01 [ 39.083436][ T248] RSP: 0018:ffffb15a005abe90 EFLAGS: 00010246 [ 39.083808][ T248] RAX: 0000000000000000 RBX: ffffa3fdc46d2e00 RCX: ffffb15a02579000 [ 39.084298][ T248] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000002 [ 39.084792][ T248] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 [ 39.085280][ T248] R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000 [ 39.085770][ T248] R13: ffffa3fdc46d2ee0 R14: 0000000000000000 R15: 0000000000000000 [ 39.086258][ T248] FS: 0000000000000000(0000) GS:ffffa3fdc7800000(0000) knlGS:0000000000000000 [ 39.086806][ T248] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 39.087217][ T248] CR2: 0000000000000000 CR3: 0000000003c92005 CR4: 0000000000360ef0 [ 39.087706][ T248] Call Trace: [ 39.087909][ T248] ? vimc_streamer_pipeline_terminate+0x90/0x90 [vimc] [ 39.088318][ T248] vimc_streamer_thread+0x7c/0xe0 [vimc] [ 39.088663][ T248] kthread+0x10d/0x130 [ 39.088919][ T248] ? kthread_park+0x80/0x80 [ 39.089205][ T248] ret_from_fork+0x35/0x40 [ 39.089475][ T248] Modules linked in: vimc videobuf2_vmalloc videobuf2_memops v4l2_tpg videobuf2_v4l2 videobuf2_common videodev mc [ 39.090208][ T248] CR2: 0000000000000000 [ 39.090463][ T248] ---[ end trace 697650fefbf78bee ]--- [ 39.090796][ T248] RIP: 0010:vimc_sca_process_frame+0xdb/0x210 [vimc] [ 39.091209][ T248] Code: 44 8d 0c 28 8b 93 a4 01 00 00 48 8b 8b 98 01 00 00 85 d2 74 40 48 8b 74 24 10 8d 7a ff 4c 01 c9 31 d2 4c 01 fe eb 03 4c 89 c2 <44> 0f b6 04 16 44 88 04 11 4c 8d 42 01 48 39 fa 75 eb 8b 93 a4 01 [ 39.092417][ T248] RSP: 0018:ffffb15a005abe90 EFLAGS: 00010246 [ 39.092789][ T248] RAX: 0000000000000000 RBX: ffffa3fdc46d2e00 RCX: ffffb15a02579000 [ 39.093278][ T248] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000002 [ 39.093766][ T248] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 [ 39.094254][ T248] R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000 [ 39.094742][ T248] R13: ffffa3fdc46d2ee0 R14: 0000000000000000 R15: 0000000000000000 [ 39.095309][ T248] FS: 0000000000000000(0000) GS:ffffa3fdc7800000(0000) knlGS:0000000000000000 [ 39.095974][ T248] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 39.096372][ T248] CR2: 0000000000000000 CR3: 0000000003c92005 CR4: 0000000000360ef0 Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 10 ++++++++++ drivers/media/platform/vimc/vimc-common.h | 8 ++++++++ drivers/media/platform/vimc/vimc-streamer.c | 13 +++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 43e6fa5886da..16ce9f3b7c75 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -164,6 +164,16 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { }, }; +bool vimc_is_source(struct media_entity *ent) +{ + unsigned int i; + + for (i = 0; i < ent->num_pads; i++) + if (ent->pads[i].flags & MEDIA_PAD_FL_SINK) + return false; + return true; +} + const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i) { if (i >= ARRAY_SIZE(vimc_pix_map_list)) diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index bf729fcde6a9..87eb8259c2a8 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -139,6 +139,14 @@ struct vimc_ent_config { void (*rm)(struct vimc_device *vimc, struct vimc_ent_device *ved); }; +/** + * vimc_is_source - returns true if the entity has only source pads + * + * @ent: pointer to &struct media_entity + * + */ +bool vimc_is_source(struct media_entity *ent); + /* prototypes for vimc_ent_config add and rm hooks */ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, const char *vcfg_name); diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index 1349be188a5b..cd6b55433c9e 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -104,9 +104,18 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream, } entity = vimc_get_source_entity(ved->ent); - /* Check if the end of the pipeline was reached*/ - if (!entity) + /* Check if the end of the pipeline was reached */ + if (!entity) { + /* the first entity of the pipe should be source only */ + if (!vimc_is_source(ved->ent)) { + dev_err(ved->dev, + "first entity in the pipe '%s' is not a source\n", + ved->ent->name); + vimc_streamer_pipeline_terminate(stream); + return -EPIPE; + } return 0; + } /* Get the next device in the pipeline */ if (is_media_entity_v4l2_subdev(entity)) { -- cgit v1.2.3 From 3192b2ca79b3f72fbc0eab9cd95432e3c317ab0d Mon Sep 17 00:00:00 2001 From: Hirokazu Honda Date: Sun, 10 Nov 2019 07:25:34 +0100 Subject: media: mtk-vcodec: Remove extra area allocation in an input buffer on encoding MediaTek encoder allocates non pixel data area for an input buffer every plane. As the input buffer should be read-only, the driver should not write anything in the buffer. Therefore, the extra data should be unnecessary. Signed-off-by: Hirokazu Honda Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index fd8de027e83e..6aad53d97d74 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -332,14 +332,12 @@ static int vidioc_try_fmt(struct v4l2_format *f, pix_fmt_mp->num_planes = fmt->num_planes; pix_fmt_mp->plane_fmt[0].sizeimage = - pix_fmt_mp->width * pix_fmt_mp->height + - ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16); + pix_fmt_mp->width * pix_fmt_mp->height; pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; if (pix_fmt_mp->num_planes == 2) { pix_fmt_mp->plane_fmt[1].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 2 + - (ALIGN(pix_fmt_mp->width, 16) * 16); + (pix_fmt_mp->width * pix_fmt_mp->height) / 2; pix_fmt_mp->plane_fmt[2].sizeimage = 0; pix_fmt_mp->plane_fmt[1].bytesperline = pix_fmt_mp->width; @@ -347,8 +345,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, } else if (pix_fmt_mp->num_planes == 3) { pix_fmt_mp->plane_fmt[1].sizeimage = pix_fmt_mp->plane_fmt[2].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 4 + - ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16); + (pix_fmt_mp->width * pix_fmt_mp->height) / 4; pix_fmt_mp->plane_fmt[1].bytesperline = pix_fmt_mp->plane_fmt[2].bytesperline = pix_fmt_mp->width / 2; -- cgit v1.2.3 From ff1c21f4cffdd490f2d7fd1ce28462e3dca610de Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Sun, 10 Nov 2019 07:26:11 +0100 Subject: media: coda: disable encoder compose selections Disable capture side compose selections for the encoder. This fixes the following v4l2-compliance complaint: fail: v4l2-test-formats.cpp(1662): IS_ENCODER(node) test Composing: FAIL Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 5a38808c8695..af6c59e05138 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -942,7 +942,8 @@ static int coda_g_selection(struct file *file, void *fh, /* fallthrough */ case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + ctx->inst_type == CODA_INST_ENCODER) return -EINVAL; break; default: -- cgit v1.2.3 From ee8951e56c0f960b9621636603a822811cef3158 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 10 Nov 2019 07:27:04 +0100 Subject: media: v4l2-ioctl.c: zero reserved fields for S/TRY_FMT v4l2_vbi_format, v4l2_sliced_vbi_format and v4l2_sdr_format have a reserved array at the end that should be zeroed by drivers as per the V4L2 spec. Older drivers often do not do this, so just handle this in the core. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 60453b21a855..4e700583659b 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1617,12 +1617,12 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_s_fmt_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_vid_out)) @@ -1648,22 +1648,22 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_s_fmt_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_sdr_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_s_fmt_sdr_cap(file, fh, arg); case V4L2_BUF_TYPE_SDR_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_sdr_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_s_fmt_sdr_out(file, fh, arg); case V4L2_BUF_TYPE_META_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_meta_cap)) @@ -1719,12 +1719,12 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_try_fmt_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_vid_out)) @@ -1750,22 +1750,22 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_try_fmt_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_sdr_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_try_fmt_sdr_cap(file, fh, arg); case V4L2_BUF_TYPE_SDR_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_sdr_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_try_fmt_sdr_out(file, fh, arg); case V4L2_BUF_TYPE_META_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_meta_cap)) -- cgit v1.2.3 From 4ffd31463ce303de92724d4e1faf7807f68a4aa5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Sun, 10 Nov 2019 07:27:40 +0100 Subject: media: coda: disable decoder crop selections Disable output side crop selections for the decoder. This fixes the following v4l2-compliance complaint: fail: v4l2-test-formats.cpp(1576): IS_DECODER(node) test Cropping: FAIL Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index af6c59e05138..94fb4d2ecc43 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -933,7 +933,8 @@ static int coda_g_selection(struct file *file, void *fh, rsel = &r; /* fallthrough */ case V4L2_SEL_TGT_CROP: - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + ctx->inst_type == CODA_INST_DECODER) return -EINVAL; break; case V4L2_SEL_TGT_COMPOSE_BOUNDS: -- cgit v1.2.3 From 2df200ab234a86836a8879a05a8007d6b884eb14 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Sun, 10 Nov 2019 07:28:15 +0100 Subject: media: si470x-i2c: add missed operations in remove The driver misses calling v4l2_ctrl_handler_free and v4l2_device_unregister in remove like what is done in probe failure. Add the calls to fix it. Signed-off-by: Chuhong Yuan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-i2c.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 7541698a0be1..f491420d7b53 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -482,6 +482,8 @@ static int si470x_i2c_remove(struct i2c_client *client) if (radio->gpio_reset) gpiod_set_value(radio->gpio_reset, 0); + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); return 0; } -- cgit v1.2.3 From df4a3e7f88e3b0d7ae46d70b9ff8e3c0ea730785 Mon Sep 17 00:00:00 2001 From: Pi-Hsun Shih Date: Sun, 10 Nov 2019 07:29:10 +0100 Subject: media: v4l2-ctrl: Lock main_hdl on operations of requests_queued. There's a race condition between the list_del_init in the v4l2_ctrl_request_complete, and the list_add_tail in the v4l2_ctrl_request_queue, since they can be called in different thread and the requests_queued list is not protected by a lock. This can lead to that the v4l2_ctrl_handler is still in the requests_queued list while the request_is_queued is already set to false, which would cause use-after-free if the v4l2_ctrl_handler is later released. Fix this by locking the ->lock of main_hdl (which is the owner of the requests_queued list) when doing list operations on the ->requests_queued list. Signed-off-by: Pi-Hsun Shih Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index a565ae3ba7e4..2928c5e0a73d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3302,6 +3302,7 @@ static void v4l2_ctrl_request_queue(struct media_request_object *obj) struct v4l2_ctrl_handler *prev_hdl = NULL; struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL; + mutex_lock(main_hdl->lock); if (list_empty(&main_hdl->requests_queued)) goto queue; @@ -3333,18 +3334,22 @@ static void v4l2_ctrl_request_queue(struct media_request_object *obj) queue: list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued); hdl->request_is_queued = true; + mutex_unlock(main_hdl->lock); } static void v4l2_ctrl_request_unbind(struct media_request_object *obj) { struct v4l2_ctrl_handler *hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); + struct v4l2_ctrl_handler *main_hdl = obj->priv; list_del_init(&hdl->requests); + mutex_lock(main_hdl->lock); if (hdl->request_is_queued) { list_del_init(&hdl->requests_queued); hdl->request_is_queued = false; } + mutex_unlock(main_hdl->lock); } static void v4l2_ctrl_request_release(struct media_request_object *obj) @@ -4298,9 +4303,11 @@ void v4l2_ctrl_request_complete(struct media_request *req, v4l2_ctrl_unlock(ctrl); } + mutex_lock(main_hdl->lock); WARN_ON(!hdl->request_is_queued); list_del_init(&hdl->requests_queued); hdl->request_is_queued = false; + mutex_unlock(main_hdl->lock); media_request_object_complete(obj); media_request_object_put(obj); } -- cgit v1.2.3 From 9f22e88a4bba270d3427684cee84dfbf67489e86 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 9 Nov 2019 14:03:08 +0100 Subject: media: vim2m: media_device_cleanup was called too early Running the contrib/test/test-media script in v4l-utils with the vim2m argument will cause this kernel warning: [ 554.430157] ------------[ cut here ]------------ [ 554.433034] DEBUG_LOCKS_WARN_ON(lock->magic != lock) [ 554.433064] WARNING: CPU: 0 PID: 616 at kernel/locking/mutex.c:938 __mutex_lock+0xd7a/0x1380 [ 554.439736] Modules linked in: vim2m v4l2_mem2mem vivid rc_cec videobuf2_dma_contig v4l2_dv_timings cec videobuf2_vmalloc videobuf2_memops v4l2_tpg videobuf2_v4l2 videobuf2_common videodev mc rc_core [last unloaded: vivid] [ 554.445794] CPU: 0 PID: 616 Comm: sleep Not tainted 5.4.0-rc1-virtme #1 [ 554.448481] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org 04/01/2014 [ 554.453088] RIP: 0010:__mutex_lock+0xd7a/0x1380 [ 554.454955] Code: d2 0f 85 de 05 00 00 44 8b 05 82 d9 f7 00 45 85 c0 0f 85 bf f3 ff ff 48 c7 c6 e0 30 a6 b7 48 c7 c7 e0 2e a6 b7 e8 5c 76 36 fe <0f> 0b e9 a5 f3 ff ff 65 48 8b 1c 25 80 ef 01 00 be 08 00 00 00 48 [ 554.462836] RSP: 0018:ffff88803a4cfad0 EFLAGS: 00010282 [ 554.465129] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffffb5a3d24f [ 554.468143] RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffffffffb85273f4 [ 554.471000] RBP: ffff88803a4cfc50 R08: fffffbfff701e681 R09: fffffbfff701e681 [ 554.473990] R10: fffffbfff701e680 R11: ffffffffb80f3403 R12: 0000000000000000 [ 554.476831] R13: dffffc0000000000 R14: ffffffffb9714f00 R15: ffff888053103fc8 [ 554.479622] FS: 00007fac6358a540(0000) GS:ffff88805d000000(0000) knlGS:0000000000000000 [ 554.482673] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 554.484949] CR2: 00007fac6343faf0 CR3: 0000000036c22000 CR4: 00000000003406f0 [ 554.487811] Call Trace: [ 554.488860] ? v4l2_release+0x1b8/0x390 [videodev] [ 554.490818] ? do_exit+0x946/0x2980 [ 554.492269] ? mutex_lock_io_nested+0x1250/0x1250 [ 554.494128] ? __lock_acquire+0xe90/0x3c30 [ 554.495774] ? fsnotify_first_mark+0x120/0x120 [ 554.497487] ? vim2m_device_release+0x50/0x50 [vim2m] [ 554.499469] ? v4l2_release+0x1b8/0x390 [videodev] [ 554.501493] v4l2_release+0x1b8/0x390 [videodev] [ 554.503430] __fput+0x256/0x790 [ 554.504711] task_work_run+0x109/0x190 [ 554.506145] do_exit+0x95e/0x2980 [ 554.507421] ? vfs_lock_file+0x21/0xf0 [ 554.509013] ? find_held_lock+0x33/0x1c0 [ 554.510382] ? __close_fd+0xee/0x190 [ 554.511862] ? release_task.part.21+0x1310/0x1310 [ 554.513701] ? lock_downgrade+0x6d0/0x6d0 [ 554.515299] do_group_exit+0xeb/0x2d0 [ 554.516862] __x64_sys_exit_group+0x35/0x40 [ 554.518610] do_syscall_64+0x90/0x450 [ 554.520142] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 554.522289] RIP: 0033:0x7fac6348ecf6 [ 554.523876] Code: Bad RIP value. [ 554.525294] RSP: 002b:00007ffe6373dc58 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 [ 554.528555] RAX: ffffffffffffffda RBX: 00007fac6357f760 RCX: 00007fac6348ecf6 [ 554.531537] RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000 [ 554.534709] RBP: 0000000000000000 R08: 00000000000000e7 R09: ffffffffffffff80 [ 554.536752] R10: 00007ffe6373db24 R11: 0000000000000246 R12: 00007fac6357f760 [ 554.538643] R13: 0000000000000002 R14: 00007fac63588428 R15: 0000000000000000 [ 554.540634] irq event stamp: 21731 [ 554.541618] hardirqs last enabled at (21731): [] _raw_spin_unlock_irq+0x24/0x30 [ 554.544145] hardirqs last disabled at (21730): [] _raw_spin_lock_irq+0xa/0x40 [ 554.547027] softirqs last enabled at (20148): [] __do_softirq+0x64d/0x906 [ 554.550385] softirqs last disabled at (19857): [] irq_exit+0x175/0x1a0 [ 554.553668] ---[ end trace a389c80c2ca84244 ]--- This is caused by media_device_cleanup() which destroys v4l2_dev->mdev->req_queue_mutex. But v4l2_release() tries to lock that mutex after media_device_cleanup() is called. By moving media_device_cleanup() to the video_device's release function it is guaranteed that the mutex is valid whenever v4l2_release is called. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vim2m.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index e17792f837f8..8d6b09623d88 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1275,6 +1275,9 @@ static void vim2m_device_release(struct video_device *vdev) v4l2_device_unregister(&dev->v4l2_dev); v4l2_m2m_release(dev->m2m_dev); +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_cleanup(&dev->mdev); +#endif kfree(dev); } @@ -1399,7 +1402,6 @@ static int vim2m_remove(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER media_device_unregister(&dev->mdev); v4l2_m2m_unregister_media_controller(dev->m2m_dev); - media_device_cleanup(&dev->mdev); #endif video_unregister_device(&dev->vfd); -- cgit v1.2.3 From 693c5f144aeb9636ae161a3c61a838c50b2ae41c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 9 Nov 2019 15:06:18 +0100 Subject: media: vicodec: media_device_cleanup was called too early Running the contrib/test/test-media script in v4l-utils with the vicodec argument will cause this kernel warning: [ 372.298824] ------------[ cut here ]------------ [ 372.298848] DEBUG_LOCKS_WARN_ON(lock->magic != lock) [ 372.298896] WARNING: CPU: 11 PID: 2220 at kernel/locking/mutex.c:938 __mutex_lock+0x919/0xc10 [ 372.298907] Modules linked in: vicodec v4l2_mem2mem vivid rc_cec v4l2_tpg videobuf2_dma_contig cec rc_core v4l2_dv_timings videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev mc vmw_balloon vmw_vmci button vmwgfx [last unloaded: vimc] [ 372.298961] CPU: 11 PID: 2220 Comm: sleep Not tainted 5.4.0-rc1-test-no #150 [ 372.298970] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/29/2019 [ 372.298983] RIP: 0010:__mutex_lock+0x919/0xc10 [ 372.298995] Code: 59 83 e8 9a fc 16 ff 44 8b 05 23 61 38 01 45 85 c0 0f 85 ef f7 ff ff 48 c7 c6 a0 1f 87 82 48 c7 c7 a0 1e 87 82 e8 cd bb f7 fe <0f> 0b e9 d5 f7 ff ff f6 c3 04 0f 84 3b fd ff ff 49 89 df 41 83 e7 [ 372.299004] RSP: 0018:ffff8881b400fb80 EFLAGS: 00010286 [ 372.299014] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 [ 372.299022] RDX: 0000000000000003 RSI: 0000000000000004 RDI: ffffed1036801f62 [ 372.299030] RBP: ffff8881b400fcf0 R08: ffffffff81217c91 R09: fffffbfff061c271 [ 372.299038] R10: fffffbfff061c270 R11: ffffffff830e1383 R12: ffff88814761dc80 [ 372.299046] R13: 0000000000000000 R14: ffff88814761cbf0 R15: ffff88814761d030 [ 372.299055] FS: 0000000000000000(0000) GS:ffff8881b68c0000(0000) knlGS:0000000000000000 [ 372.299063] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 372.299071] CR2: 00007f606d78aa20 CR3: 0000000003013002 CR4: 00000000001606e0 [ 372.299153] Call Trace: [ 372.299176] ? __kasan_slab_free+0x12f/0x180 [ 372.299187] ? kmem_cache_free+0x9b/0x250 [ 372.299200] ? do_exit+0xcdf/0x1200 [ 372.299210] ? do_group_exit+0x85/0x130 [ 372.299220] ? __x64_sys_exit_group+0x23/0x30 [ 372.299231] ? do_syscall_64+0x5e/0x1c0 [ 372.299241] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 372.299295] ? v4l2_release+0xed/0x190 [videodev] [ 372.299309] ? mutex_lock_io_nested+0xb80/0xb80 [ 372.299323] ? find_held_lock+0x85/0xa0 [ 372.299335] ? fsnotify+0x5b0/0x600 [ 372.299351] ? locks_remove_file+0x78/0x2b0 [ 372.299363] ? __fsnotify_update_child_dentry_flags.part.0+0x170/0x170 [ 372.299383] ? vidioc_querycap+0x50/0x50 [vicodec] [ 372.299426] ? v4l2_release+0xed/0x190 [videodev] [ 372.299467] v4l2_release+0xed/0x190 [videodev] [ 372.299484] __fput+0x15a/0x390 [ 372.299499] task_work_run+0xb2/0xe0 [ 372.299512] do_exit+0x4d0/0x1200 [ 372.299528] ? do_user_addr_fault+0x367/0x610 [ 372.299538] ? release_task+0x990/0x990 [ 372.299552] ? rwsem_spin_on_owner+0x170/0x170 [ 372.299567] ? vmacache_find+0xb2/0x100 [ 372.299580] do_group_exit+0x85/0x130 [ 372.299592] __x64_sys_exit_group+0x23/0x30 [ 372.299602] do_syscall_64+0x5e/0x1c0 [ 372.299614] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 372.299624] RIP: 0033:0x7f606d74a9d6 [ 372.299640] Code: Bad RIP value. [ 372.299648] RSP: 002b:00007fff65364468 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 [ 372.299658] RAX: ffffffffffffffda RBX: 00007f606d83b760 RCX: 00007f606d74a9d6 [ 372.299666] RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000 [ 372.299673] RBP: 0000000000000000 R08: 00000000000000e7 R09: ffffffffffffff80 [ 372.299681] R10: 00007fff65364334 R11: 0000000000000246 R12: 00007f606d83b760 [ 372.299689] R13: 0000000000000002 R14: 00007f606d844428 R15: 0000000000000000 [ 372.299704] ---[ end trace add7d62ca4bc65e3 ]--- This is caused by media_device_cleanup() which destroys v4l2_dev->mdev->req_queue_mutex. But v4l2_release() tries to lock that mutex after media_device_cleanup() is called. By moving media_device_cleanup() to the v4l2_device's release function it is guaranteed that the mutex is valid whenever v4l2_release is called. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vicodec/vicodec-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 0ee143ae0f6b..82350097503e 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -2139,6 +2139,9 @@ static void vicodec_v4l2_dev_release(struct v4l2_device *v4l2_dev) v4l2_m2m_release(dev->stateful_enc.m2m_dev); v4l2_m2m_release(dev->stateful_dec.m2m_dev); v4l2_m2m_release(dev->stateless_dec.m2m_dev); +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_cleanup(&dev->mdev); +#endif kfree(dev); } @@ -2250,7 +2253,6 @@ static int vicodec_remove(struct platform_device *pdev) v4l2_m2m_unregister_media_controller(dev->stateful_enc.m2m_dev); v4l2_m2m_unregister_media_controller(dev->stateful_dec.m2m_dev); v4l2_m2m_unregister_media_controller(dev->stateless_dec.m2m_dev); - media_device_cleanup(&dev->mdev); #endif video_unregister_device(&dev->stateful_enc.vfd); -- cgit v1.2.3 From 01da44446b3b9f82f4f629aa20cad9371a287dd5 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Sat, 31 Aug 2019 17:11:51 +0200 Subject: media: siano: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header file related to Siano Mobile Silicon Digital TV. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46 Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/siano/smsir.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h index b2c54c256e86..ada41d5c4e83 100644 --- a/drivers/media/common/siano/smsir.h +++ b/drivers/media/common/siano/smsir.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* - * SPDX-License-Identifier: GPL-2.0+ * * Siano Mobile Silicon, Inc. * MDTV receiver kernel modules. -- cgit v1.2.3 From 59d8bf5ddfcfdd16e4dcb1a23915d09648a05558 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Wed, 4 Sep 2019 16:58:27 +0200 Subject: media: i2c: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files related to I2C controlled media codec drivers. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46 and some manual changes. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/max2175.h | 4 ++-- drivers/media/i2c/saa711x_regs.h | 2 +- drivers/media/i2c/tda1997x_regs.h | 2 +- drivers/media/i2c/tvp5150_reg.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/max2175.h b/drivers/media/i2c/max2175.h index 1ece587c153d..4c722ea3e5f1 100644 --- a/drivers/media/i2c/max2175.h +++ b/drivers/media/i2c/max2175.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0 - * +/* SPDX-License-Identifier: GPL-2.0 */ +/* * Maxim Integrated MAX2175 RF to Bits tuner driver * * This driver & most of the hard coded values are based on the reference diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h index 44fabe08234d..4b5f6985710b 100644 --- a/drivers/media/i2c/saa711x_regs.h +++ b/drivers/media/i2c/saa711x_regs.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* - * SPDX-License-Identifier: GPL-2.0+ * saa711x - Philips SAA711x video decoder register specifications * * Copyright (c) 2006 Mauro Carvalho Chehab diff --git a/drivers/media/i2c/tda1997x_regs.h b/drivers/media/i2c/tda1997x_regs.h index ecf87534613b..d9b3daada07d 100644 --- a/drivers/media/i2c/tda1997x_regs.h +++ b/drivers/media/i2c/tda1997x_regs.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2018 Gateworks Corporation */ diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h index 9088186c24d1..f716129adf09 100644 --- a/drivers/media/i2c/tvp5150_reg.h +++ b/drivers/media/i2c/tvp5150_reg.h @@ -1,5 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * SPDX-License-Identifier: GPL-2.0 * * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers * -- cgit v1.2.3 From 1dea68495da582da459fcfca60428457acae8063 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 25 Oct 2019 15:46:12 +0200 Subject: media: zr364xx: remove redundant assigmnent to idx, clean up code The variable cable_type is being initialized with a value that is never read and is being re-assigned a little later on. Replace the redundant initializtion with the assignment that occurs a little later. Also initialize frm too rather than have a later assignment. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/zr364xx/zr364xx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index aff78d63b869..57dbcc8083bf 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -555,14 +555,12 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, { unsigned char *pdest; unsigned char *psrc; - s32 idx = -1; - struct zr364xx_framei *frm; + s32 idx = cam->cur_frame; + struct zr364xx_framei *frm = &cam->buffer.frame[idx]; int i = 0; unsigned char *ptr = NULL; _DBG("buffer to user\n"); - idx = cam->cur_frame; - frm = &cam->buffer.frame[idx]; /* swap bytes if camera needs it */ if (cam->method == METHOD0) { -- cgit v1.2.3 From a79b200af75cd6c55079213ad6c9cb93c9a78aa8 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Wed, 6 Nov 2019 21:26:22 +0100 Subject: media: ti-vpe: vpe: fix compatible to match bindings Update the compatible string to match the updated bindings. Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/vpe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index b54f637633a7..65c2c048b018 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2644,7 +2644,7 @@ static int vpe_remove(struct platform_device *pdev) #if defined(CONFIG_OF) static const struct of_device_id vpe_of_match[] = { { - .compatible = "ti,vpe", + .compatible = "ti,dra7-vpe", }, {}, }; -- cgit v1.2.3 From dca6b3733a4a46e63603496f544ece8ace541fde Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 10 Nov 2019 14:30:18 +0100 Subject: media: Revert "media: mtk-vcodec: Remove extra area allocation in an input buffer on encoding" This reverts commit 3192b2ca79b3f72fbc0eab9cd95432e3c317ab0d. There are indications that this patch causes problems on some platforms due to some hardware prefetch. Reverting this patch for now until this is better understood. Reported-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 6aad53d97d74..fd8de027e83e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -332,12 +332,14 @@ static int vidioc_try_fmt(struct v4l2_format *f, pix_fmt_mp->num_planes = fmt->num_planes; pix_fmt_mp->plane_fmt[0].sizeimage = - pix_fmt_mp->width * pix_fmt_mp->height; + pix_fmt_mp->width * pix_fmt_mp->height + + ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16); pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; if (pix_fmt_mp->num_planes == 2) { pix_fmt_mp->plane_fmt[1].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 2; + (pix_fmt_mp->width * pix_fmt_mp->height) / 2 + + (ALIGN(pix_fmt_mp->width, 16) * 16); pix_fmt_mp->plane_fmt[2].sizeimage = 0; pix_fmt_mp->plane_fmt[1].bytesperline = pix_fmt_mp->width; @@ -345,7 +347,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, } else if (pix_fmt_mp->num_planes == 3) { pix_fmt_mp->plane_fmt[1].sizeimage = pix_fmt_mp->plane_fmt[2].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 4; + (pix_fmt_mp->width * pix_fmt_mp->height) / 4 + + ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16); pix_fmt_mp->plane_fmt[1].bytesperline = pix_fmt_mp->plane_fmt[2].bytesperline = pix_fmt_mp->width / 2; -- cgit v1.2.3