From 33bdd5a88a0fb7fbd08947261b243fcec4ff089d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 3 Jun 2013 04:23:48 -0300 Subject: [media] mem2mem: add support for hardware buffered queue On mem2mem decoders with a hardware bitstream ringbuffer, to drain the buffer at the end of the stream, remaining frames might need to be decoded from the bitstream buffer without additional input buffers being provided. To achieve this, allow a queue to be marked as buffered by the driver, and allow scheduling of device_runs when buffered ready queues are empty. This also allows a driver to copy input buffers into their bitstream ringbuffer and immediately mark them as done to be dequeued. The motivation for this patch is hardware assisted h.264 reordering support in the coda driver. For high profile streams, the coda can hold back out-of-order frames, causing a few mem2mem device runs in the beginning, that don't produce any decompressed buffer at the v4l2 capture side. At the same time, the last few frames can be decoded from the bitstream with mem2mem device runs that don't need a new input buffer at the v4l2 output side. The decoder command ioctl can be used to put the decoder into the ringbuffer draining end-of-stream mode. Signed-off-by: Philipp Zabel Acked-by: Sylwester Nawrocki Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index e96497f7c3ed..89b90672088c 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -196,6 +196,10 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) * 2) at least one destination buffer has to be queued, * 3) streaming has to be on. * + * If a queue is buffered (for example a decoder hardware ringbuffer that has + * to be drained before doing streamoff), allow scheduling without v4l2 buffers + * on that queue. + * * There may also be additional, custom requirements. In such case the driver * should supply a custom callback (job_ready in v4l2_m2m_ops) that should * return 1 if the instance is ready. @@ -224,7 +228,8 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) } spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); - if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) { + if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue) + && !m2m_ctx->out_q_ctx.buffered) { spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); @@ -232,7 +237,8 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) return; } spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); - if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { + if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue) + && !m2m_ctx->cap_q_ctx.buffered) { spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, -- cgit v1.2.3 From 3c2ade017a7e10470aa8112f8a4bfdb2ee30f97f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:08:08 -0300 Subject: [media] V4L: Drop bus_type check in v4l2-async match functions These match_* functions are internal callbacks and are always invoked only after checking asd->bus_type. So drop redundant checks in match_i2c() and match_platform() functions. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index aae241730caa..ff87c299f640 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -27,7 +27,6 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client = i2c_verify_client(dev); return client && - asd->bus_type == V4L2_ASYNC_BUS_I2C && asd->match.i2c.adapter_id == client->adapter->nr && asd->match.i2c.address == client->addr; #else @@ -37,8 +36,7 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd) { - return asd->bus_type == V4L2_ASYNC_BUS_PLATFORM && - !strcmp(asd->match.platform.name, dev_name(dev)); + return !strcmp(asd->match.platform.name, dev_name(dev)); } static LIST_HEAD(subdev_list); -- cgit v1.2.3 From cfca7644d7959e1f50cb132306d1557bf6c2da57 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:14:46 -0300 Subject: [media] V4L: Rename v4l2_async_bus_* to v4l2_async_match_* enum v4l2_async_bus_type also selects a method subdevs are matched in the notification handlers, rename it to v4l2_async_match_type so V4L2_ASYNC_MATCH_OF entry can be further added for matching by device tree node pointer. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- .../platform/soc_camera/sh_mobile_ceu_camera.c | 6 ++--- drivers/media/platform/soc_camera/soc_camera.c | 2 +- drivers/media/v4l2-core/v4l2-async.c | 26 +++++++++++----------- include/media/v4l2-async.h | 12 +++++----- 4 files changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index f2de0066089a..dae9716e34b1 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1837,9 +1837,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) for (j = 0; pcdev->pdata->asd_sizes[j]; j++) { for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) { dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n", - __func__, i, (*asd)->bus_type); - if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM && - !strncmp(name, (*asd)->match.platform.name, + __func__, i, (*asd)->match_type); + if ((*asd)->match_type == V4L2_ASYNC_MATCH_DEVNAME && + !strncmp(name, (*asd)->match.device_name.name, sizeof(name) - 1)) { pcdev->csi2_asd = *asd; break; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 2dd0e5272941..8af572bcf03a 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1475,7 +1475,7 @@ static int scan_async_group(struct soc_camera_host *ici, break; } - if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) { + if (i == size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { /* All useless */ dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); return -ENODEV; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ff87c299f640..86934ca73357 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -34,9 +34,9 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) #endif } -static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd) +static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) { - return !strcmp(asd->match.platform.name, dev_name(dev)); + return !strcmp(asd->match.device_name.name, dev_name(dev)); } static LIST_HEAD(subdev_list); @@ -53,17 +53,17 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ - switch (asd->bus_type) { - case V4L2_ASYNC_BUS_CUSTOM: + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_CUSTOM: match = asd->match.custom.match; if (!match) /* Match always */ return asd; break; - case V4L2_ASYNC_BUS_PLATFORM: - match = match_platform; + case V4L2_ASYNC_MATCH_DEVNAME: + match = match_devname; break; - case V4L2_ASYNC_BUS_I2C: + case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; default: @@ -141,15 +141,15 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, for (i = 0; i < notifier->num_subdevs; i++) { asd = notifier->subdev[i]; - switch (asd->bus_type) { - case V4L2_ASYNC_BUS_CUSTOM: - case V4L2_ASYNC_BUS_PLATFORM: - case V4L2_ASYNC_BUS_I2C: + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_CUSTOM: + case V4L2_ASYNC_MATCH_DEVNAME: + case V4L2_ASYNC_MATCH_I2C: break; default: dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, - "Invalid bus-type %u on %p\n", - asd->bus_type, asd); + "Invalid match type %u on %p\n", + asd->match_type, asd); return -EINVAL; } list_add_tail(&asd->list, ¬ifier->waiting); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index c3ec6ac75f7e..33e3b2a30ecb 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -22,10 +22,10 @@ struct v4l2_async_notifier; /* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U -enum v4l2_async_bus_type { - V4L2_ASYNC_BUS_CUSTOM, - V4L2_ASYNC_BUS_PLATFORM, - V4L2_ASYNC_BUS_I2C, +enum v4l2_async_match_type { + V4L2_ASYNC_MATCH_CUSTOM, + V4L2_ASYNC_MATCH_DEVNAME, + V4L2_ASYNC_MATCH_I2C, }; /** @@ -36,11 +36,11 @@ enum v4l2_async_bus_type { * probed, to a notifier->waiting list */ struct v4l2_async_subdev { - enum v4l2_async_bus_type bus_type; + enum v4l2_async_match_type match_type; union { struct { const char *name; - } platform; + } device_name; struct { int adapter_id; unsigned short address; -- cgit v1.2.3 From e7359f8e660882fb2168609638163561eb2f50f0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:21:29 -0300 Subject: [media] V4L: Add V4L2_ASYNC_MATCH_OF subdev matching type Add support for matching by device_node pointer. This allows the notifier user to simply pass a list of device_node pointers corresponding to sub-devices. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 9 +++++++++ include/media/v4l2-async.h | 5 +++++ 2 files changed, 14 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 86934ca73357..9f91013a600d 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -39,6 +39,11 @@ static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) return !strcmp(asd->match.device_name.name, dev_name(dev)); } +static bool match_of(struct device *dev, struct v4l2_async_subdev *asd) +{ + return dev->of_node == asd->match.of.node; +} + static LIST_HEAD(subdev_list); static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); @@ -66,6 +71,9 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; + case V4L2_ASYNC_MATCH_OF: + match = match_of; + break; default: /* Cannot happen, unless someone breaks us */ WARN_ON(true); @@ -145,6 +153,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, case V4L2_ASYNC_MATCH_CUSTOM: case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: + case V4L2_ASYNC_MATCH_OF: break; default: dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 33e3b2a30ecb..82b29813f8be 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -15,6 +15,7 @@ #include struct device; +struct device_node; struct v4l2_device; struct v4l2_subdev; struct v4l2_async_notifier; @@ -26,6 +27,7 @@ enum v4l2_async_match_type { V4L2_ASYNC_MATCH_CUSTOM, V4L2_ASYNC_MATCH_DEVNAME, V4L2_ASYNC_MATCH_I2C, + V4L2_ASYNC_MATCH_OF, }; /** @@ -38,6 +40,9 @@ enum v4l2_async_match_type { struct v4l2_async_subdev { enum v4l2_async_match_type match_type; union { + struct { + const struct device_node *node; + } of; struct { const char *name; } device_name; -- cgit v1.2.3 From e8419d0890efaccb93f9f274d9ba0aae91210e8c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:31:10 -0300 Subject: [media] V4L: Rename subdev field of struct v4l2_async_notifier This is a purely cosmetic change. Since the 'subdev' member points to an array of subdevs make it more explicit by renaming to the plural form. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 2 +- drivers/media/platform/davinci/vpif_display.c | 2 +- drivers/media/platform/soc_camera/soc_camera.c | 2 +- drivers/media/v4l2-core/v4l2-async.c | 2 +- include/media/v4l2-async.h | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index b11d7a74497e..7fbde6d790b5 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -2168,7 +2168,7 @@ static __init int vpif_probe(struct platform_device *pdev) } vpif_probe_complete(); } else { - vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; vpif_obj.notifier.bound = vpif_async_bound; vpif_obj.notifier.complete = vpif_async_complete; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index c2ff06745fdc..6336dfc86482 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1832,7 +1832,7 @@ static __init int vpif_probe(struct platform_device *pdev) } vpif_probe_complete(); } else { - vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; vpif_obj.notifier.bound = vpif_async_bound; vpif_obj.notifier.complete = vpif_async_complete; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 8af572bcf03a..4b42572253e0 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1501,7 +1501,7 @@ static int scan_async_group(struct soc_camera_host *ici, return -ENOMEM; } - sasc->notifier.subdev = asd; + sasc->notifier.subdevs = asd; sasc->notifier.num_subdevs = size; sasc->notifier.bound = soc_camera_async_bound; sasc->notifier.unbind = soc_camera_async_unbind; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 9f91013a600d..ed31a655b9b7 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -147,7 +147,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, INIT_LIST_HEAD(¬ifier->done); for (i = 0; i < notifier->num_subdevs; i++) { - asd = notifier->subdev[i]; + asd = notifier->subdevs[i]; switch (asd->match_type) { case V4L2_ASYNC_MATCH_CUSTOM: diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 82b29813f8be..8fac8eaca51a 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -77,7 +77,7 @@ struct v4l2_async_subdev_list { /** * v4l2_async_notifier - v4l2_device notifier data * @num_subdevs:number of subdevices - * @subdev: array of pointers to subdevice descriptors + * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers * @done: list of struct v4l2_async_subdev_list, already probed @@ -88,7 +88,7 @@ struct v4l2_async_subdev_list { */ struct v4l2_async_notifier { unsigned int num_subdevs; - struct v4l2_async_subdev **subdev; + struct v4l2_async_subdev **subdevs; struct v4l2_device *v4l2_dev; struct list_head waiting; struct list_head done; -- cgit v1.2.3 From b426b3a660c85faf6e1ca1c92c6d43577022aa3b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 22 Jul 2013 08:01:33 -0300 Subject: [media] V4L: Merge struct v4l2_async_subdev_list with struct v4l2_subdev By integrating the v4l2-async API internals a bit more with the core overall the v4l2-async code becomes a bit simpler and easier to follow. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 67 ++++++++++++++++-------------------- include/media/v4l2-async.h | 15 +------- include/media/v4l2-subdev.h | 13 ++++--- 3 files changed, 36 insertions(+), 59 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ed31a655b9b7..b350ab99652c 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -49,12 +49,10 @@ static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev_list *asdl) + struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); struct v4l2_async_subdev *asd; - bool (*match)(struct device *, - struct v4l2_async_subdev *); + bool (*match)(struct device *, struct v4l2_async_subdev *); list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ @@ -89,16 +87,15 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * } static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev_list *asdl, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); int ret; /* Remove from the waiting list */ list_del(&asd->list); - asdl->asd = asd; - asdl->notifier = notifier; + sd->asd = asd; + sd->notifier = notifier; if (notifier->bound) { ret = notifier->bound(notifier, sd, asd); @@ -106,7 +103,7 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, return ret; } /* Move from the global subdevice list to notifier's done */ - list_move(&asdl->list, ¬ifier->done); + list_move(&sd->async_list, ¬ifier->done); ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); if (ret < 0) { @@ -121,21 +118,19 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, return 0; } -static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl) +static void v4l2_async_cleanup(struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); - v4l2_device_unregister_subdev(sd); - /* Subdevice driver will reprobe and put asdl back onto the list */ - list_del_init(&asdl->list); - asdl->asd = NULL; + /* Subdevice driver will reprobe and put the subdev back onto the list */ + list_del_init(&sd->async_list); + sd->asd = NULL; sd->dev = NULL; } int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev_list *asdl, *tmp; + struct v4l2_subdev *sd, *tmp; struct v4l2_async_subdev *asd; int i; @@ -169,14 +164,14 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, /* Keep also completed notifiers on the list */ list_add(¬ifier->list, ¬ifier_list); - list_for_each_entry_safe(asdl, tmp, &subdev_list, list) { + list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { int ret; - asd = v4l2_async_belongs(notifier, asdl); + asd = v4l2_async_belongs(notifier, sd); if (!asd) continue; - ret = v4l2_async_test_notify(notifier, asdl, asd); + ret = v4l2_async_test_notify(notifier, sd, asd); if (ret < 0) { mutex_unlock(&list_lock); return ret; @@ -191,7 +186,7 @@ EXPORT_SYMBOL(v4l2_async_notifier_register); void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev_list *asdl, *tmp; + struct v4l2_subdev *sd, *tmp; unsigned int notif_n_subdev = notifier->num_subdevs; unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS); struct device *dev[n_subdev]; @@ -201,18 +196,16 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) list_del(¬ifier->list); - list_for_each_entry_safe(asdl, tmp, ¬ifier->done, list) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); - + list_for_each_entry_safe(sd, tmp, ¬ifier->done, list) { dev[i] = get_device(sd->dev); - v4l2_async_cleanup(asdl); + v4l2_async_cleanup(sd); /* If we handled USB devices, we'd have to lock the parent too */ device_release_driver(dev[i++]); if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asdl.asd); + notifier->unbind(notifier, sd, sd->asd); } mutex_unlock(&list_lock); @@ -241,24 +234,23 @@ EXPORT_SYMBOL(v4l2_async_notifier_unregister); int v4l2_async_register_subdev(struct v4l2_subdev *sd) { - struct v4l2_async_subdev_list *asdl = &sd->asdl; struct v4l2_async_notifier *notifier; mutex_lock(&list_lock); - INIT_LIST_HEAD(&asdl->list); + INIT_LIST_HEAD(&sd->async_list); list_for_each_entry(notifier, ¬ifier_list, list) { - struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl); + struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd); if (asd) { - int ret = v4l2_async_test_notify(notifier, asdl, asd); + int ret = v4l2_async_test_notify(notifier, sd, asd); mutex_unlock(&list_lock); return ret; } } /* None matched, wait for hot-plugging */ - list_add(&asdl->list, &subdev_list); + list_add(&sd->async_list, &subdev_list); mutex_unlock(&list_lock); @@ -268,23 +260,22 @@ EXPORT_SYMBOL(v4l2_async_register_subdev); void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { - struct v4l2_async_subdev_list *asdl = &sd->asdl; - struct v4l2_async_notifier *notifier = asdl->notifier; + struct v4l2_async_notifier *notifier = sd->notifier; - if (!asdl->asd) { - if (!list_empty(&asdl->list)) - v4l2_async_cleanup(asdl); + if (!sd->asd) { + if (!list_empty(&sd->async_list)) + v4l2_async_cleanup(sd); return; } mutex_lock(&list_lock); - list_add(&asdl->asd->list, ¬ifier->waiting); + list_add(&sd->asd->list, ¬ifier->waiting); - v4l2_async_cleanup(asdl); + v4l2_async_cleanup(sd); if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asdl.asd); + notifier->unbind(notifier, sd, sd->asd); mutex_unlock(&list_lock); } diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8fac8eaca51a..768356917bea 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -61,26 +61,13 @@ struct v4l2_async_subdev { struct list_head list; }; -/** - * v4l2_async_subdev_list - provided by subdevices - * @list: links struct v4l2_async_subdev_list objects to a global list - * before probing, and onto notifier->done after probing - * @asd: pointer to respective struct v4l2_async_subdev - * @notifier: pointer to managing notifier - */ -struct v4l2_async_subdev_list { - struct list_head list; - struct v4l2_async_subdev *asd; - struct v4l2_async_notifier *notifier; -}; - /** * v4l2_async_notifier - v4l2_device notifier data * @num_subdevs:number of subdevices * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers - * @done: list of struct v4l2_async_subdev_list, already probed + * @done: list of struct v4l2_subdev, already probed * @list: member in a global list of notifiers * @bound: a subdevice driver has successfully probed one of subdevices * @complete: all subdevices have been probed successfully diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 3250cc5e7925..bfda0fe9aeb0 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -586,15 +586,14 @@ struct v4l2_subdev { struct video_device *devnode; /* pointer to the physical device, if any */ struct device *dev; - struct v4l2_async_subdev_list asdl; + /* Links this subdev to a global subdev_list or @notifier->done list. */ + struct list_head async_list; + /* Pointer to respective struct v4l2_async_subdev. */ + struct v4l2_async_subdev *asd; + /* Pointer to the managing notifier. */ + struct v4l2_async_notifier *notifier; }; -static inline struct v4l2_subdev *v4l2_async_to_subdev( - struct v4l2_async_subdev_list *asdl) -{ - return container_of(asdl, struct v4l2_subdev, asdl); -} - #define media_entity_to_v4l2_subdev(ent) \ container_of(ent, struct v4l2_subdev, entity) #define vdev_to_v4l2_subdev(vdev) \ -- cgit v1.2.3 From d1e9b7c12b745af715101578a677e4a6b16c09e4 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 9 Jul 2013 01:24:40 -0300 Subject: [media] V4L: Add support for integer menu controls with standard menu items The patch modifies the helper function v4l2_ctrl_new_std_menu to accept integer menu controls with standard menu items. Signed-off-by: Sylwester Nawrocki Signed-off-by: Arun Kumar K Acked-by: Hans Verkuil Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-controls.txt | 21 +++++++++++---------- drivers/media/v4l2-core/v4l2-ctrls.c | 28 +++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 13 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 676f87366025..06cf3ac83631 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -124,26 +124,27 @@ You add non-menu controls by calling v4l2_ctrl_new_std: const struct v4l2_ctrl_ops *ops, u32 id, s32 min, s32 max, u32 step, s32 def); -Menu controls are added by calling v4l2_ctrl_new_std_menu: +Menu and integer menu controls are added by calling v4l2_ctrl_new_std_menu: struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 skip_mask, s32 def); -Or alternatively for integer menu controls, by calling v4l2_ctrl_new_int_menu: +Menu controls with a driver specific menu are added by calling +v4l2_ctrl_new_std_menu_items: + + struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items( + struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, u32 id, s32 max, + s32 skip_mask, s32 def, const char * const *qmenu); + +Integer menu controls with a driver specific menu can be added by calling +v4l2_ctrl_new_int_menu: struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 def, const s64 *qmenu_int); -Standard menu controls with a driver specific menu are added by calling -v4l2_ctrl_new_std_menu_items: - - struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items( - struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, u32 id, s32 max, - s32 skip_mask, s32 def, const char * const *qmenu); - These functions are typically called right after the v4l2_ctrl_handler_init: static const s64 exp_bias_qmenu[] = { diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fccd08b66d1a..e03a2e852143 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -552,6 +552,20 @@ const char * const *v4l2_ctrl_get_menu(u32 id) } EXPORT_SYMBOL(v4l2_ctrl_get_menu); +/* + * Returns NULL or an s64 type array containing the menu for given + * control ID. The total number of the menu items is returned in @len. + */ +const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len) +{ + switch (id) { + default: + *len = 0; + return NULL; + }; +} +EXPORT_SYMBOL(v4l2_ctrl_get_int_menu); + /* Return the control name. */ const char *v4l2_ctrl_get_name(u32 id) { @@ -1712,20 +1726,28 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 mask, s32 def) { - const char * const *qmenu = v4l2_ctrl_get_menu(id); + const char * const *qmenu = NULL; + const s64 *qmenu_int = NULL; const char *name; enum v4l2_ctrl_type type; + unsigned int qmenu_int_len; s32 min; s32 step; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type != V4L2_CTRL_TYPE_MENU) { + + if (type == V4L2_CTRL_TYPE_MENU) + qmenu = v4l2_ctrl_get_menu(id); + else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) + qmenu_int = v4l2_ctrl_get_int_menu(id, &qmenu_int_len); + + if ((!qmenu && !qmenu_int) || (qmenu_int && max > qmenu_int_len)) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL, NULL); + 0, max, mask, def, flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); -- cgit v1.2.3 From bc9028e1d38419f9249cb0d1285e290be7e67223 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:41 -0300 Subject: [media] V4L: Add VP8 encoder controls This patch adds new V4L controls for VP8 encoding. Signed-off-by: Kiran AVND Signed-off-by: Arun Kumar K Acked-by: Hans Verkuil Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 168 ++++++++++++++++++++++++++- drivers/media/v4l2-core/v4l2-ctrls.c | 39 ++++++- include/uapi/linux/v4l2-controls.h | 29 +++++ 3 files changed, 229 insertions(+), 7 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index c2fc9ec1417e..7a3b49b3cc3b 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -722,17 +722,22 @@ for more details.
- MPEG Control Reference + Codec Control Reference - Below all controls within the MPEG control class are + Below all controls within the Codec control class are described. First the generic controls, then controls specific for certain hardware. + Note: These controls are applicable to all codecs and +not just MPEG. The defines are prefixed with V4L2_CID_MPEG/V4L2_MPEG +as the controls were originally made for MPEG codecs and later +extended to cover all encoding formats. +
- Generic MPEG Controls + Generic Codec Controls - MPEG Control IDs + Codec Control IDs @@ -752,7 +757,7 @@ certain hardware. V4L2_CID_MPEG_CLASS  class - The MPEG class + The Codec class descriptor. Calling &VIDIOC-QUERYCTRL; for this control will return a description of this control class. This description can be used as the caption of a Tab page in a GUI, for example. @@ -3009,6 +3014,159 @@ in by the application. 0 = do not insert, 1 = insert packets.
+ +
+ VPX Control Reference + + The VPX controls include controls for encoding parameters + of VPx video codec. + + + VPX Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS + enum v4l2_vp8_num_partitions + + The number of token partitions to use in VP8 encoder. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION + 1 coefficient partition + + + V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS + 2 coefficient partitions + + + V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS + 4 coefficient partitions + + + V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS + 8 coefficient partitions + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 + boolean + + Setting this prevents intra 4x4 mode in the intra mode decision. + + + + + V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES + enum v4l2_vp8_num_ref_frames + + The number of reference pictures for encoding P frames. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME + Last encoded frame will be searched + + + V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME + Two frames will be searched among the last encoded frame, the golden frame +and the alternate reference (altref) frame. The encoder implementation will decide which two are chosen. + + + V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME + The last encoded frame, the golden frame and the altref frame will be searched. + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL + integer + + Indicates the loop filter level. The adjustment of the loop +filter level is done via a delta value against a baseline loop filter value. + + + + + V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS + integer + + This parameter affects the loop filter. Anything above +zero weakens the deblocking effect on the loop filter. + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD + integer + + Sets the refresh period for the golden frame. The period is defined +in number of frames. For a value of 'n', every nth frame starting from the first key frame will be taken as a golden frame. +For eg. for encoding sequence of 0, 1, 2, 3, 4, 5, 6, 7 where the golden frame refresh period is set as 4, the frames +0, 4, 8 etc will be taken as the golden frames as frame 0 is always a key frame. + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL + enum v4l2_vp8_golden_frame_sel + + Selects the golden frame for encoding. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV + Use the (n-2)th frame as a golden frame, current frame index being 'n'. + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD + Use the previous specific frame indicated by +V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame. + + + + + + + + +
+ +
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e03a2e852143..c6dc1fd427d7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -424,6 +424,12 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const vpx_golden_frame_sel[] = { + "Use Previous Frame", + "Use Previous Specific Frame", + NULL, + }; + static const char * const flash_led_mode[] = { "Off", "Flash", @@ -538,6 +544,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_mpeg4_level; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return mpeg4_profile; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + return vpx_golden_frame_sel; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return jpeg_chroma_subsampling; case V4L2_CID_DV_TX_MODE: @@ -552,13 +560,26 @@ const char * const *v4l2_ctrl_get_menu(u32 id) } EXPORT_SYMBOL(v4l2_ctrl_get_menu); +#define __v4l2_qmenu_int_len(arr, len) ({ *(len) = ARRAY_SIZE(arr); arr; }) /* * Returns NULL or an s64 type array containing the menu for given * control ID. The total number of the menu items is returned in @len. */ const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len) { + static const s64 const qmenu_int_vpx_num_partitions[] = { + 1, 2, 4, 8, + }; + + static const s64 const qmenu_int_vpx_num_ref_frames[] = { + 1, 2, 3, + }; + switch (id) { + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: + return __v4l2_qmenu_int_len(qmenu_int_vpx_num_partitions, len); + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: + return __v4l2_qmenu_int_len(qmenu_int_vpx_num_ref_frames, len); default: *len = 0; return NULL; @@ -614,9 +635,11 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr"; - /* MPEG controls */ + /* Codec controls */ + /* The MPEG controls are applicable to all codec controls + * and the 'MPEG' part of the define is historical */ /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; + case V4L2_CID_MPEG_CLASS: return "Codec Controls"; case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; @@ -714,6 +737,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; + /* VPX controls */ + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: return "VPX Number of Partitions"; + case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4: return "VPX Intra Mode Decision Disable"; + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: return "VPX No. of Refs for P Frame"; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL: return "VPX Loop Filter Level Range"; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS: return "VPX Deblocking Effect Control"; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD: return "VPX Golden Frame Refresh Period"; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: return "VPX Golden Frame Indicator"; + /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; @@ -928,6 +960,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_RX_RGB_RANGE: case V4L2_CID_TEST_PATTERN: case V4L2_CID_TUNE_DEEMPHASIS: + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -939,6 +972,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_ISO_SENSITIVITY: case V4L2_CID_AUTO_EXPOSURE_BIAS: + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: *type = V4L2_CTRL_TYPE_INTEGER_MENU; break; case V4L2_CID_USER_CLASS: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index e90a88a8708f..083bb5a5aae2 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -161,6 +161,8 @@ enum v4l2_colorfx { #define V4L2_CID_USER_SI476X_BASE (V4L2_CID_USER_BASE + 0x1040) /* MPEG-class control IDs */ +/* The MPEG controls are applicable to all codec controls + * and the 'MPEG' part of the define is historical */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) #define V4L2_CID_MPEG_CLASS (V4L2_CTRL_CLASS_MPEG | 1) @@ -522,6 +524,33 @@ enum v4l2_mpeg_video_mpeg4_profile { }; #define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_MPEG_BASE+407) +/* Control IDs for VP8 streams + * Although VP8 is not part of MPEG we add these controls to the MPEG class + * as that class is already handling other video compression standards + */ +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS (V4L2_CID_MPEG_BASE+500) +enum v4l2_vp8_num_partitions { + V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION = 0, + V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS = 1, + V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS = 2, + V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS = 3, +}; +#define V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (V4L2_CID_MPEG_BASE+501) +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES (V4L2_CID_MPEG_BASE+502) +enum v4l2_vp8_num_ref_frames { + V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME = 0, + V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME = 1, + V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME = 2, +}; +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (V4L2_CID_MPEG_BASE+503) +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (V4L2_CID_MPEG_BASE+504) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (V4L2_CID_MPEG_BASE+505) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL (V4L2_CID_MPEG_BASE+506) +enum v4l2_vp8_golden_frame_sel { + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV = 0, + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD = 1, +}; + /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) -- cgit v1.2.3 From 8a90f1a61c9b31bd0c89b8456fa4dce91ad69c57 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 2 Aug 2013 13:55:21 -0300 Subject: [media] media: vb2: Take queue or device lock in mmap-related vb2 ioctl handlers The vb2_fop_mmap() and vb2_fop_get_unmapped_area() functions are plug-in implementation of the mmap() and get_unmapped_area() file operations that calls vb2_mmap() and vb2_get_unmapped_area() on the queue associated with the video device. Neither the vb2_fop_mmap/vb2_fop_get_unmapped_area nor the v4l2_mmap/vb2_get_unmapped_area functions in the V4L2 core take any lock, leading to race conditions between mmap/get_unmapped_area and other buffer-related ioctls such as VIDIOC_REQBUFS. Fix it by taking the queue or device lock around the vb2_mmap() and vb2_get_unmapped_area() calls. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9fc4bab2da97..c9b50c7665de 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2578,8 +2578,15 @@ EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf); int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + int err; - return vb2_mmap(vdev->queue, vma); + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + err = vb2_mmap(vdev->queue, vma); + if (lock) + mutex_unlock(lock); + return err; } EXPORT_SYMBOL_GPL(vb2_fop_mmap); @@ -2685,8 +2692,15 @@ unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + int ret; - return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + ret = vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); + if (lock) + mutex_unlock(lock); + return ret; } EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); #endif -- cgit v1.2.3 From b18787ed1ce32eb0c2ce2323220abd4ed93c4b97 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:55 -0300 Subject: [media] v4l2-dv-timings: add new helper module This module makes it easy to filter valid timings from the full list of CEA and DMT timings based on the timings capabilities. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/Makefile | 1 + drivers/media/v4l2-core/v4l2-dv-timings.c | 192 ++++++++++++++++++++++++++++++ include/media/v4l2-dv-timings.h | 67 +++++++++++ 3 files changed, 260 insertions(+) create mode 100644 drivers/media/v4l2-core/v4l2-dv-timings.c create mode 100644 include/media/v4l2-dv-timings.h (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 4c33b8d6520c..1a85eee581f8 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -17,6 +17,7 @@ endif obj-$(CONFIG_VIDEO_V4L2) += videodev.o obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o +obj-$(CONFIG_VIDEO_V4L2) += v4l2-dv-timings.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c new file mode 100644 index 000000000000..58279467a7a5 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -0,0 +1,192 @@ +/* + * v4l2-dv-timings - dv-timings helper functions + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static const struct v4l2_dv_timings timings[] = { + V4L2_DV_BT_CEA_640X480P59_94, + V4L2_DV_BT_CEA_720X480I59_94, + V4L2_DV_BT_CEA_720X480P59_94, + V4L2_DV_BT_CEA_720X576I50, + V4L2_DV_BT_CEA_720X576P50, + V4L2_DV_BT_CEA_1280X720P24, + V4L2_DV_BT_CEA_1280X720P25, + V4L2_DV_BT_CEA_1280X720P30, + V4L2_DV_BT_CEA_1280X720P50, + V4L2_DV_BT_CEA_1280X720P60, + V4L2_DV_BT_CEA_1920X1080P24, + V4L2_DV_BT_CEA_1920X1080P25, + V4L2_DV_BT_CEA_1920X1080P30, + V4L2_DV_BT_CEA_1920X1080I50, + V4L2_DV_BT_CEA_1920X1080P50, + V4L2_DV_BT_CEA_1920X1080I60, + V4L2_DV_BT_CEA_1920X1080P60, + V4L2_DV_BT_DMT_640X350P85, + V4L2_DV_BT_DMT_640X400P85, + V4L2_DV_BT_DMT_720X400P85, + V4L2_DV_BT_DMT_640X480P72, + V4L2_DV_BT_DMT_640X480P75, + V4L2_DV_BT_DMT_640X480P85, + V4L2_DV_BT_DMT_800X600P56, + V4L2_DV_BT_DMT_800X600P60, + V4L2_DV_BT_DMT_800X600P72, + V4L2_DV_BT_DMT_800X600P75, + V4L2_DV_BT_DMT_800X600P85, + V4L2_DV_BT_DMT_800X600P120_RB, + V4L2_DV_BT_DMT_848X480P60, + V4L2_DV_BT_DMT_1024X768I43, + V4L2_DV_BT_DMT_1024X768P60, + V4L2_DV_BT_DMT_1024X768P70, + V4L2_DV_BT_DMT_1024X768P75, + V4L2_DV_BT_DMT_1024X768P85, + V4L2_DV_BT_DMT_1024X768P120_RB, + V4L2_DV_BT_DMT_1152X864P75, + V4L2_DV_BT_DMT_1280X768P60_RB, + V4L2_DV_BT_DMT_1280X768P60, + V4L2_DV_BT_DMT_1280X768P75, + V4L2_DV_BT_DMT_1280X768P85, + V4L2_DV_BT_DMT_1280X768P120_RB, + V4L2_DV_BT_DMT_1280X800P60_RB, + V4L2_DV_BT_DMT_1280X800P60, + V4L2_DV_BT_DMT_1280X800P75, + V4L2_DV_BT_DMT_1280X800P85, + V4L2_DV_BT_DMT_1280X800P120_RB, + V4L2_DV_BT_DMT_1280X960P60, + V4L2_DV_BT_DMT_1280X960P85, + V4L2_DV_BT_DMT_1280X960P120_RB, + V4L2_DV_BT_DMT_1280X1024P60, + V4L2_DV_BT_DMT_1280X1024P75, + V4L2_DV_BT_DMT_1280X1024P85, + V4L2_DV_BT_DMT_1280X1024P120_RB, + V4L2_DV_BT_DMT_1360X768P60, + V4L2_DV_BT_DMT_1360X768P120_RB, + V4L2_DV_BT_DMT_1366X768P60, + V4L2_DV_BT_DMT_1366X768P60_RB, + V4L2_DV_BT_DMT_1400X1050P60_RB, + V4L2_DV_BT_DMT_1400X1050P60, + V4L2_DV_BT_DMT_1400X1050P75, + V4L2_DV_BT_DMT_1400X1050P85, + V4L2_DV_BT_DMT_1400X1050P120_RB, + V4L2_DV_BT_DMT_1440X900P60_RB, + V4L2_DV_BT_DMT_1440X900P60, + V4L2_DV_BT_DMT_1440X900P75, + V4L2_DV_BT_DMT_1440X900P85, + V4L2_DV_BT_DMT_1440X900P120_RB, + V4L2_DV_BT_DMT_1600X900P60_RB, + V4L2_DV_BT_DMT_1600X1200P60, + V4L2_DV_BT_DMT_1600X1200P65, + V4L2_DV_BT_DMT_1600X1200P70, + V4L2_DV_BT_DMT_1600X1200P75, + V4L2_DV_BT_DMT_1600X1200P85, + V4L2_DV_BT_DMT_1600X1200P120_RB, + V4L2_DV_BT_DMT_1680X1050P60_RB, + V4L2_DV_BT_DMT_1680X1050P60, + V4L2_DV_BT_DMT_1680X1050P75, + V4L2_DV_BT_DMT_1680X1050P85, + V4L2_DV_BT_DMT_1680X1050P120_RB, + V4L2_DV_BT_DMT_1792X1344P60, + V4L2_DV_BT_DMT_1792X1344P75, + V4L2_DV_BT_DMT_1792X1344P120_RB, + V4L2_DV_BT_DMT_1856X1392P60, + V4L2_DV_BT_DMT_1856X1392P75, + V4L2_DV_BT_DMT_1856X1392P120_RB, + V4L2_DV_BT_DMT_1920X1200P60_RB, + V4L2_DV_BT_DMT_1920X1200P60, + V4L2_DV_BT_DMT_1920X1200P75, + V4L2_DV_BT_DMT_1920X1200P85, + V4L2_DV_BT_DMT_1920X1200P120_RB, + V4L2_DV_BT_DMT_1920X1440P60, + V4L2_DV_BT_DMT_1920X1440P75, + V4L2_DV_BT_DMT_1920X1440P120_RB, + V4L2_DV_BT_DMT_2048X1152P60_RB, + V4L2_DV_BT_DMT_2560X1600P60_RB, + V4L2_DV_BT_DMT_2560X1600P60, + V4L2_DV_BT_DMT_2560X1600P75, + V4L2_DV_BT_DMT_2560X1600P85, + V4L2_DV_BT_DMT_2560X1600P120_RB, +}; + +bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *dvcap) +{ + const struct v4l2_bt_timings *bt = &t->bt; + const struct v4l2_bt_timings_cap *cap = &dvcap->bt; + u32 caps = cap->capabilities; + + if (t->type != V4L2_DV_BT_656_1120) + return false; + if (t->type != dvcap->type || + bt->height < cap->min_height || + bt->height > cap->max_height || + bt->width < cap->min_width || + bt->width > cap->max_width || + bt->pixelclock < cap->min_pixelclock || + bt->pixelclock > cap->max_pixelclock || + (cap->standards && !(bt->standards & cap->standards)) || + (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) || + (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE))) + return false; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_dv_valid_timings); + +int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, + const struct v4l2_dv_timings_cap *cap) +{ + u32 i, idx; + + memset(t->reserved, 0, sizeof(t->reserved)); + for (i = idx = 0; i < ARRAY_SIZE(timings); i++) { + if (v4l2_dv_valid_timings(timings + i, cap) && + idx++ == t->index) { + t->timings = timings[i]; + return 0; + } + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_enum_dv_timings_cap); + +bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap, + unsigned pclock_delta) +{ + int i; + + if (!v4l2_dv_valid_timings(t, cap)) + return false; + + for (i = 0; i < ARRAY_SIZE(timings); i++) { + if (v4l2_dv_valid_timings(timings + i, cap) && + v4l_match_dv_timings(t, timings + i, pclock_delta)) { + *t = timings[i]; + return true; + } + } + return false; +} +EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h new file mode 100644 index 000000000000..41075fa02a96 --- /dev/null +++ b/include/media/v4l2-dv-timings.h @@ -0,0 +1,67 @@ +/* + * v4l2-dv-timings - Internal header with dv-timings helper functions + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * 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 __V4L2_DV_TIMINGS_H +#define __V4L2_DV_TIMINGS_H + +#include + +/** v4l2_dv_valid_timings() - are these timings valid? + * @t: the v4l2_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * + * Returns true if the given dv_timings struct is supported by the + * hardware capabilities, returns false otherwise. + */ +bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap); + +/** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities + * @t: the v4l2_enum_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * + * This enumerates dv_timings using the full list of possible CEA-861 and DMT + * timings, filtering out any timings that are not supported based on the + * hardware capabilities. + * + * If a valid timing for the given index is found, it will fill in @t and + * return 0, otherwise it returns -EINVAL. + */ +int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, + const struct v4l2_dv_timings_cap *cap); + +/** v4l2_find_dv_timings_cap() - Find the closest timings struct + * @t: the v4l2_enum_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * @pclock_delta: maximum delta between t->pixelclock and the timing struct + * under consideration. + * + * This function tries to map the given timings to an entry in the + * full list of possible CEA-861 and DMT timings, filtering out any timings + * that are not supported based on the hardware capabilities. + * + * On success it will fill in @t with the found timings and it returns true. + * On failure it will return false. + */ +bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap, + unsigned pclock_delta); + +#endif -- cgit v1.2.3 From 2576415846bcbad3c0a6885fc44f950837106364 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:56 -0300 Subject: [media] v4l2: move dv-timings related code to v4l2-dv-timings.c v4l2-common.c contained a bunch of dv-timings related functions. Move that to the new v4l2-dv-timings.c which is a more appropriate place for them. There aren't many drivers that do HDTV, so it is a good idea to separate common code related to that into a module of its own. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 1 + drivers/media/i2c/adv7604.c | 1 + drivers/media/i2c/ths8200.c | 1 + drivers/media/usb/hdpvr/hdpvr-video.c | 1 + drivers/media/v4l2-core/v4l2-common.c | 357 ----------------------------- drivers/media/v4l2-core/v4l2-dv-timings.c | 358 +++++++++++++++++++++++++++++- include/media/v4l2-common.h | 13 -- include/media/v4l2-dv-timings.h | 59 +++++ 8 files changed, 420 insertions(+), 371 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index ba4364dfae66..2fa8d7286cea 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 1d675b58fd71..181a6c359335 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -38,6 +38,7 @@ #include #include #include +#include #include static int debug; diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 8a29810d155a..aef7c0e7cd67 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -21,6 +21,7 @@ #include #include +#include #include #include diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 4f8567aa99d8..9c67b6e127e3 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "hdpvr.h" diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index a95e5e23403f..037d7a55aa8c 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -495,363 +495,6 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, } EXPORT_SYMBOL_GPL(v4l_bound_align_image); -/** - * v4l_match_dv_timings - check if two timings match - * @t1 - compare this v4l2_dv_timings struct... - * @t2 - with this struct. - * @pclock_delta - the allowed pixelclock deviation. - * - * Compare t1 with t2 with a given margin of error for the pixelclock. - */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta) -{ - if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) - return false; - if (t1->bt.width == t2->bt.width && - t1->bt.height == t2->bt.height && - t1->bt.interlaced == t2->bt.interlaced && - t1->bt.polarities == t2->bt.polarities && - t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta && - t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta && - t1->bt.hfrontporch == t2->bt.hfrontporch && - t1->bt.vfrontporch == t2->bt.vfrontporch && - t1->bt.vsync == t2->bt.vsync && - t1->bt.vbackporch == t2->bt.vbackporch && - (!t1->bt.interlaced || - (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch && - t1->bt.il_vsync == t2->bt.il_vsync && - t1->bt.il_vbackporch == t2->bt.il_vbackporch))) - return true; - return false; -} -EXPORT_SYMBOL_GPL(v4l_match_dv_timings); - -/* - * CVT defines - * Based on Coordinated Video Timings Standard - * version 1.1 September 10, 2003 - */ - -#define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ - -/* Normal blanking */ -#define CVT_MIN_V_BPORCH 7 /* lines */ -#define CVT_MIN_V_PORCH_RND 3 /* lines */ -#define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ - -/* Normal blanking for CVT uses GTF to calculate horizontal blanking */ -#define CVT_CELL_GRAN 8 /* character cell granularity */ -#define CVT_M 600 /* blanking formula gradient */ -#define CVT_C 40 /* blanking formula offset */ -#define CVT_K 128 /* blanking formula scaling factor */ -#define CVT_J 20 /* blanking formula scaling factor */ -#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J) -#define CVT_M_PRIME (CVT_K * CVT_M / 256) - -/* Reduced Blanking */ -#define CVT_RB_MIN_V_BPORCH 7 /* lines */ -#define CVT_RB_V_FPORCH 3 /* lines */ -#define CVT_RB_MIN_V_BLANK 460 /* us */ -#define CVT_RB_H_SYNC 32 /* pixels */ -#define CVT_RB_H_BPORCH 80 /* pixels */ -#define CVT_RB_H_BLANK 160 /* pixels */ - -/** v4l2_detect_cvt - detect if the given timings follow the CVT standard - * @frame_height - the total height of the frame (including blanking) in lines. - * @hfreq - the horizontal frequency in Hz. - * @vsync - the height of the vertical sync in lines. - * @polarities - the horizontal and vertical polarities (same as struct - * v4l2_bt_timings polarities). - * @fmt - the resulting timings. - * - * This function will attempt to detect if the given values correspond to a - * valid CVT format. If so, then it will return true, and fmt will be filled - * in with the found CVT timings. - */ -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_dv_timings *fmt) -{ - int v_fp, v_bp, h_fp, h_bp, hsync; - int frame_width, image_height, image_width; - bool reduced_blanking; - unsigned pix_clk; - - if (vsync < 4 || vsync > 7) - return false; - - if (polarities == V4L2_DV_VSYNC_POS_POL) - reduced_blanking = false; - else if (polarities == V4L2_DV_HSYNC_POS_POL) - reduced_blanking = true; - else - return false; - - /* Vertical */ - if (reduced_blanking) { - v_fp = CVT_RB_V_FPORCH; - v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; - v_bp -= vsync + v_fp; - - if (v_bp < CVT_RB_MIN_V_BPORCH) - v_bp = CVT_RB_MIN_V_BPORCH; - } else { - v_fp = CVT_MIN_V_PORCH_RND; - v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; - - if (v_bp < CVT_MIN_V_BPORCH) - v_bp = CVT_MIN_V_BPORCH; - } - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; - - /* Aspect ratio based on vsync */ - switch (vsync) { - case 4: - image_width = (image_height * 4) / 3; - break; - case 5: - image_width = (image_height * 16) / 9; - break; - case 6: - image_width = (image_height * 16) / 10; - break; - case 7: - /* special case */ - if (image_height == 1024) - image_width = (image_height * 5) / 4; - else if (image_height == 768) - image_width = (image_height * 15) / 9; - else - return false; - break; - default: - return false; - } - - image_width = image_width & ~7; - - /* Horizontal */ - if (reduced_blanking) { - pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; - - h_bp = CVT_RB_H_BPORCH; - hsync = CVT_RB_H_SYNC; - h_fp = CVT_RB_H_BLANK - h_bp - hsync; - - frame_width = image_width + CVT_RB_H_BLANK; - } else { - int h_blank; - unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; - - h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / - (100 - ideal_duty_cycle); - h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); - - if (h_blank * 100 / image_width < 20) { - h_blank = image_width / 5; - h_blank = (h_blank + 0x7) & ~0x7; - } - - pix_clk = (image_width + h_blank) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; - - h_bp = h_blank / 2; - frame_width = image_width + h_blank; - - hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % CVT_CELL_GRAN; - h_fp = h_blank - hsync - h_bp; - } - - fmt->bt.polarities = polarities; - fmt->bt.width = image_width; - fmt->bt.height = image_height; - fmt->bt.hfrontporch = h_fp; - fmt->bt.vfrontporch = v_fp; - fmt->bt.hsync = hsync; - fmt->bt.vsync = vsync; - fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; - fmt->bt.pixelclock = pix_clk; - fmt->bt.standards = V4L2_DV_BT_STD_CVT; - if (reduced_blanking) - fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; - return true; -} -EXPORT_SYMBOL_GPL(v4l2_detect_cvt); - -/* - * GTF defines - * Based on Generalized Timing Formula Standard - * Version 1.1 September 2, 1999 - */ - -#define GTF_PXL_CLK_GRAN 250000 /* pixel clock granularity */ - -#define GTF_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ -#define GTF_V_FP 1 /* vertical front porch (lines) */ -#define GTF_CELL_GRAN 8 /* character cell granularity */ - -/* Default */ -#define GTF_D_M 600 /* blanking formula gradient */ -#define GTF_D_C 40 /* blanking formula offset */ -#define GTF_D_K 128 /* blanking formula scaling factor */ -#define GTF_D_J 20 /* blanking formula scaling factor */ -#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J) -#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256) - -/* Secondary */ -#define GTF_S_M 3600 /* blanking formula gradient */ -#define GTF_S_C 40 /* blanking formula offset */ -#define GTF_S_K 128 /* blanking formula scaling factor */ -#define GTF_S_J 35 /* blanking formula scaling factor */ -#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J) -#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256) - -/** v4l2_detect_gtf - detect if the given timings follow the GTF standard - * @frame_height - the total height of the frame (including blanking) in lines. - * @hfreq - the horizontal frequency in Hz. - * @vsync - the height of the vertical sync in lines. - * @polarities - the horizontal and vertical polarities (same as struct - * v4l2_bt_timings polarities). - * @aspect - preferred aspect ratio. GTF has no method of determining the - * aspect ratio in order to derive the image width from the - * image height, so it has to be passed explicitly. Usually - * the native screen aspect ratio is used for this. If it - * is not filled in correctly, then 16:9 will be assumed. - * @fmt - the resulting timings. - * - * This function will attempt to detect if the given values correspond to a - * valid GTF format. If so, then it will return true, and fmt will be filled - * in with the found GTF timings. - */ -bool v4l2_detect_gtf(unsigned frame_height, - unsigned hfreq, - unsigned vsync, - u32 polarities, - struct v4l2_fract aspect, - struct v4l2_dv_timings *fmt) -{ - int pix_clk; - int v_fp, v_bp, h_fp, hsync; - int frame_width, image_height, image_width; - bool default_gtf; - int h_blank; - - if (vsync != 3) - return false; - - if (polarities == V4L2_DV_VSYNC_POS_POL) - default_gtf = true; - else if (polarities == V4L2_DV_HSYNC_POS_POL) - default_gtf = false; - else - return false; - - /* Vertical */ - v_fp = GTF_V_FP; - v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; - - if (aspect.numerator == 0 || aspect.denominator == 0) { - aspect.numerator = 16; - aspect.denominator = 9; - } - image_width = ((image_height * aspect.numerator) / aspect.denominator); - - /* Horizontal */ - if (default_gtf) - h_blank = ((image_width * GTF_D_C_PRIME * hfreq) - - (image_width * GTF_D_M_PRIME * 1000) + - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000); - else - h_blank = ((image_width * GTF_S_C_PRIME * hfreq) - - (image_width * GTF_S_M_PRIME * 1000) + - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000); - - h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN); - frame_width = image_width + h_blank; - - pix_clk = (image_width + h_blank) * hfreq; - pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; - - hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % GTF_CELL_GRAN; - - h_fp = h_blank / 2 - hsync; - - fmt->bt.polarities = polarities; - fmt->bt.width = image_width; - fmt->bt.height = image_height; - fmt->bt.hfrontporch = h_fp; - fmt->bt.vfrontporch = v_fp; - fmt->bt.hsync = hsync; - fmt->bt.vsync = vsync; - fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; - fmt->bt.pixelclock = pix_clk; - fmt->bt.standards = V4L2_DV_BT_STD_GTF; - if (!default_gtf) - fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; - return true; -} -EXPORT_SYMBOL_GPL(v4l2_detect_gtf); - -/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes - * 0x15 and 0x16 from the EDID. - * @hor_landscape - byte 0x15 from the EDID. - * @vert_portrait - byte 0x16 from the EDID. - * - * Determines the aspect ratio from the EDID. - * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: - * "Horizontal and Vertical Screen Size or Aspect Ratio" - */ -struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) -{ - struct v4l2_fract aspect = { 16, 9 }; - u32 tmp; - u8 ratio; - - /* Nothing filled in, fallback to 16:9 */ - if (!hor_landscape && !vert_portrait) - return aspect; - /* Both filled in, so they are interpreted as the screen size in cm */ - if (hor_landscape && vert_portrait) { - aspect.numerator = hor_landscape; - aspect.denominator = vert_portrait; - return aspect; - } - /* Only one is filled in, so interpret them as a ratio: - (val + 99) / 100 */ - ratio = hor_landscape | vert_portrait; - /* Change some rounded values into the exact aspect ratio */ - if (ratio == 79) { - aspect.numerator = 16; - aspect.denominator = 9; - } else if (ratio == 34) { - aspect.numerator = 4; - aspect.numerator = 3; - } else if (ratio == 68) { - aspect.numerator = 15; - aspect.numerator = 9; - } else { - aspect.numerator = hor_landscape + 99; - aspect.denominator = 100; - } - if (hor_landscape) - return aspect; - /* The aspect ratio is for portrait, so swap numerator and denominator */ - tmp = aspect.denominator; - aspect.denominator = aspect.numerator; - aspect.numerator = tmp; - return aspect; -} -EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); - const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( const struct v4l2_discrete_probe *probe, s32 width, s32 height) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 58279467a7a5..f20b316f7d00 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -24,7 +24,6 @@ #include #include #include -#include #include static const struct v4l2_dv_timings timings[] = { @@ -190,3 +189,360 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, return false; } EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); + +/** + * v4l_match_dv_timings - check if two timings match + * @t1 - compare this v4l2_dv_timings struct... + * @t2 - with this struct. + * @pclock_delta - the allowed pixelclock deviation. + * + * Compare t1 with t2 with a given margin of error for the pixelclock. + */ +bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, + const struct v4l2_dv_timings *t2, + unsigned pclock_delta) +{ + if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) + return false; + if (t1->bt.width == t2->bt.width && + t1->bt.height == t2->bt.height && + t1->bt.interlaced == t2->bt.interlaced && + t1->bt.polarities == t2->bt.polarities && + t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta && + t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta && + t1->bt.hfrontporch == t2->bt.hfrontporch && + t1->bt.vfrontporch == t2->bt.vfrontporch && + t1->bt.vsync == t2->bt.vsync && + t1->bt.vbackporch == t2->bt.vbackporch && + (!t1->bt.interlaced || + (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch && + t1->bt.il_vsync == t2->bt.il_vsync && + t1->bt.il_vbackporch == t2->bt.il_vbackporch))) + return true; + return false; +} +EXPORT_SYMBOL_GPL(v4l_match_dv_timings); + +/* + * CVT defines + * Based on Coordinated Video Timings Standard + * version 1.1 September 10, 2003 + */ + +#define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ + +/* Normal blanking */ +#define CVT_MIN_V_BPORCH 7 /* lines */ +#define CVT_MIN_V_PORCH_RND 3 /* lines */ +#define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ + +/* Normal blanking for CVT uses GTF to calculate horizontal blanking */ +#define CVT_CELL_GRAN 8 /* character cell granularity */ +#define CVT_M 600 /* blanking formula gradient */ +#define CVT_C 40 /* blanking formula offset */ +#define CVT_K 128 /* blanking formula scaling factor */ +#define CVT_J 20 /* blanking formula scaling factor */ +#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J) +#define CVT_M_PRIME (CVT_K * CVT_M / 256) + +/* Reduced Blanking */ +#define CVT_RB_MIN_V_BPORCH 7 /* lines */ +#define CVT_RB_V_FPORCH 3 /* lines */ +#define CVT_RB_MIN_V_BLANK 460 /* us */ +#define CVT_RB_H_SYNC 32 /* pixels */ +#define CVT_RB_H_BPORCH 80 /* pixels */ +#define CVT_RB_H_BLANK 160 /* pixels */ + +/** v4l2_detect_cvt - detect if the given timings follow the CVT standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid CVT format. If so, then it will return true, and fmt will be filled + * in with the found CVT timings. + */ +bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_dv_timings *fmt) +{ + int v_fp, v_bp, h_fp, h_bp, hsync; + int frame_width, image_height, image_width; + bool reduced_blanking; + unsigned pix_clk; + + if (vsync < 4 || vsync > 7) + return false; + + if (polarities == V4L2_DV_VSYNC_POS_POL) + reduced_blanking = false; + else if (polarities == V4L2_DV_HSYNC_POS_POL) + reduced_blanking = true; + else + return false; + + /* Vertical */ + if (reduced_blanking) { + v_fp = CVT_RB_V_FPORCH; + v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; + v_bp -= vsync + v_fp; + + if (v_bp < CVT_RB_MIN_V_BPORCH) + v_bp = CVT_RB_MIN_V_BPORCH; + } else { + v_fp = CVT_MIN_V_PORCH_RND; + v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + + if (v_bp < CVT_MIN_V_BPORCH) + v_bp = CVT_MIN_V_BPORCH; + } + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + /* Aspect ratio based on vsync */ + switch (vsync) { + case 4: + image_width = (image_height * 4) / 3; + break; + case 5: + image_width = (image_height * 16) / 9; + break; + case 6: + image_width = (image_height * 16) / 10; + break; + case 7: + /* special case */ + if (image_height == 1024) + image_width = (image_height * 5) / 4; + else if (image_height == 768) + image_width = (image_height * 15) / 9; + else + return false; + break; + default: + return false; + } + + image_width = image_width & ~7; + + /* Horizontal */ + if (reduced_blanking) { + pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; + pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + + h_bp = CVT_RB_H_BPORCH; + hsync = CVT_RB_H_SYNC; + h_fp = CVT_RB_H_BLANK - h_bp - hsync; + + frame_width = image_width + CVT_RB_H_BLANK; + } else { + int h_blank; + unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; + + h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / + (100 - ideal_duty_cycle); + h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); + + if (h_blank * 100 / image_width < 20) { + h_blank = image_width / 5; + h_blank = (h_blank + 0x7) & ~0x7; + } + + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + + h_bp = h_blank / 2; + frame_width = image_width + h_blank; + + hsync = (frame_width * 8 + 50) / 100; + hsync = hsync - hsync % CVT_CELL_GRAN; + h_fp = h_blank - hsync - h_bp; + } + + fmt->bt.polarities = polarities; + fmt->bt.width = image_width; + fmt->bt.height = image_height; + fmt->bt.hfrontporch = h_fp; + fmt->bt.vfrontporch = v_fp; + fmt->bt.hsync = hsync; + fmt->bt.vsync = vsync; + fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.pixelclock = pix_clk; + fmt->bt.standards = V4L2_DV_BT_STD_CVT; + if (reduced_blanking) + fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_detect_cvt); + +/* + * GTF defines + * Based on Generalized Timing Formula Standard + * Version 1.1 September 2, 1999 + */ + +#define GTF_PXL_CLK_GRAN 250000 /* pixel clock granularity */ + +#define GTF_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ +#define GTF_V_FP 1 /* vertical front porch (lines) */ +#define GTF_CELL_GRAN 8 /* character cell granularity */ + +/* Default */ +#define GTF_D_M 600 /* blanking formula gradient */ +#define GTF_D_C 40 /* blanking formula offset */ +#define GTF_D_K 128 /* blanking formula scaling factor */ +#define GTF_D_J 20 /* blanking formula scaling factor */ +#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J) +#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256) + +/* Secondary */ +#define GTF_S_M 3600 /* blanking formula gradient */ +#define GTF_S_C 40 /* blanking formula offset */ +#define GTF_S_K 128 /* blanking formula scaling factor */ +#define GTF_S_J 35 /* blanking formula scaling factor */ +#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J) +#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256) + +/** v4l2_detect_gtf - detect if the given timings follow the GTF standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @aspect - preferred aspect ratio. GTF has no method of determining the + * aspect ratio in order to derive the image width from the + * image height, so it has to be passed explicitly. Usually + * the native screen aspect ratio is used for this. If it + * is not filled in correctly, then 16:9 will be assumed. + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid GTF format. If so, then it will return true, and fmt will be filled + * in with the found GTF timings. + */ +bool v4l2_detect_gtf(unsigned frame_height, + unsigned hfreq, + unsigned vsync, + u32 polarities, + struct v4l2_fract aspect, + struct v4l2_dv_timings *fmt) +{ + int pix_clk; + int v_fp, v_bp, h_fp, hsync; + int frame_width, image_height, image_width; + bool default_gtf; + int h_blank; + + if (vsync != 3) + return false; + + if (polarities == V4L2_DV_VSYNC_POS_POL) + default_gtf = true; + else if (polarities == V4L2_DV_HSYNC_POS_POL) + default_gtf = false; + else + return false; + + /* Vertical */ + v_fp = GTF_V_FP; + v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + if (aspect.numerator == 0 || aspect.denominator == 0) { + aspect.numerator = 16; + aspect.denominator = 9; + } + image_width = ((image_height * aspect.numerator) / aspect.denominator); + + /* Horizontal */ + if (default_gtf) + h_blank = ((image_width * GTF_D_C_PRIME * hfreq) - + (image_width * GTF_D_M_PRIME * 1000) + + (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) / + (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000); + else + h_blank = ((image_width * GTF_S_C_PRIME * hfreq) - + (image_width * GTF_S_M_PRIME * 1000) + + (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) / + (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000); + + h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN); + frame_width = image_width + h_blank; + + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; + + hsync = (frame_width * 8 + 50) / 100; + hsync = hsync - hsync % GTF_CELL_GRAN; + + h_fp = h_blank / 2 - hsync; + + fmt->bt.polarities = polarities; + fmt->bt.width = image_width; + fmt->bt.height = image_height; + fmt->bt.hfrontporch = h_fp; + fmt->bt.vfrontporch = v_fp; + fmt->bt.hsync = hsync; + fmt->bt.vsync = vsync; + fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.pixelclock = pix_clk; + fmt->bt.standards = V4L2_DV_BT_STD_GTF; + if (!default_gtf) + fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_detect_gtf); + +/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes + * 0x15 and 0x16 from the EDID. + * @hor_landscape - byte 0x15 from the EDID. + * @vert_portrait - byte 0x16 from the EDID. + * + * Determines the aspect ratio from the EDID. + * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: + * "Horizontal and Vertical Screen Size or Aspect Ratio" + */ +struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) +{ + struct v4l2_fract aspect = { 16, 9 }; + u32 tmp; + u8 ratio; + + /* Nothing filled in, fallback to 16:9 */ + if (!hor_landscape && !vert_portrait) + return aspect; + /* Both filled in, so they are interpreted as the screen size in cm */ + if (hor_landscape && vert_portrait) { + aspect.numerator = hor_landscape; + aspect.denominator = vert_portrait; + return aspect; + } + /* Only one is filled in, so interpret them as a ratio: + (val + 99) / 100 */ + ratio = hor_landscape | vert_portrait; + /* Change some rounded values into the exact aspect ratio */ + if (ratio == 79) { + aspect.numerator = 16; + aspect.denominator = 9; + } else if (ratio == 34) { + aspect.numerator = 4; + aspect.numerator = 3; + } else if (ratio == 68) { + aspect.numerator = 15; + aspect.numerator = 9; + } else { + aspect.numerator = hor_landscape + 99; + aspect.denominator = 100; + } + if (hor_landscape) + return aspect; + /* The aspect ratio is for portrait, so swap numerator and denominator */ + tmp = aspect.denominator; + aspect.denominator = aspect.numerator; + aspect.numerator = tmp; + return aspect; +} +EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 015ff82da73c..0e1d01056f16 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -201,19 +201,6 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( const struct v4l2_discrete_probe *probe, s32 width, s32 height); -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta); - -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_dv_timings *fmt); - -bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_fract aspect, - struct v4l2_dv_timings *fmt); - -struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); - void v4l2_get_timestamp(struct timeval *tv); #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 41075fa02a96..4c7bb5491658 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -64,4 +64,63 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, unsigned pclock_delta); +/** v4l_match_dv_timings() - do two timings match? + * @measured: the measured timings data. + * @standard: the timings according to the standard. + * @pclock_delta: maximum delta in Hz between standard->pixelclock and + * the measured timings. + * + * Returns true if the two timings match, returns false otherwise. + */ +bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, + const struct v4l2_dv_timings *standard, + unsigned pclock_delta); + +/** v4l2_detect_cvt - detect if the given timings follow the CVT standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid CVT format. If so, then it will return true, and fmt will be filled + * in with the found CVT timings. + */ +bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_dv_timings *fmt); + +/** v4l2_detect_gtf - detect if the given timings follow the GTF standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @aspect - preferred aspect ratio. GTF has no method of determining the + * aspect ratio in order to derive the image width from the + * image height, so it has to be passed explicitly. Usually + * the native screen aspect ratio is used for this. If it + * is not filled in correctly, then 16:9 will be assumed. + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid GTF format. If so, then it will return true, and fmt will be filled + * in with the found GTF timings. + */ +bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_fract aspect, + struct v4l2_dv_timings *fmt); + +/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes + * 0x15 and 0x16 from the EDID. + * @hor_landscape - byte 0x15 from the EDID. + * @vert_portrait - byte 0x16 from the EDID. + * + * Determines the aspect ratio from the EDID. + * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: + * "Horizontal and Vertical Screen Size or Aspect Ratio" + */ +struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); + #endif -- cgit v1.2.3 From 8023ed09cb278004a28f49844602adf23bd3615e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 10 Jul 2012 10:41:40 -0300 Subject: [media] videobuf2-core: Verify planes lengths for output buffers For output buffers application provide to the kernel the number of bytes they stored in each plane of the buffer. Verify that the value is smaller than or equal to the plane length. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Pawel Osciak Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index c9b50c7665de..35a5b8ff6a09 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -333,6 +333,41 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer return 0; } +/** + * __verify_length() - Verify that the bytesused value for each plane fits in + * the plane length and that the data offset doesn't exceed the bytesused value. + */ +static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{ + unsigned int length; + unsigned int plane; + + if (!V4L2_TYPE_IS_OUTPUT(b->type)) + return 0; + + if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { + for (plane = 0; plane < vb->num_planes; ++plane) { + length = (b->memory == V4L2_MEMORY_USERPTR) + ? b->m.planes[plane].length + : vb->v4l2_planes[plane].length; + + if (b->m.planes[plane].bytesused > length) + return -EINVAL; + if (b->m.planes[plane].data_offset >= + b->m.planes[plane].bytesused) + return -EINVAL; + } + } else { + length = (b->memory == V4L2_MEMORY_USERPTR) + ? b->length : vb->v4l2_planes[0].length; + + if (b->bytesused > length) + return -EINVAL; + } + + return 0; +} + /** * __buffer_in_use() - return true if the buffer is in use and * the queue cannot be freed (by the means of REQBUFS(0)) call @@ -1167,6 +1202,10 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) struct vb2_queue *q = vb->vb2_queue; int ret; + ret = __verify_length(vb, b); + if (ret < 0) + return ret; + switch (q->memory) { case V4L2_MEMORY_MMAP: ret = __qbuf_mmap(vb, b); -- cgit v1.2.3 From 91f9241fd3487cd959abcf8c210af620ebfeb2cd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 2 Jul 2013 23:04:25 -0300 Subject: [media] v4l: of: Use of_get_child_by_name() Replace a manual loop through child nodes with a call to of_get_child_by_name(). Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-of.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index aa59639d013c..f64d95342bf1 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -173,12 +173,8 @@ struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, if (node) parent = node; - for_each_child_of_node(parent, node) { - if (!of_node_cmp(node->name, "port")) { - port = node; - break; - } - } + port = of_get_child_by_name(parent, "port"); + if (port) { /* Found a port, get an endpoint. */ endpoint = of_get_next_child(port, NULL); -- cgit v1.2.3 From 5045b495196a624c17a1ef271cbdaa6d3e2d1097 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 2 Jul 2013 23:05:51 -0300 Subject: [media] v4l: of: Drop acquired reference to node when getting next endpoint The of_get_child_by_name() function takes a reference to the node it returns. Make sure to drop it when looking for the ports node in v4l2_of_get_next_endpoint(). Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-of.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index f64d95342bf1..ed305d8c65f0 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -186,6 +186,7 @@ struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, if (!endpoint) pr_err("%s(): no endpoint nodes specified for %s\n", __func__, parent->full_name); + of_node_put(node); } else { port = of_get_parent(prev); if (!port) -- cgit v1.2.3 From 8e3fbfee23fffa2f50a36ef7b7d18092d0f57f6c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 3 Jul 2013 07:49:06 -0300 Subject: [media] v4l: async: Make it safe to unregister unregistered notifier Calling v4l2_async_notifier_unregister() on a notifier that hasn't been registered leads to a crash. To simplify drivers, make it safe to unregister a notifier that has not been registered. Signed-off-by: Laurent Pinchart Tested-by: Sylwester Nawrocki Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index b350ab99652c..10bb62cb8d7d 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -192,6 +192,9 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) struct device *dev[n_subdev]; int i = 0; + if (!notifier->v4l2_dev) + return; + mutex_lock(&list_lock); list_del(¬ifier->list); @@ -225,6 +228,9 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) } put_device(d); } + + notifier->v4l2_dev = NULL; + /* * Don't care about the waiting list, it is initialised and populated * upon notifier registration. -- cgit v1.2.3 From ceedcc4ede04e069c5201748a34b2bdc044d3824 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 31 Jul 2013 13:10:18 -0300 Subject: [media] v4l2-async: Use proper list head for iteration over registered subdevs This fixes regression introduced in commit b426b3a660c85faf6e1ca1c92c6d [media] V4L: Merge struct v4l2_async_subdev_list with struct v4l2_subdev Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 10bb62cb8d7d..c85d69da35bd 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -199,7 +199,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) list_del(¬ifier->list); - list_for_each_entry_safe(sd, tmp, ¬ifier->done, list) { + list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { dev[i] = get_device(sd->dev); v4l2_async_cleanup(sd); -- cgit v1.2.3 From a517cca6b24fc54ac209e44118ec8962051662e3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Aug 2013 08:11:25 -0300 Subject: [media] media: vb2: Fix potential deadlock in vb2_prepare_buffer Commit b037c0fde22b1d3cd0b3c3717d28e54619fc1592 ("media: vb2: fix potential deadlock in mmap vs. get_userptr handling") fixes an AB-BA deadlock related to the mmap_sem and driver locks. The same deadlock can occur in vb2_prepare_buffer(), fix it the same way. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 52 ++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 35a5b8ff6a09..fb6802c667d3 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1248,50 +1248,84 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) */ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) { + struct rw_semaphore *mmap_sem = NULL; struct vb2_buffer *vb; int ret; + /* + * In case of user pointer buffers vb2 allocator needs to get direct + * access to userspace pages. This requires getting read access on + * mmap semaphore in the current process structure. The same + * semaphore is taken before calling mmap operation, while both mmap + * and prepare_buf are called by the driver or v4l2 core with driver's + * lock held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in + * mmap and driver's lock then mmap_sem in prepare_buf) the videobuf2 + * core release driver's lock, takes mmap_sem and then takes again + * driver's lock. + * + * To avoid race with other vb2 calls, which might be called after + * releasing driver's lock, this operation is performed at the + * beggining of prepare_buf processing. This way the queue status is + * consistent after getting driver's lock back. + */ + if (q->memory == V4L2_MEMORY_USERPTR) { + mmap_sem = ¤t->mm->mmap_sem; + call_qop(q, wait_prepare, q); + down_read(mmap_sem); + call_qop(q, wait_finish, q); + } + if (q->fileio) { dprintk(1, "%s(): file io in progress\n", __func__); - return -EBUSY; + ret = -EBUSY; + goto unlock; } if (b->type != q->type) { dprintk(1, "%s(): invalid buffer type\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (b->index >= q->num_buffers) { dprintk(1, "%s(): buffer index out of range\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } vb = q->bufs[b->index]; if (NULL == vb) { /* Should never happen */ dprintk(1, "%s(): buffer is NULL\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (b->memory != q->memory) { dprintk(1, "%s(): invalid memory type\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (vb->state != VB2_BUF_STATE_DEQUEUED) { dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state); - return -EINVAL; + ret = -EINVAL; + goto unlock; } ret = __verify_planes_array(vb, b); if (ret < 0) - return ret; + goto unlock; + ret = __buf_prepare(vb, b); if (ret < 0) - return ret; + goto unlock; __fill_v4l2_buffer(vb, b); - return 0; +unlock: + if (mmap_sem) + up_read(mmap_sem); + return ret; } EXPORT_SYMBOL_GPL(vb2_prepare_buf); -- cgit v1.2.3 From 012043b8346b2e288a791ca4a0f4f9db815b8a0a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Aug 2013 08:11:26 -0300 Subject: [media] media: vb2: Share code between vb2_prepare_buf and vb2_qbuf The two operations are very similar, refactor most of the code in a helper function. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 202 ++++++++++++------------------- 1 file changed, 79 insertions(+), 123 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index fb6802c667d3..594c75eab5a5 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1231,42 +1231,31 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) return ret; } -/** - * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_prepare_buf - * handler in driver - * - * Should be called from vidioc_prepare_buf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_prepare callback in the driver (if provided), in which - * driver-specific buffer initialization can be performed, - * - * The return values from this function are intended to be directly returned - * from vidioc_prepare_buf handler in driver. - */ -int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) +static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, + const char *opname, + int (*handler)(struct vb2_queue *, + struct v4l2_buffer *, + struct vb2_buffer *)) { struct rw_semaphore *mmap_sem = NULL; struct vb2_buffer *vb; int ret; /* - * In case of user pointer buffers vb2 allocator needs to get direct - * access to userspace pages. This requires getting read access on - * mmap semaphore in the current process structure. The same - * semaphore is taken before calling mmap operation, while both mmap - * and prepare_buf are called by the driver or v4l2 core with driver's - * lock held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in - * mmap and driver's lock then mmap_sem in prepare_buf) the videobuf2 - * core release driver's lock, takes mmap_sem and then takes again - * driver's lock. + * In case of user pointer buffers vb2 allocators need to get direct + * access to userspace pages. This requires getting the mmap semaphore + * for read access in the current process structure. The same semaphore + * is taken before calling mmap operation, while both qbuf/prepare_buf + * and mmap are called by the driver or v4l2 core with the driver's lock + * held. To avoid an AB-BA deadlock (mmap_sem then driver's lock in mmap + * and driver's lock then mmap_sem in qbuf/prepare_buf) the videobuf2 + * core releases the driver's lock, takes mmap_sem and then takes the + * driver's lock again. * - * To avoid race with other vb2 calls, which might be called after - * releasing driver's lock, this operation is performed at the - * beggining of prepare_buf processing. This way the queue status is - * consistent after getting driver's lock back. + * To avoid racing with other vb2 calls, which might be called after + * releasing the driver's lock, this operation is performed at the + * beginning of qbuf/prepare_buf processing. This way the queue status + * is consistent after getting the driver's lock back. */ if (q->memory == V4L2_MEMORY_USERPTR) { mmap_sem = ¤t->mm->mmap_sem; @@ -1276,19 +1265,19 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) } if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", __func__); + dprintk(1, "%s(): file io in progress\n", opname); ret = -EBUSY; goto unlock; } if (b->type != q->type) { - dprintk(1, "%s(): invalid buffer type\n", __func__); + dprintk(1, "%s(): invalid buffer type\n", opname); ret = -EINVAL; goto unlock; } if (b->index >= q->num_buffers) { - dprintk(1, "%s(): buffer index out of range\n", __func__); + dprintk(1, "%s(): buffer index out of range\n", opname); ret = -EINVAL; goto unlock; } @@ -1296,131 +1285,83 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) vb = q->bufs[b->index]; if (NULL == vb) { /* Should never happen */ - dprintk(1, "%s(): buffer is NULL\n", __func__); + dprintk(1, "%s(): buffer is NULL\n", opname); ret = -EINVAL; goto unlock; } if (b->memory != q->memory) { - dprintk(1, "%s(): invalid memory type\n", __func__); + dprintk(1, "%s(): invalid memory type\n", opname); ret = -EINVAL; goto unlock; } - if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state); - ret = -EINVAL; - goto unlock; - } ret = __verify_planes_array(vb, b); - if (ret < 0) + if (ret) goto unlock; - ret = __buf_prepare(vb, b); - if (ret < 0) + ret = handler(q, b, vb); + if (ret) goto unlock; + /* Fill buffer information for the userspace */ __fill_v4l2_buffer(vb, b); + dprintk(1, "%s() of buffer %d succeeded\n", opname, vb->v4l2_buf.index); unlock: if (mmap_sem) up_read(mmap_sem); return ret; } -EXPORT_SYMBOL_GPL(vb2_prepare_buf); + +static int __vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, + struct vb2_buffer *vb) +{ + if (vb->state != VB2_BUF_STATE_DEQUEUED) { + dprintk(1, "%s(): invalid buffer state %d\n", __func__, + vb->state); + return -EINVAL; + } + + return __buf_prepare(vb, b); +} /** - * vb2_qbuf() - Queue a buffer from userspace + * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_qbuf handler - * in driver + * @b: buffer structure passed from userspace to vidioc_prepare_buf + * handler in driver * - * Should be called from vidioc_qbuf ioctl handler of a driver. + * Should be called from vidioc_prepare_buf ioctl handler of a driver. * This function: * 1) verifies the passed buffer, - * 2) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 3) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. + * 2) calls buf_prepare callback in the driver (if provided), in which + * driver-specific buffer initialization can be performed, * * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. + * from vidioc_prepare_buf handler in driver. */ -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) { - struct rw_semaphore *mmap_sem = NULL; - struct vb2_buffer *vb; - int ret = 0; - - /* - * In case of user pointer buffers vb2 allocator needs to get direct - * access to userspace pages. This requires getting read access on - * mmap semaphore in the current process structure. The same - * semaphore is taken before calling mmap operation, while both mmap - * and qbuf are called by the driver or v4l2 core with driver's lock - * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in - * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core - * release driver's lock, takes mmap_sem and then takes again driver's - * lock. - * - * To avoid race with other vb2 calls, which might be called after - * releasing driver's lock, this operation is performed at the - * beggining of qbuf processing. This way the queue status is - * consistent after getting driver's lock back. - */ - if (q->memory == V4L2_MEMORY_USERPTR) { - mmap_sem = ¤t->mm->mmap_sem; - call_qop(q, wait_prepare, q); - down_read(mmap_sem); - call_qop(q, wait_finish, q); - } - - if (q->fileio) { - dprintk(1, "qbuf: file io in progress\n"); - ret = -EBUSY; - goto unlock; - } - - if (b->type != q->type) { - dprintk(1, "qbuf: invalid buffer type\n"); - ret = -EINVAL; - goto unlock; - } - - if (b->index >= q->num_buffers) { - dprintk(1, "qbuf: buffer index out of range\n"); - ret = -EINVAL; - goto unlock; - } - - vb = q->bufs[b->index]; - if (NULL == vb) { - /* Should never happen */ - dprintk(1, "qbuf: buffer is NULL\n"); - ret = -EINVAL; - goto unlock; - } + return vb2_queue_or_prepare_buf(q, b, "prepare_buf", __vb2_prepare_buf); +} +EXPORT_SYMBOL_GPL(vb2_prepare_buf); - if (b->memory != q->memory) { - dprintk(1, "qbuf: invalid memory type\n"); - ret = -EINVAL; - goto unlock; - } - ret = __verify_planes_array(vb, b); - if (ret) - goto unlock; +static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b, + struct vb2_buffer *vb) +{ + int ret; switch (vb->state) { case VB2_BUF_STATE_DEQUEUED: ret = __buf_prepare(vb, b); if (ret) - goto unlock; + return ret; case VB2_BUF_STATE_PREPARED: break; default: dprintk(1, "qbuf: buffer already in use\n"); - ret = -EINVAL; - goto unlock; + return -EINVAL; } /* @@ -1437,14 +1378,29 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) if (q->streaming) __enqueue_in_driver(vb); - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); + return 0; +} - dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); -unlock: - if (mmap_sem) - up_read(mmap_sem); - return ret; +/** + * vb2_qbuf() - Queue a buffer from userspace + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_qbuf handler + * in driver + * + * Should be called from vidioc_qbuf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue + * callback for processing. + * + * The return values from this function are intended to be directly returned + * from vidioc_qbuf handler in driver. + */ +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + return vb2_queue_or_prepare_buf(q, b, "qbuf", __vb2_qbuf); } EXPORT_SYMBOL_GPL(vb2_qbuf); -- cgit v1.2.3 From fea564a5f6a4ed6d3241aa90714ae2f0894a768a Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Tue, 13 Aug 2013 02:58:07 -0300 Subject: [media] v4l2-mem2mem: clear m2m context from job_queue before ctx streamoff When streamoff is called on the context and the context is added to the job_queue, 1] sometimes device_run receives the empty vb2 buffers (as v4l2_m2m_streamoff is dropping the ready queue). 2] sometimes v4l2_m2m_job_finish may not succeed as the m2m_dev->curr_ctx is made NULL in the v4l2_m2m_streamoff() The above points may stop the execution of the other queued contexts. This patch makes sure that before streamoff is executed on any context, that context should "not be running" or "not queued" in the job_queue. 1] If the current context is running, then abort job will be called. 2] If the current context is queued, then the context will be removed from the job_queue. Signed-off-by: Shaik Ameer Basha Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 59 ++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 21 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 89b90672088c..7c4371288215 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -265,6 +265,39 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) v4l2_m2m_try_run(m2m_dev); } +/** + * v4l2_m2m_cancel_job() - cancel pending jobs for the context + * + * In case of streamoff or release called on any context, + * 1] If the context is currently running, then abort job will be called + * 2] If the context is queued, then the context will be removed from + * the job_queue + */ +static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev; + unsigned long flags; + + m2m_dev = m2m_ctx->m2m_dev; + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (m2m_ctx->job_flags & TRANS_RUNNING) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); + dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); + wait_event(m2m_ctx->finished, + !(m2m_ctx->job_flags & TRANS_RUNNING)); + } else if (m2m_ctx->job_flags & TRANS_QUEUED) { + list_del(&m2m_ctx->queue); + m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("m2m_ctx: %p had been on queue and was removed\n", + m2m_ctx); + } else { + /* Do nothing, was not on queue/running */ + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + } +} + /** * v4l2_m2m_job_finish() - inform the framework that a job has been finished * and have it clean up @@ -436,6 +469,9 @@ int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, unsigned long flags_job, flags; int ret; + /* wait until the current context is dequeued from job_queue */ + v4l2_m2m_cancel_job(m2m_ctx); + q_ctx = get_queue_ctx(m2m_ctx, type); ret = vb2_streamoff(&q_ctx->q, type); if (ret) @@ -658,27 +694,8 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); */ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) { - struct v4l2_m2m_dev *m2m_dev; - unsigned long flags; - - m2m_dev = m2m_ctx->m2m_dev; - - spin_lock_irqsave(&m2m_dev->job_spinlock, flags); - if (m2m_ctx->job_flags & TRANS_RUNNING) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); - dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); - wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING)); - } else if (m2m_ctx->job_flags & TRANS_QUEUED) { - list_del(&m2m_ctx->queue); - m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - dprintk("m2m_ctx: %p had been on queue and was removed\n", - m2m_ctx); - } else { - /* Do nothing, was not on queue/running */ - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - } + /* wait until the current context is dequeued from job_queue */ + v4l2_m2m_cancel_job(m2m_ctx); vb2_queue_release(&m2m_ctx->cap_q_ctx.q); vb2_queue_release(&m2m_ctx->out_q_ctx.q); -- cgit v1.2.3 From d1d70aa69db3d09240e9faf6bf68a044b9107480 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sun, 11 Aug 2013 02:02:24 -0300 Subject: [media] media: OF: add "sync-on-green-active" property This patch adds 'sync-on-green-active' property as part of endpoint property. Signed-off-by: Lad, Prabhakar Acked-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/video-interfaces.txt | 2 ++ drivers/media/v4l2-core/v4l2-of.c | 4 ++++ include/media/v4l2-mediabus.h | 3 +++ 3 files changed, 9 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt index e022d2dc4962..ce719f89dd1c 100644 --- a/Documentation/devicetree/bindings/media/video-interfaces.txt +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt @@ -88,6 +88,8 @@ Optional endpoint properties - field-even-active: field signal level during the even field data transmission. - pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock signal. +- sync-on-green-active: active state of Sync-on-green (SoG) signal, 0/1 for + LOW/HIGH respectively. - data-lanes: an array of physical data lane indexes. Position of an entry determines the logical lane number, while the value of an entry indicates physical lane, e.g. for 2-lane MIPI CSI-2 bus we could have diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index ed305d8c65f0..a6478dca0cde 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -100,6 +100,10 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, if (!of_property_read_u32(node, "data-shift", &v)) bus->data_shift = v; + if (!of_property_read_u32(node, "sync-on-green-active", &v)) + flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : + V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; + bus->flags = flags; } diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 83ae07e53350..395c4a95a42a 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -40,6 +40,9 @@ #define V4L2_MBUS_FIELD_EVEN_HIGH (1 << 10) /* FIELD = 1/0 - Field1 (odd)/Field2 (even) */ #define V4L2_MBUS_FIELD_EVEN_LOW (1 << 11) +/* Active state of Sync-on-green (SoG) signal, 0/1 for LOW/HIGH respectively. */ +#define V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH (1 << 12) +#define V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW (1 << 13) /* Serial flags */ /* How many lanes the client can use */ -- cgit v1.2.3 From fbbb29a671b5082c9ffafed0d1b5f0ae52b83452 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 18 Aug 2013 16:05:19 -0300 Subject: [media] v4l2-ctrl: Suppress build warning from v4l2_ctrl_new_std_menu() Prevent following build warning: drivers/media/v4l2-core/v4l2-ctrls.c: In function v4l2_ctrl_new_std_menu: drivers/media/v4l2-core/v4l2-ctrls.c:1768:15: warning: 'qmenu_int_len' may be used uninitialized in this function Signed-off-by: Sylwester Nawrocki Cc: Arun Kumar K 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/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index c6dc1fd427d7..c3f080388684 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1763,9 +1763,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, { const char * const *qmenu = NULL; const s64 *qmenu_int = NULL; + unsigned int qmenu_int_len = 0; const char *name; enum v4l2_ctrl_type type; - unsigned int qmenu_int_len; s32 min; s32 step; u32 flags; -- cgit v1.2.3 From 1056e4388b0454917a512618c8416a98628fc9ce Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 13 Aug 2013 11:04:06 -0300 Subject: [media] v4l2-dev: Fix race condition on __video_register_device When 2 devices are registered at the same time, in Part 2 both will get the same minor number, making Part6 crash, because it cannot create a create a device with a duplicated minor: [ 7.157648] ------------[ cut here ]------------ [ 7.157666] WARNING: at fs/sysfs/dir.c:530 sysfs_add_one+0xbd/0xe0() [ 7.157669] sysfs: cannot create duplicate filename '/dev/char/81:1' [ 7.157672] Modules linked in: qtec_xform(+) qt5023_video(+) videobuf2_vmalloc videobuf2_dma_sg videobuf2_memops videobuf2_core gpio_xilinx(+) qtec_white qtec_cmosis(+) qtec_pcie qt5023 [ 7.157694] CPU: 0 PID: 120 Comm: systemd-udevd Not tainted 3.10.0-qtec-standard #8 [ 7.157698] Hardware name: QTechnology QT5022/QT5022, BIOS PM_2.1.0.309 X64 05/23/2013 [ 7.157702] 0000000000000009 ffff8801788358e8 ffffffff8176c487 ffff880178835928 [ 7.157707] ffffffff8106f6f0 ffff880175759770 00000000ffffffef ffff880175759930 [ 7.157712] ffff8801788359e8 ffff880178bff000 ffff880175759930 ffff880178835988 [ 7.157718] Call Trace: [ 7.157728] [] dump_stack+0x19/0x1b [ 7.157735] [] warn_slowpath_common+0x70/0xa0 [ 7.157740] [] warn_slowpath_fmt+0x46/0x50 [ 7.157746] [] ? strlcat+0x65/0x90 [ 7.157750] [] sysfs_add_one+0xbd/0xe0 [ 7.157755] [] sysfs_do_create_link_sd+0xdb/0x200 [ 7.157760] [] sysfs_create_link+0x21/0x40 [ 7.157765] [] device_add+0x21b/0x6d0 [ 7.157772] [] ? pm_runtime_init+0xe5/0xf0 [ 7.157776] [] device_register+0x1e/0x30 [ 7.157782] [] __video_register_device+0x313/0x610 [ 7.157791] [] qtec_xform_probe+0x465/0x7a4 [qtec_xform] [ 7.157797] [] platform_drv_probe+0x43/0x80 [ 7.157802] [] ? driver_sysfs_add+0x7a/0xb0 [ 7.157807] [] driver_probe_device+0x8b/0x3a0 [ 7.157812] [] __driver_attach+0xab/0xb0 [ 7.157816] [] ? driver_probe_device+0x3a0/0x3a0 [ 7.157820] [] bus_for_each_dev+0x5d/0xa0 [ 7.157825] [] driver_attach+0x1e/0x20 [ 7.157829] [] bus_add_driver+0x10e/0x280 [ 7.157833] [] ? 0xffffffffa0098fff [ 7.157837] [] ? 0xffffffffa0098fff [ 7.157842] [] driver_register+0x77/0x170 [ 7.157848] [] ? __vunmap+0x9c/0x110 [ 7.157852] [] ? 0xffffffffa0098fff [ 7.157857] [] platform_driver_register+0x46/0x50 [ 7.157863] [] qtec_xform_plat_driver_init+0x10/0x12 [qtec_xform] [ 7.157869] [] do_one_initcall+0xea/0x1a0 [ 7.157875] [] load_module+0x1a91/0x2630 [ 7.157880] [] ? ddebug_proc_show+0xe0/0xe0 [ 7.157887] [] ? page_fault+0x22/0x30 [ 7.157892] [] SyS_init_module+0xea/0x140 [ 7.157898] [] tracesys+0xd0/0xd5 [ 7.157902] ---[ end trace 660cc3a65a4bf01b ]--- [ 7.157939] __video_register_device: device_register failed Signed-off-by: Ricardo Ribalda Delgado Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index c8859d6ff6ad..b2f207f65609 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -868,6 +868,7 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* Should not happen since we thought this minor was free */ WARN_ON(video_device[vdev->minor] != NULL); + video_device[vdev->minor] = vdev; vdev->index = get_index(vdev); mutex_unlock(&videodev_lock); @@ -930,9 +931,6 @@ int __video_register_device(struct video_device *vdev, int type, int nr, #endif /* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags); - mutex_lock(&videodev_lock); - video_device[vdev->minor] = vdev; - mutex_unlock(&videodev_lock); return 0; @@ -940,6 +938,7 @@ cleanup: mutex_lock(&videodev_lock); if (vdev->cdev) cdev_del(vdev->cdev); + video_device[vdev->minor] = NULL; devnode_clear(vdev); mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */ -- cgit v1.2.3 From c3e75c7d5e203b6d1d16519bef8571b5d228b7fe Mon Sep 17 00:00:00 2001 From: Martin Bugge Date: Wed, 14 Aug 2013 04:58:38 -0300 Subject: [media] v4l2-dv-timings: fix CVT calculation This patch fixes two errors that caused incorrect format detections: The first bug is in the calculation of the vertical backporch: the combined period of vsync and backporch must *exceed* a certain minimum value, and not be equal to it. The second bug is a rounding error in the reduced blanking calculation: expand the ideal_duty_cylce to be in parts per ten thousand to avoid rounding errors. Signed-off-by: Martin Bugge Cc: Mats Randgaard Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index f20b316f7d00..72cf2240bac4 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -286,14 +286,14 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, /* Vertical */ if (reduced_blanking) { v_fp = CVT_RB_V_FPORCH; - v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; + v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 1999999) / 1000000; v_bp -= vsync + v_fp; if (v_bp < CVT_RB_MIN_V_BPORCH) v_bp = CVT_RB_MIN_V_BPORCH; } else { v_fp = CVT_MIN_V_PORCH_RND; - v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + v_bp = (CVT_MIN_VSYNC_BP * hfreq + 1999999) / 1000000 - vsync; if (v_bp < CVT_MIN_V_BPORCH) v_bp = CVT_MIN_V_BPORCH; @@ -337,17 +337,16 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, frame_width = image_width + CVT_RB_H_BLANK; } else { + unsigned ideal_duty_cycle_per_myriad = + 100 * CVT_C_PRIME - (CVT_M_PRIME * 100000) / hfreq; int h_blank; - unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; - h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / - (100 - ideal_duty_cycle); - h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); + if (ideal_duty_cycle_per_myriad < 2000) + ideal_duty_cycle_per_myriad = 2000; - if (h_blank * 100 / image_width < 20) { - h_blank = image_width / 5; - h_blank = (h_blank + 0x7) & ~0x7; - } + h_blank = image_width * ideal_duty_cycle_per_myriad / + (10000 - ideal_duty_cycle_per_myriad); + h_blank = (h_blank / (2 * CVT_CELL_GRAN)) * 2 * CVT_CELL_GRAN; pix_clk = (image_width + h_blank) * hfreq; pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; -- cgit v1.2.3 From 0216dc2fe666b5cedba54df4c0be6ddc8eb352e1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Aug 2013 08:02:40 -0300 Subject: [media] v4l2-dv-timings: add v4l2_print_dv_timings helper Drivers often have to log the contents of a dv_timings struct. Adding this helper will make it easier for drivers to do so. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 49 +++++++++++++++++++++++++++++++ include/media/v4l2-dv-timings.h | 9 ++++++ 2 files changed, 58 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 72cf2240bac4..917e58ce1f18 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -223,6 +223,55 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, } EXPORT_SYMBOL_GPL(v4l_match_dv_timings); +void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, + const struct v4l2_dv_timings *t, bool detailed) +{ + const struct v4l2_bt_timings *bt = &t->bt; + u32 htot, vtot; + + if (t->type != V4L2_DV_BT_656_1120) + return; + + htot = V4L2_DV_BT_FRAME_WIDTH(bt); + vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); + + if (prefix == NULL) + prefix = ""; + + pr_info("%s: %s%ux%u%s%u (%ux%u)\n", dev_prefix, prefix, + bt->width, bt->height, bt->interlaced ? "i" : "p", + (htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0, + htot, vtot); + + if (!detailed) + return; + + pr_info("%s: horizontal: fp = %u, %ssync = %u, bp = %u\n", + dev_prefix, bt->hfrontporch, + (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", + bt->hsync, bt->hbackporch); + pr_info("%s: vertical: fp = %u, %ssync = %u, bp = %u\n", + dev_prefix, bt->vfrontporch, + (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", + bt->vsync, bt->vbackporch); + pr_info("%s: pixelclock: %llu\n", dev_prefix, bt->pixelclock); + pr_info("%s: flags (0x%x):%s%s%s%s\n", dev_prefix, bt->flags, + (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? + " REDUCED_BLANKING" : "", + (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ? + " CAN_REDUCE_FPS" : "", + (bt->flags & V4L2_DV_FL_REDUCED_FPS) ? + " REDUCED_FPS" : "", + (bt->flags & V4L2_DV_FL_HALF_LINE) ? + " HALF_LINE" : ""); + pr_info("%s: standards (0x%x):%s%s%s%s\n", dev_prefix, bt->standards, + (bt->standards & V4L2_DV_BT_STD_CEA861) ? " CEA" : "", + (bt->standards & V4L2_DV_BT_STD_DMT) ? " DMT" : "", + (bt->standards & V4L2_DV_BT_STD_CVT) ? " CVT" : "", + (bt->standards & V4L2_DV_BT_STD_GTF) ? " GTF" : ""); +} +EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); + /* * CVT defines * Based on Coordinated Video Timings Standard diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 4c7bb5491658..696e5c2fd176 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -76,6 +76,15 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, const struct v4l2_dv_timings *standard, unsigned pclock_delta); +/** v4l2_print_dv_timings() - log the contents of a dv_timings struct + * @dev_prefix:device prefix for each log line. + * @prefix: additional prefix for each log line, may be NULL. + * @t: the timings data. + * @detailed: if true, give a detailed log. + */ +void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, + const struct v4l2_dv_timings *t, bool detailed); + /** v4l2_detect_cvt - detect if the given timings follow the CVT standard * @frame_height - the total height of the frame (including blanking) in lines. * @hfreq - the horizontal frequency in Hz. -- cgit v1.2.3 From ef1ed8f5d366a035e532456bd747d34e5cb01ee5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Aug 2013 08:28:47 -0300 Subject: [media] v4l2-dv-timings: rename v4l_match_dv_timings to v4l2_match_dv_timings It's the only function in v4l2-dv-timings.c with the v4l prefix instead of v4l2. Make it consistent with the other functions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 4 ++-- drivers/media/platform/s5p-tv/hdmi_drv.c | 2 +- drivers/media/usb/hdpvr/hdpvr-video.c | 2 +- drivers/media/v4l2-core/v4l2-dv-timings.c | 12 ++++++------ include/media/v4l2-dv-timings.h | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index ba8602c06d6b..a1a9d1e805f0 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -763,7 +763,7 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, int i; for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { - if (!v4l_match_dv_timings(timings, &predef_vid_timings[i].timings, + if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, DIGITAL_INPUT ? 250000 : 1000000)) continue; io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */ @@ -1183,7 +1183,7 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, int i; for (i = 0; adv7604_timings[i].bt.width; i++) { - if (v4l_match_dv_timings(timings, &adv7604_timings[i], + if (v4l2_match_dv_timings(timings, &adv7604_timings[i], DIGITAL_INPUT ? 250000 : 1000000)) { *timings = adv7604_timings[i]; break; diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 1b34c3629858..4ad9374913db 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -625,7 +625,7 @@ static int hdmi_s_dv_timings(struct v4l2_subdev *sd, int i; for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) - if (v4l_match_dv_timings(&hdmi_timings[i].dv_timings, + if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings, timings, 0)) break; if (i == ARRAY_SIZE(hdmi_timings)) { diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index e68245a7f3a7..0500c4175d5f 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -642,7 +642,7 @@ static int vidioc_s_dv_timings(struct file *file, void *_fh, if (dev->status != STATUS_IDLE) return -EBUSY; for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) - if (v4l_match_dv_timings(timings, hdpvr_dv_timings + i, 0)) + if (v4l2_match_dv_timings(timings, hdpvr_dv_timings + i, 0)) break; if (i == ARRAY_SIZE(hdpvr_dv_timings)) return -EINVAL; diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 917e58ce1f18..1a9d393307a1 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -181,7 +181,7 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, for (i = 0; i < ARRAY_SIZE(timings); i++) { if (v4l2_dv_valid_timings(timings + i, cap) && - v4l_match_dv_timings(t, timings + i, pclock_delta)) { + v4l2_match_dv_timings(t, timings + i, pclock_delta)) { *t = timings[i]; return true; } @@ -191,16 +191,16 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); /** - * v4l_match_dv_timings - check if two timings match + * v4l2_match_dv_timings - check if two timings match * @t1 - compare this v4l2_dv_timings struct... * @t2 - with this struct. * @pclock_delta - the allowed pixelclock deviation. * * Compare t1 with t2 with a given margin of error for the pixelclock. */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta) +bool v4l2_match_dv_timings(const struct v4l2_dv_timings *t1, + const struct v4l2_dv_timings *t2, + unsigned pclock_delta) { if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) return false; @@ -221,7 +221,7 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, return true; return false; } -EXPORT_SYMBOL_GPL(v4l_match_dv_timings); +EXPORT_SYMBOL_GPL(v4l2_match_dv_timings); void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, const struct v4l2_dv_timings *t, bool detailed) diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 696e5c2fd176..43f6b67af1cb 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -64,7 +64,7 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, unsigned pclock_delta); -/** v4l_match_dv_timings() - do two timings match? +/** v4l2_match_dv_timings() - do two timings match? * @measured: the measured timings data. * @standard: the timings according to the standard. * @pclock_delta: maximum delta in Hz between standard->pixelclock and @@ -72,9 +72,9 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, * * Returns true if the two timings match, returns false otherwise. */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, - const struct v4l2_dv_timings *standard, - unsigned pclock_delta); +bool v4l2_match_dv_timings(const struct v4l2_dv_timings *measured, + const struct v4l2_dv_timings *standard, + unsigned pclock_delta); /** v4l2_print_dv_timings() - log the contents of a dv_timings struct * @dev_prefix:device prefix for each log line. -- cgit v1.2.3 From 074ca43f2f99c926a250847ae59a337ebdec6d61 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 08:38:29 -0300 Subject: [media] v4l2-dv-timings: fill in type field The detect_cvt/gtf functions didn't fill in the type field. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 1a9d393307a1..c2f5af7acbed 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -408,6 +408,7 @@ bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, h_fp = h_blank - hsync - h_bp; } + fmt->type = V4L2_DV_BT_656_1120; fmt->bt.polarities = polarities; fmt->bt.width = image_width; fmt->bt.height = image_height; @@ -527,6 +528,7 @@ bool v4l2_detect_gtf(unsigned frame_height, h_fp = h_blank / 2 - hsync; + fmt->type = V4L2_DV_BT_656_1120; fmt->bt.polarities = polarities; fmt->bt.width = image_width; fmt->bt.height = image_height; -- cgit v1.2.3 From d1c65ad6a44b0ff79d2f0bf726fa6fd9248991f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 10:19:54 -0300 Subject: [media] v4l2-dv-timings: export the timings list Some drivers need to be able to access the full list of timings. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 18 ++++++++++-------- include/media/v4l2-dv-timings.h | 4 ++++ 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index c2f5af7acbed..f515997a7341 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -26,7 +26,7 @@ #include #include -static const struct v4l2_dv_timings timings[] = { +const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { V4L2_DV_BT_CEA_640X480P59_94, V4L2_DV_BT_CEA_720X480I59_94, V4L2_DV_BT_CEA_720X480P59_94, @@ -127,7 +127,9 @@ static const struct v4l2_dv_timings timings[] = { V4L2_DV_BT_DMT_2560X1600P75, V4L2_DV_BT_DMT_2560X1600P85, V4L2_DV_BT_DMT_2560X1600P120_RB, + { } }; +EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *dvcap) @@ -159,10 +161,10 @@ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, u32 i, idx; memset(t->reserved, 0, sizeof(t->reserved)); - for (i = idx = 0; i < ARRAY_SIZE(timings); i++) { - if (v4l2_dv_valid_timings(timings + i, cap) && + for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && idx++ == t->index) { - t->timings = timings[i]; + t->timings = v4l2_dv_timings_presets[i]; return 0; } } @@ -179,10 +181,10 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, if (!v4l2_dv_valid_timings(t, cap)) return false; - for (i = 0; i < ARRAY_SIZE(timings); i++) { - if (v4l2_dv_valid_timings(timings + i, cap) && - v4l2_match_dv_timings(t, timings + i, pclock_delta)) { - *t = timings[i]; + for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { + if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { + *t = v4l2_dv_timings_presets[i]; return true; } } diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 43f6b67af1cb..0fe310b8bc80 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -23,6 +23,10 @@ #include +/** v4l2_dv_timings_presets: list of all dv_timings presets. + */ +extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; + /** v4l2_dv_valid_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. -- cgit v1.2.3 From 70b654945bacd27622ef1c424f054ae04de597e0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 10:23:33 -0300 Subject: [media] v4l2-dv-timings: rename v4l2_dv_valid_timings to v4l2_valid_dv_timings All other functions follow the v4l2__dv_timings pattern, do the same for this function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 2 +- drivers/media/i2c/ths8200.c | 2 +- drivers/media/v4l2-core/v4l2-dv-timings.c | 10 +++++----- include/media/v4l2-dv-timings.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index bb74fb6b35c7..fc608516fc43 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -648,7 +648,7 @@ static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); /* quick sanity check */ - if (!v4l2_dv_valid_timings(timings, &ad9389b_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap)) return -EINVAL; /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 580bd1b179fb..6abf0fb36079 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -378,7 +378,7 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); - if (!v4l2_dv_valid_timings(timings, &ths8200_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap)) return -EINVAL; if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10)) { diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index f515997a7341..a77f20145881 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -131,7 +131,7 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { }; EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); -bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, +bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *dvcap) { const struct v4l2_bt_timings *bt = &t->bt; @@ -153,7 +153,7 @@ bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, return false; return true; } -EXPORT_SYMBOL_GPL(v4l2_dv_valid_timings); +EXPORT_SYMBOL_GPL(v4l2_valid_dv_timings); int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, const struct v4l2_dv_timings_cap *cap) @@ -162,7 +162,7 @@ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, memset(t->reserved, 0, sizeof(t->reserved)); for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && idx++ == t->index) { t->timings = v4l2_dv_timings_presets[i]; return 0; @@ -178,11 +178,11 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, { int i; - if (!v4l2_dv_valid_timings(t, cap)) + if (!v4l2_valid_dv_timings(t, cap)) return false; for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { *t = v4l2_dv_timings_presets[i]; return true; diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 0fe310b8bc80..bd59df8125c6 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -27,14 +27,14 @@ */ extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; -/** v4l2_dv_valid_timings() - are these timings valid? +/** v4l2_valid_dv_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. * * Returns true if the given dv_timings struct is supported by the * hardware capabilities, returns false otherwise. */ -bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, +bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap); /** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities -- cgit v1.2.3 From b8f0fff4279a1b85fa4b6d7d8b538c254edcb4a1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 11:21:50 -0300 Subject: [media] v4l2-dv-timings: add callback to handle exceptions In most cases the v4l2_bt_timings_cap struct has all the information necessary to determine valid timings, but occasionally there are exceptions. Add a callback function to be able to test for those exceptions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 7 ++++--- drivers/media/i2c/ths8200.c | 9 +++++--- drivers/media/v4l2-core/v4l2-dv-timings.c | 25 +++++++++++++++-------- include/media/v4l2-dv-timings.h | 34 +++++++++++++++++++++++++------ 4 files changed, 55 insertions(+), 20 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index fc608516fc43..836978602973 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -648,12 +648,12 @@ static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); /* quick sanity check */ - if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap, NULL, NULL)) return -EINVAL; /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings if the format is one of the CEA or DMT timings. */ - v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0); + v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0, NULL, NULL); timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; @@ -691,7 +691,8 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap); + return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap, + NULL, NULL); } static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 6abf0fb36079..a58a8f663ffb 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -378,10 +378,12 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); - if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap, + NULL, NULL)) return -EINVAL; - if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10)) { + if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10, + NULL, NULL)) { v4l2_dbg(1, debug, sd, "Unsupported format\n"); return -EINVAL; } @@ -411,7 +413,8 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd, static int ths8200_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap); + return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap, + NULL, NULL); } static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index a77f20145881..ee52b9f4a944 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -132,7 +132,9 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, - const struct v4l2_dv_timings_cap *dvcap) + const struct v4l2_dv_timings_cap *dvcap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { const struct v4l2_bt_timings *bt = &t->bt; const struct v4l2_bt_timings_cap *cap = &dvcap->bt; @@ -151,18 +153,21 @@ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) || (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE))) return false; - return true; + return fnc == NULL || fnc(t, fnc_handle); } EXPORT_SYMBOL_GPL(v4l2_valid_dv_timings); int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, - const struct v4l2_dv_timings_cap *cap) + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { u32 i, idx; memset(t->reserved, 0, sizeof(t->reserved)); for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap, + fnc, fnc_handle) && idx++ == t->index) { t->timings = v4l2_dv_timings_presets[i]; return 0; @@ -174,16 +179,20 @@ EXPORT_SYMBOL_GPL(v4l2_enum_dv_timings_cap); bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, - unsigned pclock_delta) + unsigned pclock_delta, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { int i; - if (!v4l2_valid_dv_timings(t, cap)) + if (!v4l2_valid_dv_timings(t, cap, fnc, fnc_handle)) return false; for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && - v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap, + fnc, fnc_handle) && + v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, + pclock_delta)) { *t = v4l2_dv_timings_presets[i]; return true; } diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index bd59df8125c6..4becc6716393 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -27,46 +27,68 @@ */ extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; +/** v4l2_check_dv_timings_fnc - timings check callback + * @t: the v4l2_dv_timings struct. + * @handle: a handle from the driver. + * + * Returns true if the given timings are valid. + */ +typedef bool v4l2_check_dv_timings_fnc(const struct v4l2_dv_timings *t, void *handle); + /** v4l2_valid_dv_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. + * @fnc: callback to check if this timing is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * Returns true if the given dv_timings struct is supported by the - * hardware capabilities, returns false otherwise. + * hardware capabilities and the callback function (if non-NULL), returns + * false otherwise. */ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, - const struct v4l2_dv_timings_cap *cap); + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities * @t: the v4l2_enum_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. + * @fnc: callback to check if this timing is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * This enumerates dv_timings using the full list of possible CEA-861 and DMT * timings, filtering out any timings that are not supported based on the - * hardware capabilities. + * hardware capabilities and the callback function (if non-NULL). * * If a valid timing for the given index is found, it will fill in @t and * return 0, otherwise it returns -EINVAL. */ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, - const struct v4l2_dv_timings_cap *cap); + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_find_dv_timings_cap() - Find the closest timings struct * @t: the v4l2_enum_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. * @pclock_delta: maximum delta between t->pixelclock and the timing struct * under consideration. + * @fnc: callback to check if a given timings struct is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * This function tries to map the given timings to an entry in the * full list of possible CEA-861 and DMT timings, filtering out any timings - * that are not supported based on the hardware capabilities. + * that are not supported based on the hardware capabilities and the callback + * function (if non-NULL). * * On success it will fill in @t with the found timings and it returns true. * On failure it will return false. */ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, - unsigned pclock_delta); + unsigned pclock_delta, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_match_dv_timings() - do two timings match? * @measured: the measured timings data. -- cgit v1.2.3