summaryrefslogtreecommitdiff
path: root/drivers/media/v4l2-core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/v4l2-core')
-rw-r--r--drivers/media/v4l2-core/Kconfig33
-rw-r--r--drivers/media/v4l2-core/Makefile37
-rw-r--r--drivers/media/v4l2-core/tuner-core.c13
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c744
-rw-r--r--drivers/media/v4l2-core/v4l2-cci.c203
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c566
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c108
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-api.c310
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c906
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-defs.c117
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-priv.h5
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-request.c15
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c254
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c14
-rw-r--r--drivers/media/v4l2-core/v4l2-dv-timings.c265
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-fh.c16
-rw-r--r--drivers/media/v4l2-core/v4l2-flash-led-class.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c255
-rw-r--r--drivers/media/v4l2-core/v4l2-h264.c279
-rw-r--r--drivers/media/v4l2-core/v4l2-i2c.c5
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c935
-rw-r--r--drivers/media/v4l2-core/v4l2-isp.c132
-rw-r--r--drivers/media/v4l2-core/v4l2-jpeg.c202
-rw-r--r--drivers/media/v4l2-core/v4l2-mc.c81
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c173
-rw-r--r--drivers/media/v4l2-core/v4l2-spi.c6
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev-priv.h14
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c1938
-rw-r--r--drivers/media/v4l2-core/videobuf-core.c1198
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-contig.c408
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c685
-rw-r--r--drivers/media/v4l2-core/videobuf-vmalloc.c326
33 files changed, 5797 insertions, 4450 deletions
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 6ee75c6c820e..d50ccac9733c 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -3,21 +3,13 @@
# Generic video config states
#
-# Enable the V4L2 core and API
-config VIDEO_V4L2
- tristate
- depends on (I2C || I2C=n) && VIDEO_DEV
- select RATIONAL
- select VIDEOBUF2_V4L2 if VIDEOBUF2_CORE
- default (I2C || I2C=n) && VIDEO_DEV
-
config VIDEO_V4L2_I2C
bool
- depends on I2C && VIDEO_V4L2
+ depends on I2C && VIDEO_DEV
default y
config VIDEO_V4L2_SUBDEV_API
- bool "V4L2 sub-device userspace API"
+ bool
depends on VIDEO_DEV && MEDIA_CONTROLLER
help
Enables the V4L2 sub-device pad-level userspace API used to configure
@@ -64,9 +56,11 @@ config V4L2_MEM2MEM_DEV
# Used by LED subsystem flash drivers
config V4L2_FLASH_LED_CLASS
tristate "V4L2 flash API for LED flash class devices"
- depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_DEV
depends on LEDS_CLASS_FLASH
+ select MEDIA_CONTROLLER
select V4L2_ASYNC
+ select VIDEO_V4L2_SUBDEV_API
help
Say Y here to enable V4L2 flash API support for LED flash
class drivers.
@@ -80,18 +74,15 @@ config V4L2_FWNODE
config V4L2_ASYNC
tristate
-# Used by drivers that need Videobuf modules
-config VIDEOBUF_GEN
+config V4L2_CCI
tristate
-config VIDEOBUF_DMA_SG
+config V4L2_CCI_I2C
tristate
- select VIDEOBUF_GEN
+ depends on I2C
+ select REGMAP_I2C
+ select V4L2_CCI
-config VIDEOBUF_VMALLOC
+config V4L2_ISP
tristate
- select VIDEOBUF_GEN
-
-config VIDEOBUF_DMA_CONTIG
- tristate
- select VIDEOBUF_GEN
+ depends on VIDEOBUF2_CORE
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 83fac5c746f5..329f0eadce99 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -3,37 +3,36 @@
# Makefile for the V4L2 core
#
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+ccflags-y += -I$(srctree)/drivers/media/tuners
+
tuner-objs := tuner-core.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
v4l2-event.o v4l2-subdev.o v4l2-common.o \
v4l2-ctrls-core.o v4l2-ctrls-api.o \
v4l2-ctrls-request.o v4l2-ctrls-defs.o
+
+# Please keep it alphabetically sorted by Kconfig name
+# (e. g. LC_ALL=C sort Makefile)
videodev-$(CONFIG_COMPAT) += v4l2-compat-ioctl32.o
-videodev-$(CONFIG_TRACEPOINTS) += v4l2-trace.o
videodev-$(CONFIG_MEDIA_CONTROLLER) += v4l2-mc.o
videodev-$(CONFIG_SPI) += v4l2-spi.o
+videodev-$(CONFIG_TRACEPOINTS) += v4l2-trace.o
videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
-obj-$(CONFIG_VIDEO_V4L2) += videodev.o
-obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
-obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
-obj-$(CONFIG_VIDEO_V4L2) += v4l2-dv-timings.o
-
-obj-$(CONFIG_VIDEO_TUNER) += tuner.o
-
-obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
-obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
-obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o
+# Please keep it alphabetically sorted by Kconfig name
+# (e. g. LC_ALL=C sort Makefile)
+obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
+obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
-
+obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
+obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
+obj-$(CONFIG_V4L2_ISP) += v4l2-isp.o
obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o
+obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
+obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o
-obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
-obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
-obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
-obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
-
-ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
-ccflags-y += -I$(srctree)/drivers/media/tuners
+obj-$(CONFIG_VIDEO_TUNER) += tuner.o
+obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o
diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
index 12d1e0c33c3c..5687089bea6e 100644
--- a/drivers/media/v4l2-core/tuner-core.c
+++ b/drivers/media/v4l2-core/tuner-core.c
@@ -35,7 +35,7 @@
#include "tda8290.h"
#include "tea5761.h"
#include "tea5767.h"
-#include "tuner-xc2028.h"
+#include "xc2028.h"
#include "tuner-simple.h"
#include "tda9887.h"
#include "xc5000.h"
@@ -614,7 +614,6 @@ static void tuner_lookup(struct i2c_adapter *adap,
*tuner_probe - Probes the existing tuners on an I2C bus
*
* @client: i2c_client descriptor
- * @id: not used
*
* This routine probes for tuners at the expected I2C addresses. On most
* cases, if a device answers to a given I2C address, it assumes that the
@@ -625,8 +624,7 @@ static void tuner_lookup(struct i2c_adapter *adap,
* During client attach, set_type is called by adapter's attach_inform callback.
* set_type must then be completed by tuner_probe.
*/
-static int tuner_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tuner_probe(struct i2c_client *client)
{
struct tuner *t;
struct tuner *radio;
@@ -779,7 +777,7 @@ register_client:
* @client: i2c_client descriptor
*/
-static int tuner_remove(struct i2c_client *client)
+static void tuner_remove(struct i2c_client *client)
{
struct tuner *t = to_tuner(i2c_get_clientdata(client));
@@ -789,7 +787,6 @@ static int tuner_remove(struct i2c_client *client)
list_del(&t->list);
kfree(t);
- return 0;
}
/*
@@ -1118,7 +1115,7 @@ static void tuner_status(struct dvb_frontend *fe)
if (t->mode != V4L2_TUNER_RADIO)
return;
if (fe_tuner_ops->get_status) {
- u32 tuner_status;
+ u32 tuner_status = 0;
fe_tuner_ops->get_status(&t->fe, &tuner_status);
if (tuner_status & TUNER_STATUS_LOCKED)
@@ -1258,7 +1255,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
if (vt->type == t->mode) {
vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
if (fe_tuner_ops->get_status) {
- u32 tuner_status;
+ u32 tuner_status = 0;
fe_tuner_ops->get_status(&t->fe, &tuner_status);
vt->rxsubchans =
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 0404267f1ae4..ee884a8221fb 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -24,24 +24,26 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "v4l2-subdev-priv.h"
+
static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
struct v4l2_subdev *subdev,
- struct v4l2_async_subdev *asd)
+ struct v4l2_async_connection *asc)
{
if (!n->ops || !n->ops->bound)
return 0;
- return n->ops->bound(n, subdev, asd);
+ return n->ops->bound(n, subdev, asc);
}
static void v4l2_async_nf_call_unbind(struct v4l2_async_notifier *n,
struct v4l2_subdev *subdev,
- struct v4l2_async_subdev *asd)
+ struct v4l2_async_connection *asc)
{
if (!n->ops || !n->ops->unbind)
return;
- n->ops->unbind(n, subdev, asd);
+ n->ops->unbind(n, subdev, asc);
}
static int v4l2_async_nf_call_complete(struct v4l2_async_notifier *n)
@@ -52,116 +54,143 @@ static int v4l2_async_nf_call_complete(struct v4l2_async_notifier *n)
return n->ops->complete(n);
}
+static void v4l2_async_nf_call_destroy(struct v4l2_async_notifier *n,
+ struct v4l2_async_connection *asc)
+{
+ if (!n->ops || !n->ops->destroy)
+ return;
+
+ n->ops->destroy(asc);
+}
+
static bool match_i2c(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
+ struct v4l2_subdev *sd,
+ struct v4l2_async_match_desc *match)
{
#if IS_ENABLED(CONFIG_I2C)
struct i2c_client *client = i2c_verify_client(sd->dev);
return client &&
- asd->match.i2c.adapter_id == client->adapter->nr &&
- asd->match.i2c.address == client->addr;
+ match->i2c.adapter_id == client->adapter->nr &&
+ match->i2c.address == client->addr;
#else
return false;
#endif
}
-static bool match_fwnode(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
+static struct device *notifier_dev(struct v4l2_async_notifier *notifier)
{
- struct fwnode_handle *other_fwnode;
- struct fwnode_handle *dev_fwnode;
- bool asd_fwnode_is_ep;
- bool sd_fwnode_is_ep;
- struct device *dev;
+ if (notifier->sd)
+ return notifier->sd->dev;
- /*
- * Both the subdev and the async subdev can provide either an endpoint
- * fwnode or a device fwnode. Start with the simple case of direct
- * fwnode matching.
- */
- if (sd->fwnode == asd->match.fwnode)
- return true;
+ if (notifier->v4l2_dev)
+ return notifier->v4l2_dev->dev;
- /*
- * Check the same situation for any possible secondary assigned to the
- * subdev's fwnode
- */
- if (!IS_ERR_OR_NULL(sd->fwnode->secondary) &&
- sd->fwnode->secondary == asd->match.fwnode)
- return true;
+ return NULL;
+}
- /*
- * Otherwise, check if the sd fwnode and the asd fwnode refer to an
- * endpoint or a device. If they're of the same type, there's no match.
- * Technically speaking this checks if the nodes refer to a connected
- * endpoint, which is the simplest check that works for both OF and
- * ACPI. This won't make a difference, as drivers should not try to
- * match unconnected endpoints.
- */
- sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd->fwnode);
- asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode);
+static bool
+match_fwnode_one(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd, struct fwnode_handle *sd_fwnode,
+ struct v4l2_async_match_desc *match)
+{
+ struct fwnode_handle *asd_dev_fwnode;
+ bool ret;
+
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: fwnode match: need %pfw, trying %pfw\n",
+ sd_fwnode, match->fwnode);
+
+ if (sd_fwnode == match->fwnode) {
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: direct match found\n");
+ return true;
+ }
- if (sd_fwnode_is_ep == asd_fwnode_is_ep)
+ if (!fwnode_graph_is_endpoint(match->fwnode)) {
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: direct match not found\n");
return false;
+ }
- /*
- * The sd and asd fwnodes are of different types. Get the device fwnode
- * parent of the endpoint fwnode, and compare it with the other fwnode.
- */
- if (sd_fwnode_is_ep) {
- dev_fwnode = fwnode_graph_get_port_parent(sd->fwnode);
- other_fwnode = asd->match.fwnode;
- } else {
- dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode);
- other_fwnode = sd->fwnode;
+ asd_dev_fwnode = fwnode_graph_get_port_parent(match->fwnode);
+
+ ret = sd_fwnode == asd_dev_fwnode;
+
+ fwnode_handle_put(asd_dev_fwnode);
+
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: device--endpoint match %sfound\n",
+ ret ? "" : "not ");
+
+ return ret;
+}
+
+static bool match_fwnode(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_match_desc *match)
+{
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: matching for notifier %pfw, sd fwnode %pfw\n",
+ dev_fwnode(notifier_dev(notifier)), sd->fwnode);
+
+ if (!list_empty(&sd->async_subdev_endpoint_list)) {
+ struct v4l2_async_subdev_endpoint *ase;
+
+ dev_dbg(sd->dev,
+ "v4l2-async: endpoint fwnode list available, looking for %pfw\n",
+ match->fwnode);
+
+ list_for_each_entry(ase, &sd->async_subdev_endpoint_list,
+ async_subdev_endpoint_entry) {
+ bool matched = ase->endpoint == match->fwnode;
+
+ dev_dbg(sd->dev,
+ "v4l2-async: endpoint-endpoint match %sfound with %pfw\n",
+ matched ? "" : "not ", ase->endpoint);
+
+ if (matched)
+ return true;
+ }
+
+ dev_dbg(sd->dev, "async: no endpoint matched\n");
+
+ return false;
}
- fwnode_handle_put(dev_fwnode);
+ if (match_fwnode_one(notifier, sd, sd->fwnode, match))
+ return true;
- if (dev_fwnode != other_fwnode)
+ /* Also check the secondary fwnode. */
+ if (IS_ERR_OR_NULL(sd->fwnode->secondary))
return false;
- /*
- * We have a heterogeneous match. Retrieve the struct device of the side
- * that matched on a device fwnode to print its driver name.
- */
- if (sd_fwnode_is_ep)
- dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev
- : notifier->sd->dev;
- else
- dev = sd->dev;
-
- if (dev && dev->driver) {
- if (sd_fwnode_is_ep)
- dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n",
- dev->driver->name);
- dev_notice(dev, "Consider updating driver %s to match on endpoints\n",
- dev->driver->name);
- }
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: trying secondary fwnode match\n");
- return true;
+ return match_fwnode_one(notifier, sd, sd->fwnode->secondary, match);
}
static LIST_HEAD(subdev_list);
static LIST_HEAD(notifier_list);
static DEFINE_MUTEX(list_lock);
-static struct v4l2_async_subdev *
+static struct v4l2_async_connection *
v4l2_async_find_match(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd)
{
bool (*match)(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd, struct v4l2_async_subdev *asd);
- struct v4l2_async_subdev *asd;
+ struct v4l2_subdev *sd,
+ struct v4l2_async_match_desc *match);
+ struct v4l2_async_connection *asc;
- list_for_each_entry(asd, &notifier->waiting, list) {
+ list_for_each_entry(asc, &notifier->waiting_list, asc_entry) {
/* bus_type has been verified valid before */
- switch (asd->match_type) {
- case V4L2_ASYNC_MATCH_I2C:
+ switch (asc->match.type) {
+ case V4L2_ASYNC_MATCH_TYPE_I2C:
match = match_i2c;
break;
- case V4L2_ASYNC_MATCH_FWNODE:
+ case V4L2_ASYNC_MATCH_TYPE_FWNODE:
match = match_fwnode;
break;
default:
@@ -171,28 +200,26 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier,
}
/* match cannot be NULL here */
- if (match(notifier, sd, asd))
- return asd;
+ if (match(notifier, sd, &asc->match))
+ return asc;
}
return NULL;
}
-/* Compare two async sub-device descriptors for equivalence */
-static bool asd_equal(struct v4l2_async_subdev *asd_x,
- struct v4l2_async_subdev *asd_y)
+/* Compare two async match descriptors for equivalence */
+static bool v4l2_async_match_equal(struct v4l2_async_match_desc *match1,
+ struct v4l2_async_match_desc *match2)
{
- if (asd_x->match_type != asd_y->match_type)
+ if (match1->type != match2->type)
return false;
- switch (asd_x->match_type) {
- case V4L2_ASYNC_MATCH_I2C:
- return asd_x->match.i2c.adapter_id ==
- asd_y->match.i2c.adapter_id &&
- asd_x->match.i2c.address ==
- asd_y->match.i2c.address;
- case V4L2_ASYNC_MATCH_FWNODE:
- return asd_x->match.fwnode == asd_y->match.fwnode;
+ switch (match1->type) {
+ case V4L2_ASYNC_MATCH_TYPE_I2C:
+ return match1->i2c.adapter_id == match2->i2c.adapter_id &&
+ match1->i2c.address == match2->i2c.address;
+ case V4L2_ASYNC_MATCH_TYPE_FWNODE:
+ return match1->fwnode == match2->fwnode;
default:
break;
}
@@ -206,7 +233,7 @@ v4l2_async_find_subdev_notifier(struct v4l2_subdev *sd)
{
struct v4l2_async_notifier *n;
- list_for_each_entry(n, &notifier_list, list)
+ list_for_each_entry(n, &notifier_list, notifier_entry)
if (n->sd == sd)
return n;
@@ -229,14 +256,14 @@ v4l2_async_nf_find_v4l2_dev(struct v4l2_async_notifier *notifier)
static bool
v4l2_async_nf_can_complete(struct v4l2_async_notifier *notifier)
{
- struct v4l2_subdev *sd;
+ struct v4l2_async_connection *asc;
- if (!list_empty(&notifier->waiting))
+ if (!list_empty(&notifier->waiting_list))
return false;
- list_for_each_entry(sd, &notifier->done, async_list) {
+ list_for_each_entry(asc, &notifier->done_list, asc_entry) {
struct v4l2_async_notifier *subdev_notifier =
- v4l2_async_find_subdev_notifier(sd);
+ v4l2_async_find_subdev_notifier(asc->sd);
if (subdev_notifier &&
!v4l2_async_nf_can_complete(subdev_notifier))
@@ -253,53 +280,114 @@ v4l2_async_nf_can_complete(struct v4l2_async_notifier *notifier)
static int
v4l2_async_nf_try_complete(struct v4l2_async_notifier *notifier)
{
+ struct v4l2_async_notifier *__notifier = notifier;
+
/* Quick check whether there are still more sub-devices here. */
- if (!list_empty(&notifier->waiting))
+ if (!list_empty(&notifier->waiting_list))
return 0;
+ if (notifier->sd)
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: trying to complete\n");
+
/* Check the entire notifier tree; find the root notifier first. */
while (notifier->parent)
notifier = notifier->parent;
/* This is root if it has v4l2_dev. */
- if (!notifier->v4l2_dev)
+ if (!notifier->v4l2_dev) {
+ dev_dbg(notifier_dev(__notifier),
+ "v4l2-async: V4L2 device not available\n");
return 0;
+ }
/* Is everything ready? */
if (!v4l2_async_nf_can_complete(notifier))
return 0;
+ dev_dbg(notifier_dev(__notifier), "v4l2-async: complete\n");
+
return v4l2_async_nf_call_complete(notifier);
}
static int
v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier);
+static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n,
+ struct v4l2_subdev *sd)
+{
+#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER)
+ struct media_link *link;
+
+ if (sd->entity.function != MEDIA_ENT_F_LENS &&
+ sd->entity.function != MEDIA_ENT_F_FLASH)
+ return 0;
+
+ if (!n->sd) {
+ dev_warn(notifier_dev(n),
+ "not a sub-device notifier, not creating an ancillary link for %s!\n",
+ dev_name(sd->dev));
+ return 0;
+ }
+
+ link = media_create_ancillary_link(&n->sd->entity, &sd->entity);
+
+ return IS_ERR(link) ? PTR_ERR(link) : 0;
+#else
+ return 0;
+#endif
+}
+
static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd,
- struct v4l2_async_subdev *asd)
+ struct v4l2_async_connection *asc)
{
struct v4l2_async_notifier *subdev_notifier;
+ bool registered = false;
int ret;
- ret = v4l2_device_register_subdev(v4l2_dev, sd);
- if (ret < 0)
- return ret;
+ if (list_empty(&sd->asc_list)) {
+ ret = __v4l2_device_register_subdev(v4l2_dev, sd, sd->owner);
+ if (ret < 0)
+ return ret;
+ registered = true;
+ }
- ret = v4l2_async_nf_call_bound(notifier, sd, asd);
+ ret = v4l2_async_nf_call_bound(notifier, sd, asc);
if (ret < 0) {
- v4l2_device_unregister_subdev(sd);
- return ret;
+ if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE)
+ dev_dbg(notifier_dev(notifier),
+ "failed binding %pfw (%d)\n",
+ asc->match.fwnode, ret);
+ goto err_unregister_subdev;
}
- /* Remove from the waiting list */
- list_del(&asd->list);
- sd->asd = asd;
- sd->notifier = notifier;
+ if (registered) {
+ /*
+ * Depending of the function of the entities involved, we may
+ * want to create links between them (for example between a
+ * sensor and its lens or between a sensor's source pad and the
+ * connected device's sink pad).
+ */
+ ret = v4l2_async_create_ancillary_links(notifier, sd);
+ if (ret) {
+ if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE)
+ dev_dbg(notifier_dev(notifier),
+ "failed creating links for %pfw (%d)\n",
+ asc->match.fwnode, ret);
+ goto err_call_unbind;
+ }
+ }
+
+ list_add(&asc->asc_subdev_entry, &sd->asc_list);
+ asc->sd = sd;
+
+ /* Move from the waiting list to notifier's done */
+ list_move(&asc->asc_entry, &notifier->done_list);
- /* Move from the global subdevice list to notifier's done */
- list_move(&sd->async_list, &notifier->done);
+ dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n",
+ dev_name(sd->dev), ret);
/*
* See if the sub-device has a notifier. If not, return here.
@@ -316,6 +404,16 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
subdev_notifier->parent = notifier;
return v4l2_async_nf_try_all_subdevs(subdev_notifier);
+
+err_call_unbind:
+ v4l2_async_nf_call_unbind(notifier, sd, asc);
+ list_del(&asc->asc_subdev_entry);
+
+err_unregister_subdev:
+ if (registered)
+ v4l2_device_unregister_subdev(sd);
+
+ return ret;
}
/* Test all async sub-devices in a notifier for a match. */
@@ -329,16 +427,21 @@ v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier)
if (!v4l2_dev)
return 0;
+ dev_dbg(notifier_dev(notifier), "v4l2-async: trying all sub-devices\n");
+
again:
list_for_each_entry(sd, &subdev_list, async_list) {
- struct v4l2_async_subdev *asd;
+ struct v4l2_async_connection *asc;
int ret;
- asd = v4l2_async_find_match(notifier, sd);
- if (!asd)
+ asc = v4l2_async_find_match(notifier, sd);
+ if (!asc)
continue;
- ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
+ dev_dbg(notifier_dev(notifier),
+ "v4l2-async: match found, subdev %s\n", sd->name);
+
+ ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asc);
if (ret < 0)
return ret;
@@ -354,34 +457,33 @@ again:
return 0;
}
-static void v4l2_async_cleanup(struct v4l2_subdev *sd)
+static void v4l2_async_unbind_subdev_one(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_connection *asc)
{
- v4l2_device_unregister_subdev(sd);
- /*
- * Subdevice driver will reprobe and put the subdev back
- * onto the list
- */
- list_del_init(&sd->async_list);
- sd->asd = NULL;
+ list_move_tail(&asc->asc_entry, &notifier->waiting_list);
+ if (list_is_singular(&asc->asc_subdev_entry)) {
+ v4l2_async_nf_call_unbind(notifier, asc->sd, asc);
+ v4l2_device_unregister_subdev(asc->sd);
+ asc->sd = NULL;
+ }
+ list_del(&asc->asc_subdev_entry);
}
/* Unbind all sub-devices in the notifier tree. */
static void
v4l2_async_nf_unbind_all_subdevs(struct v4l2_async_notifier *notifier)
{
- struct v4l2_subdev *sd, *tmp;
+ struct v4l2_async_connection *asc, *asc_tmp;
- list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
+ list_for_each_entry_safe(asc, asc_tmp, &notifier->done_list,
+ asc_entry) {
struct v4l2_async_notifier *subdev_notifier =
- v4l2_async_find_subdev_notifier(sd);
+ v4l2_async_find_subdev_notifier(asc->sd);
if (subdev_notifier)
v4l2_async_nf_unbind_all_subdevs(subdev_notifier);
- v4l2_async_nf_call_unbind(notifier, sd, sd->asd);
- v4l2_async_cleanup(sd);
-
- list_move(&sd->async_list, &subdev_list);
+ v4l2_async_unbind_subdev_one(notifier, asc);
}
notifier->parent = NULL;
@@ -389,106 +491,111 @@ v4l2_async_nf_unbind_all_subdevs(struct v4l2_async_notifier *notifier)
/* See if an async sub-device can be found in a notifier's lists. */
static bool
-__v4l2_async_nf_has_async_subdev(struct v4l2_async_notifier *notifier,
- struct v4l2_async_subdev *asd)
+v4l2_async_nf_has_async_match_entry(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_match_desc *match)
{
- struct v4l2_async_subdev *asd_y;
- struct v4l2_subdev *sd;
+ struct v4l2_async_connection *asc;
- list_for_each_entry(asd_y, &notifier->waiting, list)
- if (asd_equal(asd, asd_y))
+ list_for_each_entry(asc, &notifier->waiting_list, asc_entry)
+ if (v4l2_async_match_equal(&asc->match, match))
return true;
- list_for_each_entry(sd, &notifier->done, async_list) {
- if (WARN_ON(!sd->asd))
- continue;
-
- if (asd_equal(asd, sd->asd))
+ list_for_each_entry(asc, &notifier->done_list, asc_entry)
+ if (v4l2_async_match_equal(&asc->match, match))
return true;
- }
return false;
}
/*
- * Find out whether an async sub-device was set up already or
- * whether it exists in a given notifier before @this_index.
- * If @this_index < 0, search the notifier's entire @asd_list.
+ * Find out whether an async sub-device was set up already or whether it exists
+ * in a given notifier.
*/
static bool
-v4l2_async_nf_has_async_subdev(struct v4l2_async_notifier *notifier,
- struct v4l2_async_subdev *asd, int this_index)
+v4l2_async_nf_has_async_match(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_match_desc *match)
{
- struct v4l2_async_subdev *asd_y;
- int j = 0;
+ struct list_head *heads[] = {
+ &notifier->waiting_list,
+ &notifier->done_list,
+ };
+ unsigned int i;
lockdep_assert_held(&list_lock);
/* Check that an asd is not being added more than once. */
- list_for_each_entry(asd_y, &notifier->asd_list, asd_list) {
- if (this_index >= 0 && j++ >= this_index)
- break;
- if (asd_equal(asd, asd_y))
- return true;
+ for (i = 0; i < ARRAY_SIZE(heads); i++) {
+ struct v4l2_async_connection *asc;
+
+ list_for_each_entry(asc, heads[i], asc_entry) {
+ if (&asc->match == match)
+ continue;
+ if (v4l2_async_match_equal(&asc->match, match))
+ return true;
+ }
}
- /* Check that an asd does not exist in other notifiers. */
- list_for_each_entry(notifier, &notifier_list, list)
- if (__v4l2_async_nf_has_async_subdev(notifier, asd))
+ /* Check that an asc does not exist in other notifiers. */
+ list_for_each_entry(notifier, &notifier_list, notifier_entry)
+ if (v4l2_async_nf_has_async_match_entry(notifier, match))
return true;
return false;
}
-static int v4l2_async_nf_asd_valid(struct v4l2_async_notifier *notifier,
- struct v4l2_async_subdev *asd,
- int this_index)
+static int v4l2_async_nf_match_valid(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_match_desc *match)
{
- struct device *dev =
- notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL;
+ struct device *dev = notifier_dev(notifier);
- if (!asd)
- return -EINVAL;
-
- switch (asd->match_type) {
- case V4L2_ASYNC_MATCH_I2C:
- case V4L2_ASYNC_MATCH_FWNODE:
- if (v4l2_async_nf_has_async_subdev(notifier, asd, this_index)) {
- dev_dbg(dev, "subdev descriptor already listed in this or other notifiers\n");
+ switch (match->type) {
+ case V4L2_ASYNC_MATCH_TYPE_I2C:
+ case V4L2_ASYNC_MATCH_TYPE_FWNODE:
+ if (v4l2_async_nf_has_async_match(notifier, match)) {
+ dev_dbg(dev, "v4l2-async: match descriptor already listed in a notifier\n");
return -EEXIST;
}
break;
default:
- dev_err(dev, "Invalid match type %u on %p\n",
- asd->match_type, asd);
+ dev_err(dev, "v4l2-async: Invalid match type %u on %p\n",
+ match->type, match);
return -EINVAL;
}
return 0;
}
-void v4l2_async_nf_init(struct v4l2_async_notifier *notifier)
+void v4l2_async_nf_init(struct v4l2_async_notifier *notifier,
+ struct v4l2_device *v4l2_dev)
{
- INIT_LIST_HEAD(&notifier->asd_list);
+ INIT_LIST_HEAD(&notifier->waiting_list);
+ INIT_LIST_HEAD(&notifier->done_list);
+ INIT_LIST_HEAD(&notifier->notifier_entry);
+ notifier->v4l2_dev = v4l2_dev;
}
EXPORT_SYMBOL(v4l2_async_nf_init);
-static int __v4l2_async_nf_register(struct v4l2_async_notifier *notifier)
+void v4l2_async_subdev_nf_init(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd)
{
- struct v4l2_async_subdev *asd;
- int ret, i = 0;
+ INIT_LIST_HEAD(&notifier->waiting_list);
+ INIT_LIST_HEAD(&notifier->done_list);
+ INIT_LIST_HEAD(&notifier->notifier_entry);
+ notifier->sd = sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_async_subdev_nf_init);
- INIT_LIST_HEAD(&notifier->waiting);
- INIT_LIST_HEAD(&notifier->done);
+static int __v4l2_async_nf_register(struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_async_connection *asc;
+ int ret;
mutex_lock(&list_lock);
- list_for_each_entry(asd, &notifier->asd_list, asd_list) {
- ret = v4l2_async_nf_asd_valid(notifier, asd, i++);
+ list_for_each_entry(asc, &notifier->waiting_list, asc_entry) {
+ ret = v4l2_async_nf_match_valid(notifier, &asc->match);
if (ret)
goto err_unlock;
-
- list_add_tail(&asd->list, &notifier->waiting);
}
ret = v4l2_async_nf_try_all_subdevs(notifier);
@@ -500,7 +607,7 @@ static int __v4l2_async_nf_register(struct v4l2_async_notifier *notifier)
goto err_unbind;
/* Keep also completed notifiers on the list */
- list_add(&notifier->list, &notifier_list);
+ list_add(&notifier->notifier_entry, &notifier_list);
mutex_unlock(&list_lock);
@@ -518,42 +625,15 @@ err_unlock:
return ret;
}
-int v4l2_async_nf_register(struct v4l2_device *v4l2_dev,
- struct v4l2_async_notifier *notifier)
+int v4l2_async_nf_register(struct v4l2_async_notifier *notifier)
{
- int ret;
-
- if (WARN_ON(!v4l2_dev || notifier->sd))
+ if (WARN_ON(!notifier->v4l2_dev == !notifier->sd))
return -EINVAL;
- notifier->v4l2_dev = v4l2_dev;
-
- ret = __v4l2_async_nf_register(notifier);
- if (ret)
- notifier->v4l2_dev = NULL;
-
- return ret;
+ return __v4l2_async_nf_register(notifier);
}
EXPORT_SYMBOL(v4l2_async_nf_register);
-int v4l2_async_subdev_nf_register(struct v4l2_subdev *sd,
- struct v4l2_async_notifier *notifier)
-{
- int ret;
-
- if (WARN_ON(!sd || notifier->v4l2_dev))
- return -EINVAL;
-
- notifier->sd = sd;
-
- ret = __v4l2_async_nf_register(notifier);
- if (ret)
- notifier->sd = NULL;
-
- return ret;
-}
-EXPORT_SYMBOL(v4l2_async_subdev_nf_register);
-
static void
__v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier)
{
@@ -562,10 +642,7 @@ __v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier)
v4l2_async_nf_unbind_all_subdevs(notifier);
- notifier->sd = NULL;
- notifier->v4l2_dev = NULL;
-
- list_del(&notifier->list);
+ list_del_init(&notifier->notifier_entry);
}
void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier)
@@ -580,23 +657,25 @@ EXPORT_SYMBOL(v4l2_async_nf_unregister);
static void __v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier)
{
- struct v4l2_async_subdev *asd, *tmp;
+ struct v4l2_async_connection *asc, *tmp;
- if (!notifier || !notifier->asd_list.next)
+ if (!notifier || !notifier->waiting_list.next)
return;
- list_for_each_entry_safe(asd, tmp, &notifier->asd_list, asd_list) {
- switch (asd->match_type) {
- case V4L2_ASYNC_MATCH_FWNODE:
- fwnode_handle_put(asd->match.fwnode);
- break;
- default:
- break;
- }
+ WARN_ON(!list_empty(&notifier->done_list));
+
+ list_for_each_entry_safe(asc, tmp, &notifier->waiting_list, asc_entry) {
+ list_del(&asc->asc_entry);
+ v4l2_async_nf_call_destroy(notifier, asc);
- list_del(&asd->asd_list);
- kfree(asd);
+ if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE)
+ fwnode_handle_put(asc->match.fwnode);
+
+ kfree(asc);
}
+
+ notifier->sd = NULL;
+ notifier->v4l2_dev = NULL;
}
void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier)
@@ -609,143 +688,158 @@ void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier)
}
EXPORT_SYMBOL_GPL(v4l2_async_nf_cleanup);
-int __v4l2_async_nf_add_subdev(struct v4l2_async_notifier *notifier,
- struct v4l2_async_subdev *asd)
+static void __v4l2_async_nf_add_connection(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_connection *asc)
{
- int ret;
-
mutex_lock(&list_lock);
- ret = v4l2_async_nf_asd_valid(notifier, asd, -1);
- if (ret)
- goto unlock;
+ list_add_tail(&asc->asc_entry, &notifier->waiting_list);
- list_add_tail(&asd->asd_list, &notifier->asd_list);
-
-unlock:
mutex_unlock(&list_lock);
- return ret;
}
-EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_subdev);
-struct v4l2_async_subdev *
+struct v4l2_async_connection *
__v4l2_async_nf_add_fwnode(struct v4l2_async_notifier *notifier,
struct fwnode_handle *fwnode,
- unsigned int asd_struct_size)
+ unsigned int asc_struct_size)
{
- struct v4l2_async_subdev *asd;
- int ret;
+ struct v4l2_async_connection *asc;
- asd = kzalloc(asd_struct_size, GFP_KERNEL);
- if (!asd)
+ asc = kzalloc(asc_struct_size, GFP_KERNEL);
+ if (!asc)
return ERR_PTR(-ENOMEM);
- asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
- asd->match.fwnode = fwnode_handle_get(fwnode);
+ asc->notifier = notifier;
+ asc->match.type = V4L2_ASYNC_MATCH_TYPE_FWNODE;
+ asc->match.fwnode = fwnode_handle_get(fwnode);
- ret = __v4l2_async_nf_add_subdev(notifier, asd);
- if (ret) {
- fwnode_handle_put(fwnode);
- kfree(asd);
- return ERR_PTR(ret);
- }
+ __v4l2_async_nf_add_connection(notifier, asc);
- return asd;
+ return asc;
}
EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_fwnode);
-struct v4l2_async_subdev *
+struct v4l2_async_connection *
__v4l2_async_nf_add_fwnode_remote(struct v4l2_async_notifier *notif,
struct fwnode_handle *endpoint,
- unsigned int asd_struct_size)
+ unsigned int asc_struct_size)
{
- struct v4l2_async_subdev *asd;
+ struct v4l2_async_connection *asc;
struct fwnode_handle *remote;
- remote = fwnode_graph_get_remote_port_parent(endpoint);
+ remote = fwnode_graph_get_remote_endpoint(endpoint);
if (!remote)
return ERR_PTR(-ENOTCONN);
- asd = __v4l2_async_nf_add_fwnode(notif, remote, asd_struct_size);
+ asc = __v4l2_async_nf_add_fwnode(notif, remote, asc_struct_size);
/*
* Calling __v4l2_async_nf_add_fwnode grabs a refcount,
* so drop the one we got in fwnode_graph_get_remote_port_parent.
*/
fwnode_handle_put(remote);
- return asd;
+ return asc;
}
EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_fwnode_remote);
-struct v4l2_async_subdev *
+struct v4l2_async_connection *
__v4l2_async_nf_add_i2c(struct v4l2_async_notifier *notifier, int adapter_id,
- unsigned short address, unsigned int asd_struct_size)
+ unsigned short address, unsigned int asc_struct_size)
{
- struct v4l2_async_subdev *asd;
- int ret;
+ struct v4l2_async_connection *asc;
- asd = kzalloc(asd_struct_size, GFP_KERNEL);
- if (!asd)
+ asc = kzalloc(asc_struct_size, GFP_KERNEL);
+ if (!asc)
return ERR_PTR(-ENOMEM);
- asd->match_type = V4L2_ASYNC_MATCH_I2C;
- asd->match.i2c.adapter_id = adapter_id;
- asd->match.i2c.address = address;
+ asc->notifier = notifier;
+ asc->match.type = V4L2_ASYNC_MATCH_TYPE_I2C;
+ asc->match.i2c.adapter_id = adapter_id;
+ asc->match.i2c.address = address;
- ret = __v4l2_async_nf_add_subdev(notifier, asd);
- if (ret) {
- kfree(asd);
- return ERR_PTR(ret);
- }
+ __v4l2_async_nf_add_connection(notifier, asc);
- return asd;
+ return asc;
}
EXPORT_SYMBOL_GPL(__v4l2_async_nf_add_i2c);
-int v4l2_async_register_subdev(struct v4l2_subdev *sd)
+int v4l2_async_subdev_endpoint_add(struct v4l2_subdev *sd,
+ struct fwnode_handle *fwnode)
+{
+ struct v4l2_async_subdev_endpoint *ase;
+
+ ase = kmalloc(sizeof(*ase), GFP_KERNEL);
+ if (!ase)
+ return -ENOMEM;
+
+ ase->endpoint = fwnode;
+ list_add(&ase->async_subdev_endpoint_entry,
+ &sd->async_subdev_endpoint_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_async_subdev_endpoint_add);
+
+struct v4l2_async_connection *
+v4l2_async_connection_unique(struct v4l2_subdev *sd)
+{
+ if (!list_is_singular(&sd->asc_list))
+ return NULL;
+
+ return list_first_entry(&sd->asc_list,
+ struct v4l2_async_connection, asc_subdev_entry);
+}
+EXPORT_SYMBOL_GPL(v4l2_async_connection_unique);
+
+int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module)
{
struct v4l2_async_notifier *subdev_notifier;
struct v4l2_async_notifier *notifier;
+ struct v4l2_async_connection *asc;
int ret;
+ INIT_LIST_HEAD(&sd->asc_list);
+
/*
- * No reference taken. The reference is held by the device
- * (struct v4l2_subdev.dev), and async sub-device does not
- * exist independently of the device at any point of time.
+ * No reference taken. The reference is held by the device (struct
+ * v4l2_subdev.dev), and async sub-device does not exist independently
+ * of the device at any point of time.
+ *
+ * The async sub-device shall always be registered for its device node,
+ * not the endpoint node.
*/
- if (!sd->fwnode && sd->dev)
+ if (!sd->fwnode && sd->dev) {
sd->fwnode = dev_fwnode(sd->dev);
+ } else if (fwnode_graph_is_endpoint(sd->fwnode)) {
+ dev_warn(sd->dev, "sub-device fwnode is an endpoint!\n");
+ return -EINVAL;
+ }
- mutex_lock(&list_lock);
+ sd->owner = module;
- INIT_LIST_HEAD(&sd->async_list);
+ mutex_lock(&list_lock);
- list_for_each_entry(notifier, &notifier_list, list) {
+ list_for_each_entry(notifier, &notifier_list, notifier_entry) {
struct v4l2_device *v4l2_dev =
v4l2_async_nf_find_v4l2_dev(notifier);
- struct v4l2_async_subdev *asd;
if (!v4l2_dev)
continue;
- asd = v4l2_async_find_match(notifier, sd);
- if (!asd)
- continue;
+ while ((asc = v4l2_async_find_match(notifier, sd))) {
+ ret = v4l2_async_match_notify(notifier, v4l2_dev, sd,
+ asc);
+ if (ret)
+ goto err_unbind;
- ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd);
- if (ret)
- goto err_unbind;
-
- ret = v4l2_async_nf_try_complete(notifier);
- if (ret)
- goto err_unbind;
-
- goto out_unlock;
+ ret = v4l2_async_nf_try_complete(notifier);
+ if (ret)
+ goto err_unbind;
+ }
}
/* None matched, wait for hot-plugging */
list_add(&sd->async_list, &subdev_list);
-out_unlock:
mutex_unlock(&list_lock);
return 0;
@@ -759,21 +853,26 @@ err_unbind:
if (subdev_notifier)
v4l2_async_nf_unbind_all_subdevs(subdev_notifier);
- if (sd->asd)
- v4l2_async_nf_call_unbind(notifier, sd, sd->asd);
- v4l2_async_cleanup(sd);
+ if (asc)
+ v4l2_async_unbind_subdev_one(notifier, asc);
mutex_unlock(&list_lock);
+ sd->owner = NULL;
+
return ret;
}
-EXPORT_SYMBOL(v4l2_async_register_subdev);
+EXPORT_SYMBOL(__v4l2_async_register_subdev);
void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
{
+ struct v4l2_async_connection *asc, *asc_tmp;
+
if (!sd->async_list.next)
return;
+ v4l2_subdev_put_privacy_led(sd);
+
mutex_lock(&list_lock);
__v4l2_async_nf_unregister(sd->subdev_notifier);
@@ -781,30 +880,30 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
kfree(sd->subdev_notifier);
sd->subdev_notifier = NULL;
- if (sd->asd) {
- struct v4l2_async_notifier *notifier = sd->notifier;
-
- list_add(&sd->asd->list, &notifier->waiting);
-
- v4l2_async_nf_call_unbind(notifier, sd, sd->asd);
+ if (sd->asc_list.next) {
+ list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list,
+ asc_subdev_entry) {
+ v4l2_async_unbind_subdev_one(asc->notifier, asc);
+ }
}
- v4l2_async_cleanup(sd);
+ list_del(&sd->async_list);
+ sd->async_list.next = NULL;
mutex_unlock(&list_lock);
}
EXPORT_SYMBOL(v4l2_async_unregister_subdev);
-static void print_waiting_subdev(struct seq_file *s,
- struct v4l2_async_subdev *asd)
+static void print_waiting_match(struct seq_file *s,
+ struct v4l2_async_match_desc *match)
{
- switch (asd->match_type) {
- case V4L2_ASYNC_MATCH_I2C:
- seq_printf(s, " [i2c] dev=%d-%04x\n", asd->match.i2c.adapter_id,
- asd->match.i2c.address);
+ switch (match->type) {
+ case V4L2_ASYNC_MATCH_TYPE_I2C:
+ seq_printf(s, " [i2c] dev=%d-%04x\n", match->i2c.adapter_id,
+ match->i2c.address);
break;
- case V4L2_ASYNC_MATCH_FWNODE: {
- struct fwnode_handle *devnode, *fwnode = asd->match.fwnode;
+ case V4L2_ASYNC_MATCH_TYPE_FWNODE: {
+ struct fwnode_handle *devnode, *fwnode = match->fwnode;
devnode = fwnode_graph_is_endpoint(fwnode) ?
fwnode_graph_get_port_parent(fwnode) :
@@ -834,14 +933,14 @@ v4l2_async_nf_name(struct v4l2_async_notifier *notifier)
static int pending_subdevs_show(struct seq_file *s, void *data)
{
struct v4l2_async_notifier *notif;
- struct v4l2_async_subdev *asd;
+ struct v4l2_async_connection *asc;
mutex_lock(&list_lock);
- list_for_each_entry(notif, &notifier_list, list) {
+ list_for_each_entry(notif, &notifier_list, notifier_entry) {
seq_printf(s, "%s:\n", v4l2_async_nf_name(notif));
- list_for_each_entry(asd, &notif->waiting, list)
- print_waiting_subdev(s, asd);
+ list_for_each_entry(asc, &notif->waiting_list, asc_entry)
+ print_waiting_match(s, &asc->match);
}
mutex_unlock(&list_lock);
@@ -873,4 +972,5 @@ module_exit(v4l2_async_exit);
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
+MODULE_DESCRIPTION("V4L2 asynchronous subdevice registration API");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c
new file mode 100644
index 000000000000..e9ecf4785946
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-cci.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MIPI Camera Control Interface (CCI) register access helpers.
+ *
+ * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <linux/unaligned.h>
+
+#include <media/v4l2-cci.h>
+
+int cci_read(struct regmap *map, u32 reg, u64 *val, int *err)
+{
+ bool little_endian;
+ unsigned int len;
+ u8 buf[8];
+ int ret;
+
+ /*
+ * TODO: Fix smatch. Assign *val to 0 here in order to avoid
+ * failing a smatch check on caller when the caller proceeds to
+ * read *val without initialising it on caller's side. *val is set
+ * to a valid value whenever this function returns 0 but smatch
+ * can't figure that out currently.
+ */
+ *val = 0;
+
+ if (err && *err)
+ return *err;
+
+ little_endian = reg & CCI_REG_LE;
+ len = CCI_REG_WIDTH_BYTES(reg);
+ reg = CCI_REG_ADDR(reg);
+
+ ret = regmap_bulk_read(map, reg, buf, len);
+ if (ret) {
+ dev_err(regmap_get_device(map), "Error reading reg 0x%04x: %d\n",
+ reg, ret);
+ goto out;
+ }
+
+ switch (len) {
+ case 1:
+ *val = buf[0];
+ break;
+ case 2:
+ if (little_endian)
+ *val = get_unaligned_le16(buf);
+ else
+ *val = get_unaligned_be16(buf);
+ break;
+ case 3:
+ if (little_endian)
+ *val = get_unaligned_le24(buf);
+ else
+ *val = get_unaligned_be24(buf);
+ break;
+ case 4:
+ if (little_endian)
+ *val = get_unaligned_le32(buf);
+ else
+ *val = get_unaligned_be32(buf);
+ break;
+ case 8:
+ if (little_endian)
+ *val = get_unaligned_le64(buf);
+ else
+ *val = get_unaligned_be64(buf);
+ break;
+ default:
+ dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
+ len, reg);
+ ret = -EINVAL;
+ break;
+ }
+
+out:
+ if (ret && err)
+ *err = ret;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cci_read);
+
+int cci_write(struct regmap *map, u32 reg, u64 val, int *err)
+{
+ bool little_endian;
+ unsigned int len;
+ u8 buf[8];
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ little_endian = reg & CCI_REG_LE;
+ len = CCI_REG_WIDTH_BYTES(reg);
+ reg = CCI_REG_ADDR(reg);
+
+ switch (len) {
+ case 1:
+ buf[0] = val;
+ break;
+ case 2:
+ if (little_endian)
+ put_unaligned_le16(val, buf);
+ else
+ put_unaligned_be16(val, buf);
+ break;
+ case 3:
+ if (little_endian)
+ put_unaligned_le24(val, buf);
+ else
+ put_unaligned_be24(val, buf);
+ break;
+ case 4:
+ if (little_endian)
+ put_unaligned_le32(val, buf);
+ else
+ put_unaligned_be32(val, buf);
+ break;
+ case 8:
+ if (little_endian)
+ put_unaligned_le64(val, buf);
+ else
+ put_unaligned_be64(val, buf);
+ break;
+ default:
+ dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
+ len, reg);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = regmap_bulk_write(map, reg, buf, len);
+ if (ret)
+ dev_err(regmap_get_device(map), "Error writing reg 0x%04x: %d\n",
+ reg, ret);
+
+out:
+ if (ret && err)
+ *err = ret;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cci_write);
+
+int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err)
+{
+ u64 readval;
+ int ret;
+
+ ret = cci_read(map, reg, &readval, err);
+ if (ret)
+ return ret;
+
+ val = (readval & ~mask) | (val & mask);
+
+ return cci_write(map, reg, val, err);
+}
+EXPORT_SYMBOL_GPL(cci_update_bits);
+
+int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs,
+ unsigned int num_regs, int *err)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < num_regs; i++) {
+ ret = cci_write(map, regs[i].reg, regs[i].val, err);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cci_multi_reg_write);
+
+#if IS_ENABLED(CONFIG_V4L2_CCI_I2C)
+struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client,
+ int reg_addr_bits)
+{
+ struct regmap_config config = {
+ .reg_bits = reg_addr_bits,
+ .val_bits = 8,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .disable_locking = true,
+ };
+
+ return devm_regmap_init_i2c(client, &config);
+}
+EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support");
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index df34b2a283bc..554c591e1113 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -34,6 +34,9 @@
* Added Gerd Knorrs v4l1 enhancements (Justin Schoeman)
*/
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -154,13 +157,18 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
EXPORT_SYMBOL_GPL(v4l_bound_align_image);
const void *
-__v4l2_find_nearest_size(const void *array, size_t array_size,
- size_t entry_size, size_t width_offset,
- size_t height_offset, s32 width, s32 height)
+__v4l2_find_nearest_size_conditional(const void *array, size_t array_size,
+ size_t entry_size, size_t width_offset,
+ size_t height_offset, s32 width,
+ s32 height,
+ bool (*func)(const void *array,
+ size_t index,
+ const void *context),
+ const void *context)
{
u32 error, min_error = U32_MAX;
const void *best = NULL;
- unsigned int i;
+ size_t i;
if (!array)
return NULL;
@@ -169,6 +177,9 @@ __v4l2_find_nearest_size(const void *array, size_t array_size,
const u32 *entry_width = array + width_offset;
const u32 *entry_height = array + height_offset;
+ if (func && !func(array, i, context))
+ continue;
+
error = abs(*entry_width - width) + abs(*entry_height - height);
if (error > min_error)
continue;
@@ -181,7 +192,7 @@ __v4l2_find_nearest_size(const void *array, size_t array_size,
return best;
}
-EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size);
+EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size_conditional);
int v4l2_g_parm_cap(struct video_device *vdev,
struct v4l2_subdev *sd, struct v4l2_streamparm *a)
@@ -195,9 +206,9 @@ int v4l2_g_parm_cap(struct video_device *vdev,
if (vdev->device_caps & V4L2_CAP_READWRITE)
a->parm.capture.readbuffers = 2;
- if (v4l2_subdev_has_op(sd, video, g_frame_interval))
+ if (v4l2_subdev_has_op(sd, pad, get_frame_interval))
a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- ret = v4l2_subdev_call(sd, video, g_frame_interval, &ival);
+ ret = v4l2_subdev_call_state_active(sd, pad, get_frame_interval, &ival);
if (!ret)
a->parm.capture.timeperframe = ival.interval;
return ret;
@@ -222,9 +233,9 @@ int v4l2_s_parm_cap(struct video_device *vdev,
else
a->parm.capture.readbuffers = 0;
- if (v4l2_subdev_has_op(sd, video, g_frame_interval))
+ if (v4l2_subdev_has_op(sd, pad, get_frame_interval))
a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- ret = v4l2_subdev_call(sd, video, s_frame_interval, &ival);
+ ret = v4l2_subdev_call_state_active(sd, pad, set_frame_interval, &ival);
if (!ret)
a->parm.capture.timeperframe = ival.interval;
return ret;
@@ -235,83 +246,139 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{
static const struct v4l2_format_info formats[] = {
/* RGB formats */
- { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
/* YUV packed formats */
- { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_UYVY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_UYVY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_Y210, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
+ { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
/* YUV planar formats */
- { .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_NV21, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_NV16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_NV61, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
-
- { .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
- { .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
- { .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_YUV420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_YVU420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_NV21, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_NV15, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_NV16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_NV61, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_NV20, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_P012, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+
+ { .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 4 },
+ { .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 4 },
+ { .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YUV420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_YVU420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
/* Tiled YUV formats */
- { .format = V4L2_PIX_FMT_NV12_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_NV12_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_NV15_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 4, 2, 0, 0 }, .block_h = { 1, 1, 0, 0 }},
+ { .format = V4L2_PIX_FMT_P010_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
/* YUV planar formats, non contiguous variant */
- { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
-
- { .format = V4L2_PIX_FMT_NV12M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_NV21M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
- { .format = V4L2_PIX_FMT_NV16M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_NV61M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
+ { .format = V4L2_PIX_FMT_NV12M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_NV21M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ { .format = V4L2_PIX_FMT_NV16M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_NV61M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_P012M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+
+ /* Tiled YUV formats, non contiguous variant */
+ { .format = V4L2_PIX_FMT_NV12MT, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 64, 32, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
+ { .format = V4L2_PIX_FMT_NV12MT_16X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 16, 8, 0, 0 }, .block_h = { 16, 8, 0, 0 }},
/* Bayer RGB formats */
- { .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGRBG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SRGGB8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SBGGR10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGBRG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGRBG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SRGGB10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SBGGR10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGBRG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGRBG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SRGGB10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SBGGR10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGBRG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGRBG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SRGGB10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SBGGR12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGBRG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SGRBG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_SRGGB12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
+ /* Renesas Camera Data Receiver Unit formats, bayer order agnostic */
+ { .format = V4L2_PIX_FMT_RAW_CRU10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 6, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 5, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU20, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 3, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
};
unsigned int i;
@@ -336,6 +403,34 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf
return info->block_h[plane];
}
+static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane,
+ unsigned int width)
+{
+ unsigned int hdiv = plane ? info->hdiv : 1;
+ unsigned int aligned_width =
+ ALIGN(width, v4l2_format_block_width(info, plane));
+
+ return DIV_ROUND_UP(aligned_width, hdiv) *
+ info->bpp[plane] / info->bpp_div[plane];
+}
+
+static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane,
+ unsigned int height)
+{
+ unsigned int vdiv = plane ? info->vdiv : 1;
+ unsigned int aligned_height =
+ ALIGN(height, v4l2_format_block_height(info, plane));
+
+ return DIV_ROUND_UP(aligned_height, vdiv);
+}
+
+static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane,
+ unsigned int width, unsigned int height)
+{
+ return v4l2_format_plane_stride(info, plane, width) *
+ v4l2_format_plane_height(info, plane, height);
+}
+
void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
const struct v4l2_frmsize_stepwise *frmsize)
{
@@ -371,37 +466,19 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
if (info->mem_planes == 1) {
plane = &pixfmt->plane_fmt[0];
- plane->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0];
+ plane->bytesperline = v4l2_format_plane_stride(info, 0, width);
plane->sizeimage = 0;
- for (i = 0; i < info->comp_planes; i++) {
- unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
- unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
- unsigned int aligned_width;
- unsigned int aligned_height;
-
- aligned_width = ALIGN(width, v4l2_format_block_width(info, i));
- aligned_height = ALIGN(height, v4l2_format_block_height(info, i));
-
- plane->sizeimage += info->bpp[i] *
- DIV_ROUND_UP(aligned_width, hdiv) *
- DIV_ROUND_UP(aligned_height, vdiv);
- }
+ for (i = 0; i < info->comp_planes; i++)
+ plane->sizeimage +=
+ v4l2_format_plane_size(info, i, width, height);
} else {
for (i = 0; i < info->comp_planes; i++) {
- unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
- unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
- unsigned int aligned_width;
- unsigned int aligned_height;
-
- aligned_width = ALIGN(width, v4l2_format_block_width(info, i));
- aligned_height = ALIGN(height, v4l2_format_block_height(info, i));
-
plane = &pixfmt->plane_fmt[i];
plane->bytesperline =
- info->bpp[i] * DIV_ROUND_UP(aligned_width, hdiv);
- plane->sizeimage =
- plane->bytesperline * DIV_ROUND_UP(aligned_height, vdiv);
+ v4l2_format_plane_stride(info, i, width);
+ plane->sizeimage = plane->bytesperline *
+ v4l2_format_plane_height(info, i, height);
}
}
return 0;
@@ -425,28 +502,19 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
pixfmt->width = width;
pixfmt->height = height;
pixfmt->pixelformat = pixelformat;
- pixfmt->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0];
+ pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width);
pixfmt->sizeimage = 0;
- for (i = 0; i < info->comp_planes; i++) {
- unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
- unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
- unsigned int aligned_width;
- unsigned int aligned_height;
-
- aligned_width = ALIGN(width, v4l2_format_block_width(info, i));
- aligned_height = ALIGN(height, v4l2_format_block_height(info, i));
-
- pixfmt->sizeimage += info->bpp[i] *
- DIV_ROUND_UP(aligned_width, hdiv) *
- DIV_ROUND_UP(aligned_height, vdiv);
- }
+ for (i = 0; i < info->comp_planes; i++)
+ pixfmt->sizeimage +=
+ v4l2_format_plane_size(info, i, width, height);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt);
-s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul,
- unsigned int div)
+#ifdef CONFIG_MEDIA_CONTROLLER
+static s64 v4l2_get_link_freq_ctrl(struct v4l2_ctrl_handler *handler,
+ unsigned int mul, unsigned int div)
{
struct v4l2_ctrl *ctrl;
s64 freq;
@@ -473,12 +541,268 @@ s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul,
freq = div_u64(v4l2_ctrl_g_ctrl_int64(ctrl) * mul, div);
- pr_warn("%s: Link frequency estimated using pixel rate: result might be inaccurate\n",
- __func__);
- pr_warn("%s: Consider implementing support for V4L2_CID_LINK_FREQ in the transmitter driver\n",
- __func__);
+ pr_warn_once("%s: Link frequency estimated using pixel rate: result might be inaccurate\n",
+ __func__);
+ pr_warn_once("%s: Consider implementing support for V4L2_CID_LINK_FREQ in the transmitter driver\n",
+ __func__);
}
return freq > 0 ? freq : -EINVAL;
}
+
+s64 v4l2_get_link_freq(const struct media_pad *pad, unsigned int mul,
+ unsigned int div)
+{
+ struct v4l2_mbus_config mbus_config = {};
+ struct v4l2_subdev *sd;
+ int ret;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ ret = v4l2_subdev_call(sd, pad, get_mbus_config, pad->index,
+ &mbus_config);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (mbus_config.link_freq)
+ return mbus_config.link_freq;
+
+ /*
+ * Fall back to using the link frequency control if the media bus config
+ * doesn't provide a link frequency.
+ */
+ return v4l2_get_link_freq_ctrl(sd->ctrl_handler, mul, div);
+}
EXPORT_SYMBOL_GPL(v4l2_get_link_freq);
+
+int v4l2_get_active_data_lanes(const struct media_pad *pad,
+ unsigned int max_data_lanes)
+{
+ struct v4l2_mbus_config mbus_config = {};
+ struct v4l2_subdev *sd;
+ unsigned int lanes;
+ int ret;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ ret = v4l2_subdev_call(sd, pad, get_mbus_config, pad->index,
+ &mbus_config);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* This relies on the mbus_config being zeroed at init time */
+ lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
+ if (!lanes)
+ return max_data_lanes;
+
+ if (lanes > max_data_lanes) {
+ dev_dbg(sd->dev, "Active data lanes (%u) exceeds max (%u)\n",
+ lanes, max_data_lanes);
+ return -EINVAL;
+ }
+
+ return lanes;
+}
+EXPORT_SYMBOL_GPL(v4l2_get_active_data_lanes);
+#endif
+
+/*
+ * Simplify a fraction using a simple continued fraction decomposition. The
+ * idea here is to convert fractions such as 333333/10000000 to 1/30 using
+ * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
+ * arbitrary parameters to remove non-significative terms from the simple
+ * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
+ * respectively seems to give nice results.
+ */
+void v4l2_simplify_fraction(u32 *numerator, u32 *denominator,
+ unsigned int n_terms, unsigned int threshold)
+{
+ u32 *an;
+ u32 x, y, r;
+ unsigned int i, n;
+
+ an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL);
+ if (an == NULL)
+ return;
+
+ /*
+ * Convert the fraction to a simple continued fraction. See
+ * https://en.wikipedia.org/wiki/Continued_fraction
+ * Stop if the current term is bigger than or equal to the given
+ * threshold.
+ */
+ x = *numerator;
+ y = *denominator;
+
+ for (n = 0; n < n_terms && y != 0; ++n) {
+ an[n] = x / y;
+ if (an[n] >= threshold) {
+ if (n < 2)
+ n++;
+ break;
+ }
+
+ r = x - an[n] * y;
+ x = y;
+ y = r;
+ }
+
+ /* Expand the simple continued fraction back to an integer fraction. */
+ x = 0;
+ y = 1;
+
+ for (i = n; i > 0; --i) {
+ r = y;
+ y = an[i-1] * y + x;
+ x = r;
+ }
+
+ *numerator = y;
+ *denominator = x;
+ kfree(an);
+}
+EXPORT_SYMBOL_GPL(v4l2_simplify_fraction);
+
+/*
+ * Convert a fraction to a frame interval in 100ns multiples. The idea here is
+ * to compute numerator / denominator * 10000000 using 32 bit fixed point
+ * arithmetic only.
+ */
+u32 v4l2_fraction_to_interval(u32 numerator, u32 denominator)
+{
+ u32 multiplier;
+
+ /* Saturate the result if the operation would overflow. */
+ if (denominator == 0 ||
+ numerator/denominator >= ((u32)-1)/10000000)
+ return (u32)-1;
+
+ /*
+ * Divide both the denominator and the multiplier by two until
+ * numerator * multiplier doesn't overflow. If anyone knows a better
+ * algorithm please let me know.
+ */
+ multiplier = 10000000;
+ while (numerator > ((u32)-1)/multiplier) {
+ multiplier /= 2;
+ denominator /= 2;
+ }
+
+ return denominator ? numerator * multiplier / denominator : 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_fraction_to_interval);
+
+int v4l2_link_freq_to_bitmap(struct device *dev, const u64 *fw_link_freqs,
+ unsigned int num_of_fw_link_freqs,
+ const s64 *driver_link_freqs,
+ unsigned int num_of_driver_link_freqs,
+ unsigned long *bitmap)
+{
+ unsigned int i;
+
+ *bitmap = 0;
+
+ if (!num_of_fw_link_freqs) {
+ dev_err(dev, "no link frequencies in firmware\n");
+ return -ENODATA;
+ }
+
+ for (i = 0; i < num_of_fw_link_freqs; i++) {
+ unsigned int j;
+
+ for (j = 0; j < num_of_driver_link_freqs; j++) {
+ if (fw_link_freqs[i] != driver_link_freqs[j])
+ continue;
+
+ dev_dbg(dev, "enabling link frequency %lld Hz\n",
+ driver_link_freqs[j]);
+ *bitmap |= BIT(j);
+ break;
+ }
+ }
+
+ if (!*bitmap) {
+ dev_err(dev, "no matching link frequencies found\n");
+
+ dev_dbg(dev, "specified in firmware:\n");
+ for (i = 0; i < num_of_fw_link_freqs; i++)
+ dev_dbg(dev, "\t%llu Hz\n", fw_link_freqs[i]);
+
+ dev_dbg(dev, "driver supported:\n");
+ for (i = 0; i < num_of_driver_link_freqs; i++)
+ dev_dbg(dev, "\t%lld Hz\n", driver_link_freqs[i]);
+
+ return -ENOENT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_link_freq_to_bitmap);
+
+struct clk *__devm_v4l2_sensor_clk_get(struct device *dev, const char *id,
+ bool legacy, bool fixed_rate,
+ unsigned long clk_rate)
+{
+ bool of_node = is_of_node(dev_fwnode(dev));
+ const char *clk_id __free(kfree) = NULL;
+ struct clk_hw *clk_hw;
+ struct clk *clk;
+ u32 rate = clk_rate;
+ int ret = 0;
+
+ clk = devm_clk_get_optional(dev, id);
+ if (IS_ERR(clk))
+ return clk;
+
+ /*
+ * If the caller didn't request a fixed rate, retrieve it from the
+ * clock-frequency property. -EINVAL indicates the property is absent,
+ * and is not a failure. Other errors, or success with a clock-frequency
+ * value of 0, are hard failures.
+ */
+ if (!fixed_rate || !clk_rate) {
+ ret = device_property_read_u32(dev, "clock-frequency", &rate);
+ if ((ret && ret != -EINVAL) || (!ret && !rate))
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (clk) {
+ /*
+ * On non-OF platforms, or when legacy behaviour is requested,
+ * set the clock rate if a rate has been specified by the caller
+ * or by the clock-frequency property.
+ */
+ if (rate && (!of_node || legacy)) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_err(dev, "Failed to set clock rate: %u\n",
+ rate);
+ return ERR_PTR(ret);
+ }
+ }
+ return clk;
+ }
+
+ /*
+ * Register a dummy fixed clock on non-OF platforms or when legacy
+ * behaviour is requested. This required the common clock framework.
+ */
+ if (!IS_ENABLED(CONFIG_COMMON_CLK) || (of_node && !legacy))
+ return ERR_PTR(-ENOENT);
+
+ /* We need a rate to create a clock. */
+ if (ret)
+ return ERR_PTR(ret == -EINVAL ? -EPROBE_DEFER : ret);
+
+ if (!id) {
+ clk_id = kasprintf(GFP_KERNEL, "clk-%s", dev_name(dev));
+ if (!clk_id)
+ return ERR_PTR(-ENOMEM);
+ id = clk_id;
+ }
+
+ clk_hw = devm_clk_hw_register_fixed_rate(dev, id, NULL, 0, rate);
+ if (IS_ERR(clk_hw))
+ return ERR_CAST(clk_hw);
+
+ return clk_hw->clk;
+}
+EXPORT_SYMBOL_GPL(__devm_v4l2_sensor_clk_get);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 0f3d6b5667b0..2c88e1175a10 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -8,7 +8,7 @@
* Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
* Copyright (C) 2003 Pavel Machek (pavel@ucw.cz)
* Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be)
- * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Hans Verkuil <hverkuil@kernel.org>
*
* These routines maintain argument size conversion between 32bit and 64bit
* ioctls.
@@ -38,18 +38,13 @@
* data to the routine.
*/
-struct v4l2_clip32 {
- struct v4l2_rect c;
- compat_caddr_t next;
-};
-
struct v4l2_window32 {
struct v4l2_rect w;
__u32 field; /* enum v4l2_field */
__u32 chromakey;
- compat_caddr_t clips; /* actually struct v4l2_clip32 * */
- __u32 clipcount;
- compat_caddr_t bitmap;
+ compat_caddr_t clips; /* always NULL */
+ __u32 clipcount; /* always 0 */
+ compat_caddr_t bitmap; /* always NULL */
__u8 global_alpha;
};
@@ -65,17 +60,12 @@ static int get_v4l2_window32(struct v4l2_window *p64,
.w = w32.w,
.field = w32.field,
.chromakey = w32.chromakey,
- .clips = (void __force *)compat_ptr(w32.clips),
- .clipcount = w32.clipcount,
- .bitmap = compat_ptr(w32.bitmap),
+ .clips = NULL,
+ .clipcount = 0,
+ .bitmap = NULL,
.global_alpha = w32.global_alpha,
};
- if (p64->clipcount > 2048)
- return -EINVAL;
- if (!p64->clipcount)
- p64->clips = NULL;
-
return 0;
}
@@ -89,16 +79,13 @@ static int put_v4l2_window32(struct v4l2_window *p64,
.w = p64->w,
.field = p64->field,
.chromakey = p64->chromakey,
- .clips = (uintptr_t)p64->clips,
- .clipcount = p64->clipcount,
- .bitmap = ptr_to_compat(p64->bitmap),
+ .clips = 0,
+ .clipcount = 0,
+ .bitmap = 0,
.global_alpha = p64->global_alpha,
};
- /* copy everything except the clips pointer */
- if (copy_to_user(p32, &w32, offsetof(struct v4l2_window32, clips)) ||
- copy_to_user(&p32->clipcount, &w32.clipcount,
- sizeof(w32) - offsetof(struct v4l2_window32, clipcount)))
+ if (copy_to_user(p32, &w32, sizeof(w32)))
return -EFAULT;
return 0;
@@ -129,6 +116,9 @@ struct v4l2_format32 {
* @flags: additional buffer management attributes (ignored unless the
* queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability and
* configured for MMAP streaming I/O).
+ * @max_num_buffers: if V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS capability flag is set
+ * this field indicate the maximum possible number of buffers
+ * for this queue.
* @reserved: future extensions
*/
struct v4l2_create_buffers32 {
@@ -138,7 +128,8 @@ struct v4l2_create_buffers32 {
struct v4l2_format32 format;
__u32 capabilities;
__u32 flags;
- __u32 reserved[6];
+ __u32 max_num_buffers;
+ __u32 reserved[5];
};
static int get_v4l2_format32(struct v4l2_format *p64,
@@ -188,6 +179,9 @@ static int get_v4l2_create32(struct v4l2_create_buffers *p64,
return -EFAULT;
if (copy_from_user(&p64->flags, &p32->flags, sizeof(p32->flags)))
return -EFAULT;
+ if (copy_from_user(&p64->max_num_buffers, &p32->max_num_buffers,
+ sizeof(p32->max_num_buffers)))
+ return -EFAULT;
return get_v4l2_format32(&p64->format, &p32->format);
}
@@ -234,6 +228,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers *p64,
offsetof(struct v4l2_create_buffers32, format)) ||
put_user(p64->capabilities, &p32->capabilities) ||
put_user(p64->flags, &p32->flags) ||
+ put_user(p64->max_num_buffers, &p32->max_num_buffers) ||
copy_to_user(p32->reserved, p64->reserved, sizeof(p64->reserved)))
return -EFAULT;
return put_v4l2_format32(&p64->format, &p32->format);
@@ -600,14 +595,11 @@ struct v4l2_framebuffer32 {
static int get_v4l2_framebuffer32(struct v4l2_framebuffer *p64,
struct v4l2_framebuffer32 __user *p32)
{
- compat_caddr_t tmp;
-
- if (get_user(tmp, &p32->base) ||
- get_user(p64->capability, &p32->capability) ||
+ if (get_user(p64->capability, &p32->capability) ||
get_user(p64->flags, &p32->flags) ||
copy_from_user(&p64->fmt, &p32->fmt, sizeof(p64->fmt)))
return -EFAULT;
- p64->base = (void __force *)compat_ptr(tmp);
+ p64->base = NULL;
return 0;
}
@@ -680,15 +672,12 @@ struct v4l2_ext_control32 {
static inline bool ctrl_is_pointer(struct file *file, u32 id)
{
struct video_device *vdev = video_devdata(file);
- struct v4l2_fh *fh = NULL;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
struct v4l2_ctrl_handler *hdl = NULL;
struct v4l2_query_ext_ctrl qec = { id };
const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))
- fh = file->private_data;
-
- if (fh && fh->ctrl_handler)
+ if (fh->ctrl_handler)
hdl = fh->ctrl_handler;
else if (vdev->ctrl_handler)
hdl = vdev->ctrl_handler;
@@ -702,7 +691,7 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
if (!ops || !ops->vidioc_query_ext_ctrl)
return false;
- return !ops->vidioc_query_ext_ctrl(file, fh, &qec) &&
+ return !ops->vidioc_query_ext_ctrl(file, NULL, &qec) &&
(qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
}
@@ -1040,30 +1029,9 @@ int v4l2_compat_get_array_args(struct file *file, void *mbuf,
{
int err = 0;
+ memset(mbuf, 0, array_size);
+
switch (cmd) {
- case VIDIOC_G_FMT32:
- case VIDIOC_S_FMT32:
- case VIDIOC_TRY_FMT32: {
- struct v4l2_format *f64 = arg;
- struct v4l2_clip *c64 = mbuf;
- struct v4l2_clip32 __user *c32 = user_ptr;
- u32 clipcount = f64->fmt.win.clipcount;
-
- if ((f64->type != V4L2_BUF_TYPE_VIDEO_OVERLAY &&
- f64->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) ||
- clipcount == 0)
- return 0;
- if (clipcount > 2048)
- return -EINVAL;
- while (clipcount--) {
- if (copy_from_user(c64, c32, sizeof(c64->c)))
- return -EFAULT;
- c64->next = NULL;
- c64++;
- c32++;
- }
- break;
- }
#ifdef CONFIG_COMPAT_32BIT_TIME
case VIDIOC_QUERYBUF32_TIME32:
case VIDIOC_QBUF32_TIME32:
@@ -1134,28 +1102,6 @@ int v4l2_compat_put_array_args(struct file *file, void __user *user_ptr,
int err = 0;
switch (cmd) {
- case VIDIOC_G_FMT32:
- case VIDIOC_S_FMT32:
- case VIDIOC_TRY_FMT32: {
- struct v4l2_format *f64 = arg;
- struct v4l2_clip *c64 = mbuf;
- struct v4l2_clip32 __user *c32 = user_ptr;
- u32 clipcount = f64->fmt.win.clipcount;
-
- if ((f64->type != V4L2_BUF_TYPE_VIDEO_OVERLAY &&
- f64->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) ||
- clipcount == 0)
- return 0;
- if (clipcount > 2048)
- return -EINVAL;
- while (clipcount--) {
- if (copy_to_user(c32, c64, sizeof(c64->c)))
- return -EFAULT;
- c64++;
- c32++;
- }
- break;
- }
#ifdef CONFIG_COMPAT_32BIT_TIME
case VIDIOC_QUERYBUF32_TIME32:
case VIDIOC_QBUF32_TIME32:
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
index db9baa0bd05f..0078a04c5445 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -2,7 +2,7 @@
/*
* V4L2 controls framework uAPI implementation:
*
- * Copyright (C) 2010-2021 Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ * Copyright (C) 2010-2021 Hans Verkuil <hverkuil@kernel.org>
*/
#define pr_fmt(fmt) "v4l2-ctrls: " fmt
@@ -89,37 +89,66 @@ static int req_to_user(struct v4l2_ext_control *c,
/* Helper function: copy the initial control value back to the caller */
static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
{
- int idx;
+ ctrl->type_ops->init(ctrl, 0, ctrl->p_new);
- for (idx = 0; idx < ctrl->elems; idx++)
- ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
+ return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
+/* Helper function: copy the minimum control value back to the caller */
+static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
+{
+ ctrl->type_ops->minimum(ctrl, 0, ctrl->p_new);
return ptr_to_user(c, ctrl, ctrl->p_new);
}
-/* Helper function: copy the caller-provider value to the given control value */
-static int user_to_ptr(struct v4l2_ext_control *c,
- struct v4l2_ctrl *ctrl,
- union v4l2_ctrl_ptr ptr)
+/* Helper function: copy the maximum control value back to the caller */
+static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
+{
+ ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
+
+ return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
{
int ret;
u32 size;
- ctrl->is_new = 1;
- if (ctrl->is_ptr && !ctrl->is_string) {
- unsigned int idx;
+ ctrl->is_new = 0;
+ if (ctrl->is_dyn_array &&
+ c->size > ctrl->p_array_alloc_elems * ctrl->elem_size) {
+ void *old = ctrl->p_array;
+ void *tmp = kvzalloc(2 * c->size, GFP_KERNEL);
- ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0;
- if (ret || !ctrl->is_array)
- return ret;
- for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++)
- ctrl->type_ops->init(ctrl, idx, ptr);
+ if (!tmp)
+ return -ENOMEM;
+ memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size);
+ memcpy(tmp + c->size, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size);
+ ctrl->p_new.p = tmp;
+ ctrl->p_cur.p = tmp + c->size;
+ ctrl->p_array = tmp;
+ ctrl->p_array_alloc_elems = c->size / ctrl->elem_size;
+ kvfree(old);
+ }
+
+ if (ctrl->is_ptr && !ctrl->is_string) {
+ unsigned int elems = c->size / ctrl->elem_size;
+
+ if (copy_from_user(ctrl->p_new.p, c->ptr, c->size))
+ return -EFAULT;
+ ctrl->is_new = 1;
+ if (ctrl->is_dyn_array)
+ ctrl->new_elems = elems;
+ else if (ctrl->is_array)
+ ctrl->type_ops->init(ctrl, elems, ctrl->p_new);
return 0;
}
switch (ctrl->type) {
case V4L2_CTRL_TYPE_INTEGER64:
- *ptr.p_s64 = c->value64;
+ *ctrl->p_new.p_s64 = c->value64;
break;
case V4L2_CTRL_TYPE_STRING:
size = c->size;
@@ -127,32 +156,28 @@ static int user_to_ptr(struct v4l2_ext_control *c,
return -ERANGE;
if (size > ctrl->maximum + 1)
size = ctrl->maximum + 1;
- ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0;
+ ret = copy_from_user(ctrl->p_new.p_char, c->string, size) ? -EFAULT : 0;
if (!ret) {
- char last = ptr.p_char[size - 1];
+ char last = ctrl->p_new.p_char[size - 1];
- ptr.p_char[size - 1] = 0;
+ ctrl->p_new.p_char[size - 1] = 0;
/*
* If the string was longer than ctrl->maximum,
* then return an error.
*/
- if (strlen(ptr.p_char) == ctrl->maximum && last)
+ if (strlen(ctrl->p_new.p_char) == ctrl->maximum && last)
return -ERANGE;
+ ctrl->is_new = 1;
}
return ret;
default:
- *ptr.p_s32 = c->value;
+ *ctrl->p_new.p_s32 = c->value;
break;
}
+ ctrl->is_new = 1;
return 0;
}
-/* Helper function: copy the caller-provider value as the new control value */
-static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
-{
- return user_to_ptr(c, ctrl, ctrl->p_new);
-}
-
/*
* VIDIOC_G/TRY/S_EXT_CTRLS implementation
*/
@@ -220,8 +245,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
cs->error_idx = i;
if (cs->which &&
- cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
- cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
+ (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
+ cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
V4L2_CTRL_ID2WHICH(id) != cs->which) {
dprintk(vdev,
"invalid which 0x%x or control id 0x%x\n",
@@ -250,11 +275,44 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
return -EINVAL;
}
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
+ (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
+ cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
+ dprintk(vdev,
+ "invalid which 0x%x or control id 0x%x\n",
+ cs->which, id);
+ return -EINVAL;
+ }
+
if (ctrl->cluster[0]->ncontrols > 1)
have_clusters = true;
if (ctrl->cluster[0] != ctrl)
ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
- if (ctrl->is_ptr && !ctrl->is_string) {
+ if (ctrl->is_dyn_array) {
+ unsigned int max_size = ctrl->dims[0] * ctrl->elem_size;
+ unsigned int tot_size = ctrl->elem_size;
+
+ if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL)
+ tot_size *= ref->p_req_elems;
+ else
+ tot_size *= ctrl->elems;
+
+ c->size = ctrl->elem_size * (c->size / ctrl->elem_size);
+ if (get) {
+ if (c->size < tot_size) {
+ c->size = tot_size;
+ return -ENOSPC;
+ }
+ c->size = tot_size;
+ } else {
+ if (c->size > max_size) {
+ c->size = max_size;
+ return -ENOSPC;
+ }
+ if (!c->size)
+ return -EFAULT;
+ }
+ } else if (ctrl->is_ptr && !ctrl->is_string) {
unsigned int tot_size = ctrl->elems * ctrl->elem_size;
if (c->size < tot_size) {
@@ -335,8 +393,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
*/
static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
{
- if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
- which == V4L2_CTRL_WHICH_REQUEST_VAL)
+ if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
+ which <= V4L2_CTRL_WHICH_MAX_VAL))
return 0;
return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
}
@@ -346,7 +404,7 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
*
* Note that v4l2_g_ext_ctrls_common() with 'which' set to
* V4L2_CTRL_WHICH_REQUEST_VAL is only called if the request was
- * completed, and in that case valid_p_req is true for all controls.
+ * completed, and in that case p_req_valid is true for all controls.
*/
int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
struct v4l2_ext_controls *cs,
@@ -356,10 +414,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl_helper *helpers = helper;
int ret;
int i, j;
- bool is_default, is_request;
+ bool is_default, is_request, is_min, is_max;
is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
+ is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
+ is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
cs->error_idx = cs->count;
cs->which = V4L2_CTRL_ID2WHICH(cs->which);
@@ -399,13 +459,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
/*
* g_volatile_ctrl will update the new control values.
- * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
+ * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
+ * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
* V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
* it is v4l2_ctrl_request_complete() that copies the
* volatile controls at the time of request completion
* to the request, so you don't want to do that again.
*/
- if (!is_default && !is_request &&
+ if (!is_default && !is_request && !is_min && !is_max &&
((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
(master->has_volatiles && !is_cur_manual(master)))) {
for (j = 0; j < master->ncontrols; j++)
@@ -430,8 +491,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
if (is_default)
ret = def_to_user(cs->controls + idx, ref->ctrl);
- else if (is_request && ref->valid_p_req)
+ else if (is_request && ref->p_req_array_enomem)
+ ret = -ENOMEM;
+ else if (is_request && ref->p_req_valid)
ret = req_to_user(cs->controls + idx, ref);
+ else if (is_min)
+ ret = min_to_user(cs->controls + idx, ref->ctrl);
+ else if (is_max)
+ ret = max_to_user(cs->controls + idx, ref->ctrl);
else if (is_volatile)
ret = new_to_user(cs->controls + idx, ref->ctrl);
else
@@ -457,6 +524,12 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct video_device *vdev,
}
EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+/* Validate a new control */
+static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new)
+{
+ return ctrl->type_ops->validate(ctrl, p_new);
+}
+
/* Validate controls. */
static int validate_ctrls(struct v4l2_ext_controls *cs,
struct v4l2_ctrl_helper *helpers,
@@ -523,9 +596,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
cs->error_idx = cs->count;
- /* Default value cannot be changed */
- if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
- dprintk(vdev, "%s: cannot change default value\n",
+ /* Default/minimum/maximum values cannot be changed */
+ if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
+ cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
+ cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
+ dprintk(vdev, "%s: cannot change default/min/max value\n",
video_device_node_name(vdev));
return -EINVAL;
}
@@ -712,9 +787,10 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
for (i = 0; i < master->ncontrols; i++)
cur_to_new(master->cluster[i]);
ret = call_op(master, g_volatile_ctrl);
- new_to_user(c, ctrl);
+ if (!ret)
+ ret = new_to_user(c, ctrl);
} else {
- cur_to_user(c, ctrl);
+ ret = cur_to_user(c, ctrl);
}
v4l2_ctrl_unlock(master);
return ret;
@@ -729,7 +805,10 @@ int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
if (!ctrl || !ctrl->is_int)
return -EINVAL;
ret = get_ctrl(ctrl, &c);
- control->value = c.value;
+
+ if (!ret)
+ control->value = c.value;
+
return ret;
}
EXPORT_SYMBOL(v4l2_g_ctrl);
@@ -770,10 +849,11 @@ static int set_ctrl_lock(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
int ret;
v4l2_ctrl_lock(ctrl);
- user_to_new(c, ctrl);
- ret = set_ctrl(fh, ctrl, 0);
+ ret = user_to_new(c, ctrl);
if (!ret)
- cur_to_user(c, ctrl);
+ ret = set_ctrl(fh, ctrl, 0);
+ if (!ret)
+ ret = cur_to_user(c, ctrl);
v4l2_ctrl_unlock(ctrl);
return ret;
}
@@ -872,6 +952,9 @@ int __v4l2_ctrl_s_ctrl_compound(struct v4l2_ctrl *ctrl,
/* It's a driver bug if this happens. */
if (WARN_ON(ctrl->type != type))
return -EINVAL;
+ /* Setting dynamic arrays is not (yet?) supported. */
+ if (WARN_ON(ctrl->is_dyn_array))
+ return -EINVAL;
memcpy(ctrl->p_new.p, p, ctrl->elems * ctrl->elem_size);
return set_ctrl(NULL, ctrl, 0);
}
@@ -936,6 +1019,42 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
}
EXPORT_SYMBOL(__v4l2_ctrl_modify_range);
+int __v4l2_ctrl_modify_dimensions(struct v4l2_ctrl *ctrl,
+ u32 dims[V4L2_CTRL_MAX_DIMS])
+{
+ unsigned int elems = 1;
+ unsigned int i;
+ void *p_array;
+
+ lockdep_assert_held(ctrl->handler->lock);
+
+ if (!ctrl->is_array || ctrl->is_dyn_array)
+ return -EINVAL;
+
+ for (i = 0; i < ctrl->nr_of_dims; i++)
+ elems *= dims[i];
+ if (elems == 0)
+ return -EINVAL;
+ p_array = kvzalloc(2 * elems * ctrl->elem_size, GFP_KERNEL);
+ if (!p_array)
+ return -ENOMEM;
+ kvfree(ctrl->p_array);
+ ctrl->p_array_alloc_elems = elems;
+ ctrl->elems = elems;
+ ctrl->new_elems = elems;
+ ctrl->p_array = p_array;
+ ctrl->p_new.p = p_array;
+ ctrl->p_cur.p = p_array + elems * ctrl->elem_size;
+ for (i = 0; i < ctrl->nr_of_dims; i++)
+ ctrl->dims[i] = dims[i];
+ ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
+ cur_to_new(ctrl);
+ send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_VALUE |
+ V4L2_EVENT_CTRL_CH_DIMENSIONS);
+ return 0;
+}
+EXPORT_SYMBOL(__v4l2_ctrl_modify_dimensions);
+
/* Implement VIDIOC_QUERY_EXT_CTRL */
int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc)
{
@@ -972,35 +1091,40 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr
if (id >= node2id(hdl->ctrl_refs.prev)) {
ref = NULL; /* Yes, so there is no next control */
} else if (ref) {
+ struct v4l2_ctrl_ref *pos = ref;
+
/*
* We found a control with the given ID, so just get
* the next valid one in the list.
*/
- list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) {
- is_compound = ref->ctrl->is_array ||
- ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
- if (id < ref->ctrl->id &&
- (is_compound & mask) == match)
+ ref = NULL;
+ list_for_each_entry_continue(pos, &hdl->ctrl_refs, node) {
+ is_compound = pos->ctrl->is_array ||
+ pos->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
+ if (id < pos->ctrl->id &&
+ (is_compound & mask) == match) {
+ ref = pos;
break;
+ }
}
- if (&ref->node == &hdl->ctrl_refs)
- ref = NULL;
} else {
+ struct v4l2_ctrl_ref *pos;
+
/*
* No control with the given ID exists, so start
* searching for the next largest ID. We know there
* is one, otherwise the first 'if' above would have
* been true.
*/
- list_for_each_entry(ref, &hdl->ctrl_refs, node) {
- is_compound = ref->ctrl->is_array ||
- ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
- if (id < ref->ctrl->id &&
- (is_compound & mask) == match)
+ list_for_each_entry(pos, &hdl->ctrl_refs, node) {
+ is_compound = pos->ctrl->is_array ||
+ pos->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
+ if (id < pos->ctrl->id &&
+ (is_compound & mask) == match) {
+ ref = pos;
break;
+ }
}
- if (&ref->node == &hdl->ctrl_refs)
- ref = NULL;
}
}
mutex_unlock(hdl->lock);
@@ -1033,39 +1157,48 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr
}
EXPORT_SYMBOL(v4l2_query_ext_ctrl);
-/* Implement VIDIOC_QUERYCTRL */
-int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+void v4l2_query_ext_ctrl_to_v4l2_queryctrl(struct v4l2_queryctrl *to,
+ const struct v4l2_query_ext_ctrl *from)
{
- struct v4l2_query_ext_ctrl qec = { qc->id };
- int rc;
-
- rc = v4l2_query_ext_ctrl(hdl, &qec);
- if (rc)
- return rc;
+ to->id = from->id;
+ to->type = from->type;
+ to->flags = from->flags;
+ strscpy(to->name, from->name, sizeof(to->name));
- qc->id = qec.id;
- qc->type = qec.type;
- qc->flags = qec.flags;
- strscpy(qc->name, qec.name, sizeof(qc->name));
- switch (qc->type) {
+ switch (from->type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_MENU:
case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_STRING:
case V4L2_CTRL_TYPE_BITMASK:
- qc->minimum = qec.minimum;
- qc->maximum = qec.maximum;
- qc->step = qec.step;
- qc->default_value = qec.default_value;
+ to->minimum = from->minimum;
+ to->maximum = from->maximum;
+ to->step = from->step;
+ to->default_value = from->default_value;
break;
default:
- qc->minimum = 0;
- qc->maximum = 0;
- qc->step = 0;
- qc->default_value = 0;
+ to->minimum = 0;
+ to->maximum = 0;
+ to->step = 0;
+ to->default_value = 0;
break;
}
+}
+EXPORT_SYMBOL(v4l2_query_ext_ctrl_to_v4l2_queryctrl);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+ struct v4l2_query_ext_ctrl qec = { qc->id };
+ int rc;
+
+ rc = v4l2_query_ext_ctrl(hdl, &qec);
+ if (rc)
+ return rc;
+
+ v4l2_query_ext_ctrl_to_v4l2_queryctrl(qc, &qec);
+
return 0;
}
EXPORT_SYMBOL(v4l2_queryctrl);
@@ -1099,7 +1232,7 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
return -EINVAL;
/* Use mask to see if this menu item should be skipped */
- if (ctrl->menu_skip_mask & (1ULL << i))
+ if (i < BITS_PER_LONG_LONG && (ctrl->menu_skip_mask & BIT_ULL(i)))
return -EINVAL;
/* Empty menu items should also be skipped */
if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
@@ -1117,14 +1250,17 @@ EXPORT_SYMBOL(v4l2_querymenu);
* VIDIOC_LOG_STATUS helpers
*/
-int v4l2_ctrl_log_status(struct file *file, void *fh)
+int v4l2_ctrl_log_status(struct file *file, void *priv)
{
struct video_device *vfd = video_devdata(file);
- struct v4l2_fh *vfh = file->private_data;
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
+ if (vfd->v4l2_dev) {
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
+
v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
vfd->v4l2_dev->name);
+ }
+
return 0;
}
EXPORT_SYMBOL(v4l2_ctrl_log_status);
@@ -1215,7 +1351,7 @@ EXPORT_SYMBOL(v4l2_ctrl_subdev_subscribe_event);
*/
__poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
poll_wait(file, &fh->wait, wait);
if (v4l2_event_pending(fh))
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 54abe5245dcc..209bc05883bb 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -2,7 +2,7 @@
/*
* V4L2 controls framework core implementation.
*
- * Copyright (C) 2010-2021 Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ * Copyright (C) 2010-2021 Hans Verkuil <hverkuil@kernel.org>
*/
#include <linux/export.h>
@@ -65,33 +65,29 @@ void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
v4l2_event_queue_fh(sev->fh, &ev);
}
-static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
- union v4l2_ctrl_ptr ptr1,
- union v4l2_ctrl_ptr ptr2)
+bool v4l2_ctrl_type_op_equal(const struct v4l2_ctrl *ctrl,
+ union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2)
{
+ unsigned int i;
+
switch (ctrl->type) {
case V4L2_CTRL_TYPE_BUTTON:
return false;
case V4L2_CTRL_TYPE_STRING:
- idx *= ctrl->elem_size;
- /* strings are always 0-terminated */
- return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx);
- case V4L2_CTRL_TYPE_INTEGER64:
- return ptr1.p_s64[idx] == ptr2.p_s64[idx];
- case V4L2_CTRL_TYPE_U8:
- return ptr1.p_u8[idx] == ptr2.p_u8[idx];
- case V4L2_CTRL_TYPE_U16:
- return ptr1.p_u16[idx] == ptr2.p_u16[idx];
- case V4L2_CTRL_TYPE_U32:
- return ptr1.p_u32[idx] == ptr2.p_u32[idx];
+ for (i = 0; i < ctrl->elems; i++) {
+ unsigned int idx = i * ctrl->elem_size;
+
+ /* strings are always 0-terminated */
+ if (strcmp(ptr1.p_char + idx, ptr2.p_char + idx))
+ return false;
+ }
+ return true;
default:
- if (ctrl->is_int)
- return ptr1.p_s32[idx] == ptr2.p_s32[idx];
- idx *= ctrl->elem_size;
- return !memcmp(ptr1.p_const + idx, ptr2.p_const + idx,
- ctrl->elem_size);
+ return !memcmp(ptr1.p_const, ptr2.p_const,
+ ctrl->elems * ctrl->elem_size);
}
}
+EXPORT_SYMBOL(v4l2_ctrl_type_op_equal);
/* Default intra MPEG-2 quantisation coefficients, from the specification. */
static const u8 mpeg2_intra_quant_matrix[64] = {
@@ -112,7 +108,10 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
struct v4l2_ctrl_mpeg2_picture *p_mpeg2_picture;
struct v4l2_ctrl_mpeg2_quantisation *p_mpeg2_quant;
struct v4l2_ctrl_vp8_frame *p_vp8_frame;
+ struct v4l2_ctrl_vp9_frame *p_vp9_frame;
struct v4l2_ctrl_fwht_params *p_fwht_params;
+ struct v4l2_ctrl_h264_scaling_matrix *p_h264_scaling_matrix;
+ struct v4l2_ctrl_av1_sequence *p_av1_sequence;
void *p = ptr.p + idx * ctrl->elem_size;
if (ctrl->p_def.p_const)
@@ -152,6 +151,23 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
p_vp8_frame = p;
p_vp8_frame->num_dct_parts = 1;
break;
+ case V4L2_CTRL_TYPE_VP9_FRAME:
+ p_vp9_frame = p;
+ p_vp9_frame->profile = 0;
+ p_vp9_frame->bit_depth = 8;
+ p_vp9_frame->flags |= V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING |
+ V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING;
+ break;
+ case V4L2_CTRL_TYPE_AV1_SEQUENCE:
+ p_av1_sequence = p;
+ /*
+ * The initial profile is 0 which only allows YUV 420 subsampled
+ * data. Set the subsampling flags accordingly.
+ */
+ p_av1_sequence->bit_depth = 8;
+ p_av1_sequence->flags |= V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y;
+ break;
case V4L2_CTRL_TYPE_FWHT_PARAMS:
p_fwht_params = p;
p_fwht_params->version = V4L2_FWHT_VERSION;
@@ -160,48 +176,155 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
p_fwht_params->flags = V4L2_FWHT_FL_PIXENC_YUV |
(2 << V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET);
break;
+ case V4L2_CTRL_TYPE_H264_SCALING_MATRIX:
+ p_h264_scaling_matrix = p;
+ /*
+ * The default (flat) H.264 scaling matrix when none are
+ * specified in the bitstream, this is according to formulas
+ * (7-8) and (7-9) of the specification.
+ */
+ memset(p_h264_scaling_matrix, 16, sizeof(*p_h264_scaling_matrix));
+ break;
}
}
-static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
- union v4l2_ctrl_ptr ptr)
+static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
+ union v4l2_ctrl_ptr ptr)
+{
+ void *p = ptr.p + idx * ctrl->elem_size;
+
+ if (ctrl->p_min.p_const)
+ memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
+ else
+ memset(p, 0, ctrl->elem_size);
+}
+
+static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
+ union v4l2_ctrl_ptr ptr)
{
+ void *p = ptr.p + idx * ctrl->elem_size;
+
+ if (ctrl->p_max.p_const)
+ memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
+ else
+ memset(p, 0, ctrl->elem_size);
+}
+
+static void __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
+ u32 which, union v4l2_ctrl_ptr ptr)
+{
+ unsigned int i;
+ u32 tot_elems = ctrl->elems;
+ u32 elems = tot_elems - from_idx;
+ s64 value;
+
+ switch (which) {
+ case V4L2_CTRL_WHICH_DEF_VAL:
+ value = ctrl->default_value;
+ break;
+ case V4L2_CTRL_WHICH_MAX_VAL:
+ value = ctrl->maximum;
+ break;
+ case V4L2_CTRL_WHICH_MIN_VAL:
+ value = ctrl->minimum;
+ break;
+ default:
+ return;
+ }
+
switch (ctrl->type) {
case V4L2_CTRL_TYPE_STRING:
- idx *= ctrl->elem_size;
- memset(ptr.p_char + idx, ' ', ctrl->minimum);
- ptr.p_char[idx + ctrl->minimum] = '\0';
+ if (which == V4L2_CTRL_WHICH_DEF_VAL)
+ value = ctrl->minimum;
+
+ for (i = from_idx; i < tot_elems; i++) {
+ unsigned int offset = i * ctrl->elem_size;
+
+ memset(ptr.p_char + offset, ' ', value);
+ ptr.p_char[offset + value] = '\0';
+ }
break;
case V4L2_CTRL_TYPE_INTEGER64:
- ptr.p_s64[idx] = ctrl->default_value;
+ if (value) {
+ for (i = from_idx; i < tot_elems; i++)
+ ptr.p_s64[i] = value;
+ } else {
+ memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
+ }
break;
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_MENU:
case V4L2_CTRL_TYPE_BITMASK:
case V4L2_CTRL_TYPE_BOOLEAN:
- ptr.p_s32[idx] = ctrl->default_value;
+ if (value) {
+ for (i = from_idx; i < tot_elems; i++)
+ ptr.p_s32[i] = value;
+ } else {
+ memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
+ }
break;
case V4L2_CTRL_TYPE_BUTTON:
case V4L2_CTRL_TYPE_CTRL_CLASS:
- ptr.p_s32[idx] = 0;
+ memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
break;
case V4L2_CTRL_TYPE_U8:
- ptr.p_u8[idx] = ctrl->default_value;
+ memset(ptr.p_u8 + from_idx, value, elems);
break;
case V4L2_CTRL_TYPE_U16:
- ptr.p_u16[idx] = ctrl->default_value;
+ if (value) {
+ for (i = from_idx; i < tot_elems; i++)
+ ptr.p_u16[i] = value;
+ } else {
+ memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
+ }
break;
case V4L2_CTRL_TYPE_U32:
- ptr.p_u32[idx] = ctrl->default_value;
+ if (value) {
+ for (i = from_idx; i < tot_elems; i++)
+ ptr.p_u32[i] = value;
+ } else {
+ memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
+ }
break;
default:
- std_init_compound(ctrl, idx, ptr);
+ for (i = from_idx; i < tot_elems; i++) {
+ switch (which) {
+ case V4L2_CTRL_WHICH_DEF_VAL:
+ std_init_compound(ctrl, i, ptr);
+ break;
+ case V4L2_CTRL_WHICH_MAX_VAL:
+ std_max_compound(ctrl, i, ptr);
+ break;
+ case V4L2_CTRL_WHICH_MIN_VAL:
+ std_min_compound(ctrl, i, ptr);
+ break;
+ }
+ }
break;
}
}
-static void std_log(const struct v4l2_ctrl *ctrl)
+void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
+ union v4l2_ctrl_ptr ptr)
+{
+ __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
+}
+EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
+
+static void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl,
+ u32 from_idx, union v4l2_ctrl_ptr ptr)
+{
+ __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
+}
+
+static void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl,
+ u32 from_idx, union v4l2_ctrl_ptr ptr)
+{
+ __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
+}
+
+void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
{
union v4l2_ctrl_ptr ptr = ctrl->p_cur;
@@ -244,6 +367,9 @@ static void std_log(const struct v4l2_ctrl *ctrl)
case V4L2_CTRL_TYPE_U32:
pr_cont("%u", (unsigned)*ptr.p_u32);
break;
+ case V4L2_CTRL_TYPE_AREA:
+ pr_cont("%ux%u", ptr.p_area->width, ptr.p_area->height);
+ break;
case V4L2_CTRL_TYPE_H264_SPS:
pr_cont("H264_SPS");
break;
@@ -289,11 +415,44 @@ static void std_log(const struct v4l2_ctrl *ctrl)
case V4L2_CTRL_TYPE_VP9_FRAME:
pr_cont("VP9_FRAME");
break;
+ case V4L2_CTRL_TYPE_HEVC_SPS:
+ pr_cont("HEVC_SPS");
+ break;
+ case V4L2_CTRL_TYPE_HEVC_PPS:
+ pr_cont("HEVC_PPS");
+ break;
+ case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
+ pr_cont("HEVC_SLICE_PARAMS");
+ break;
+ case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
+ pr_cont("HEVC_SCALING_MATRIX");
+ break;
+ case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
+ pr_cont("HEVC_DECODE_PARAMS");
+ break;
+ case V4L2_CTRL_TYPE_AV1_SEQUENCE:
+ pr_cont("AV1_SEQUENCE");
+ break;
+ case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
+ pr_cont("AV1_TILE_GROUP_ENTRY");
+ break;
+ case V4L2_CTRL_TYPE_AV1_FRAME:
+ pr_cont("AV1_FRAME");
+ break;
+ case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
+ pr_cont("AV1_FILM_GRAIN");
+ break;
+ case V4L2_CTRL_TYPE_RECT:
+ pr_cont("(%d,%d)/%ux%u",
+ ptr.p_rect->left, ptr.p_rect->top,
+ ptr.p_rect->width, ptr.p_rect->height);
+ break;
default:
pr_cont("unknown type %d", ctrl->type);
break;
}
}
+EXPORT_SYMBOL(v4l2_ctrl_type_op_log);
/*
* Round towards the closest legal value. Be careful when we are
@@ -382,7 +541,7 @@ validate_vp9_seg_params(struct v4l2_vp9_segmentation *seg)
}
for (i = 0; i < ARRAY_SIZE(seg->feature_data); i++) {
- const int range[] = { 255, 63, 3, 0 };
+ static const int range[] = { 255, 63, 3, 0 };
for (j = 0; j < ARRAY_SIZE(seg->feature_data[j]); j++) {
if (seg->feature_data[i][j] < -range[j] ||
@@ -485,9 +644,310 @@ validate_vp9_frame(struct v4l2_ctrl_vp9_frame *frame)
return 0;
}
+static int validate_av1_quantization(struct v4l2_av1_quantization *q)
+{
+ if (q->flags > GENMASK(2, 0))
+ return -EINVAL;
+
+ if (q->delta_q_y_dc < -64 || q->delta_q_y_dc > 63 ||
+ q->delta_q_u_dc < -64 || q->delta_q_u_dc > 63 ||
+ q->delta_q_v_dc < -64 || q->delta_q_v_dc > 63 ||
+ q->delta_q_u_ac < -64 || q->delta_q_u_ac > 63 ||
+ q->delta_q_v_ac < -64 || q->delta_q_v_ac > 63 ||
+ q->delta_q_res > GENMASK(1, 0))
+ return -EINVAL;
+
+ if (q->qm_y > GENMASK(3, 0) ||
+ q->qm_u > GENMASK(3, 0) ||
+ q->qm_v > GENMASK(3, 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int validate_av1_segmentation(struct v4l2_av1_segmentation *s)
+{
+ u32 i;
+ u32 j;
+
+ if (s->flags > GENMASK(4, 0))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(s->feature_data); i++) {
+ static const int segmentation_feature_signed[] = { 1, 1, 1, 1, 1, 0, 0, 0 };
+ static const int segmentation_feature_max[] = { 255, 63, 63, 63, 63, 7, 0, 0};
+
+ for (j = 0; j < ARRAY_SIZE(s->feature_data[j]); j++) {
+ s32 limit = segmentation_feature_max[j];
+
+ if (segmentation_feature_signed[j]) {
+ if (s->feature_data[i][j] < -limit ||
+ s->feature_data[i][j] > limit)
+ return -EINVAL;
+ } else {
+ if (s->feature_data[i][j] < 0 || s->feature_data[i][j] > limit)
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int validate_av1_loop_filter(struct v4l2_av1_loop_filter *lf)
+{
+ u32 i;
+
+ if (lf->flags > GENMASK(3, 0))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(lf->level); i++) {
+ if (lf->level[i] > GENMASK(5, 0))
+ return -EINVAL;
+ }
+
+ if (lf->sharpness > GENMASK(2, 0))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(lf->ref_deltas); i++) {
+ if (lf->ref_deltas[i] < -64 || lf->ref_deltas[i] > 63)
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lf->mode_deltas); i++) {
+ if (lf->mode_deltas[i] < -64 || lf->mode_deltas[i] > 63)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_av1_cdef(struct v4l2_av1_cdef *cdef)
+{
+ u32 i;
+
+ if (cdef->damping_minus_3 > GENMASK(1, 0) ||
+ cdef->bits > GENMASK(1, 0))
+ return -EINVAL;
+
+ for (i = 0; i < 1 << cdef->bits; i++) {
+ if (cdef->y_pri_strength[i] > GENMASK(3, 0) ||
+ cdef->y_sec_strength[i] > 4 ||
+ cdef->uv_pri_strength[i] > GENMASK(3, 0) ||
+ cdef->uv_sec_strength[i] > 4)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_av1_loop_restauration(struct v4l2_av1_loop_restoration *lr)
+{
+ if (lr->lr_unit_shift > 3 || lr->lr_uv_shift > 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int validate_av1_film_grain(struct v4l2_ctrl_av1_film_grain *fg)
+{
+ u32 i;
+
+ if (fg->flags > GENMASK(4, 0))
+ return -EINVAL;
+
+ if (fg->film_grain_params_ref_idx > GENMASK(2, 0) ||
+ fg->num_y_points > 14 ||
+ fg->num_cb_points > 10 ||
+ fg->num_cr_points > GENMASK(3, 0) ||
+ fg->grain_scaling_minus_8 > GENMASK(1, 0) ||
+ fg->ar_coeff_lag > GENMASK(1, 0) ||
+ fg->ar_coeff_shift_minus_6 > GENMASK(1, 0) ||
+ fg->grain_scale_shift > GENMASK(1, 0))
+ return -EINVAL;
+
+ if (!(fg->flags & V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN))
+ return 0;
+
+ for (i = 1; i < fg->num_y_points; i++)
+ if (fg->point_y_value[i] <= fg->point_y_value[i - 1])
+ return -EINVAL;
+
+ for (i = 1; i < fg->num_cb_points; i++)
+ if (fg->point_cb_value[i] <= fg->point_cb_value[i - 1])
+ return -EINVAL;
+
+ for (i = 1; i < fg->num_cr_points; i++)
+ if (fg->point_cr_value[i] <= fg->point_cr_value[i - 1])
+ return -EINVAL;
+
+ return 0;
+}
+
+static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f)
+{
+ int ret = 0;
+
+ ret = validate_av1_quantization(&f->quantization);
+ if (ret)
+ return ret;
+ ret = validate_av1_segmentation(&f->segmentation);
+ if (ret)
+ return ret;
+ ret = validate_av1_loop_filter(&f->loop_filter);
+ if (ret)
+ return ret;
+ ret = validate_av1_cdef(&f->cdef);
+ if (ret)
+ return ret;
+ ret = validate_av1_loop_restauration(&f->loop_restoration);
+ if (ret)
+ return ret;
+
+ if (f->flags &
+ ~(V4L2_AV1_FRAME_FLAG_SHOW_FRAME |
+ V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME |
+ V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE |
+ V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE |
+ V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS |
+ V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV |
+ V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC |
+ V4L2_AV1_FRAME_FLAG_USE_SUPERRES |
+ V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV |
+ V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE |
+ V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS |
+ V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF |
+ V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION |
+ V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT |
+ V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET |
+ V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED |
+ V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT |
+ V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE |
+ V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT |
+ V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING))
+ return -EINVAL;
+
+ if (f->superres_denom > GENMASK(2, 0) + 9)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * validate_av1_sequence - validate AV1 sequence header fields
+ * @s: control struct from userspace
+ *
+ * Implements AV1 spec §5.5.2 color_config() checks that are
+ * possible with the current v4l2_ctrl_av1_sequence definition.
+ *
+ * TODO: extend validation once additional fields such as
+ * color_primaries, transfer_characteristics,
+ * matrix_coefficients, and chroma_sample_position
+ * are added to the uAPI.
+ *
+ * Returns 0 if valid, -EINVAL otherwise.
+ */
+static int validate_av1_sequence(struct v4l2_ctrl_av1_sequence *s)
+{
+ const bool mono = s->flags & V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME;
+ const bool sx = s->flags & V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X;
+ const bool sy = s->flags & V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y;
+ const bool uv_dq = s->flags & V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q;
+
+ /* 1. Reject unknown flags */
+ if (s->flags &
+ ~(V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE |
+ V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION |
+ V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME |
+ V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y |
+ V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT |
+ V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q))
+ return -EINVAL;
+
+ /* 2. Profile range */
+ if (s->seq_profile > 2)
+ return -EINVAL;
+
+ /* 3. Monochrome shortcut */
+ if (mono) {
+ /* Profile 1 forbids monochrome */
+ if (s->seq_profile == 1)
+ return -EINVAL;
+
+ /* Mono → subsampling must look like 4:0:0: sx=1, sy=1 */
+ if (!sx || !sy)
+ return -EINVAL;
+
+ /* separate_uv_delta_q must be 0 */
+ if (uv_dq)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* 4. Profile-specific rules */
+ switch (s->seq_profile) {
+ case 0:
+ /* Profile 0: only 8/10-bit, subsampling=4:2:0 (sx=1, sy=1) */
+ if (s->bit_depth != 8 && s->bit_depth != 10)
+ return -EINVAL;
+ if (!(sx && sy))
+ return -EINVAL;
+ break;
+
+ case 1:
+ /* Profile 1: only 8/10-bit, subsampling=4:4:4 (sx=0, sy=0) */
+ if (s->bit_depth != 8 && s->bit_depth != 10)
+ return -EINVAL;
+ if (sx || sy)
+ return -EINVAL;
+ break;
+
+ case 2:
+ /* Profile 2: 8/10/12-bit allowed */
+ if (s->bit_depth != 8 && s->bit_depth != 10 &&
+ s->bit_depth != 12)
+ return -EINVAL;
+
+ if (s->bit_depth == 12) {
+ if (!sx) {
+ /* 4:4:4 → sy must be 0 */
+ if (sy)
+ return -EINVAL;
+ } else {
+ /* sx=1 → sy=0 (4:2:2) or sy=1 (4:2:0) */
+ if (sy != 0 && sy != 1)
+ return -EINVAL;
+ }
+ } else {
+ /* 8/10-bit → only 4:2:2 allowed (sx=1, sy=0) */
+ if (!(sx && !sy))
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return 0;
+}
+
/*
* Compound controls validation requires setting unused fields/flags to zero
- * in order to properly detect unchanged controls with std_equal's memcmp.
+ * in order to properly detect unchanged controls with v4l2_ctrl_type_op_equal's
+ * memcmp.
*/
static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
union v4l2_ctrl_ptr ptr)
@@ -503,10 +963,10 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
struct v4l2_ctrl_h264_decode_params *p_h264_dec_params;
struct v4l2_ctrl_hevc_sps *p_hevc_sps;
struct v4l2_ctrl_hevc_pps *p_hevc_pps;
- struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
struct v4l2_area *area;
+ struct v4l2_rect *rect;
void *p = ptr.p + idx * ctrl->elem_size;
unsigned int i;
@@ -589,12 +1049,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS;
-
- if (p_h264_sps->chroma_format_idc < 3)
- p_h264_sps->flags &=
- ~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
}
+ if (p_h264_sps->chroma_format_idc < 3)
+ p_h264_sps->flags &=
+ ~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
+
if (p_h264_sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD;
@@ -781,8 +1241,6 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
p_hevc_pps->pps_beta_offset_div2 = 0;
p_hevc_pps->pps_tc_offset_div2 = 0;
}
-
- zero_padding(*p_hevc_pps);
break;
case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
@@ -791,21 +1249,9 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
if (p_hevc_decode_params->num_active_dpb_entries >
V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
return -EINVAL;
-
- for (i = 0; i < p_hevc_decode_params->num_active_dpb_entries;
- i++) {
- struct v4l2_hevc_dpb_entry *dpb_entry =
- &p_hevc_decode_params->dpb[i];
-
- zero_padding(*dpb_entry);
- }
break;
case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
- p_hevc_slice_params = p;
-
- zero_padding(p_hevc_slice_params->pred_weight_table);
- zero_padding(*p_hevc_slice_params);
break;
case V4L2_CTRL_TYPE_HDR10_CLL_INFO:
@@ -863,6 +1309,14 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
case V4L2_CTRL_TYPE_VP9_FRAME:
return validate_vp9_frame(p);
+ case V4L2_CTRL_TYPE_AV1_FRAME:
+ return validate_av1_frame(p);
+ case V4L2_CTRL_TYPE_AV1_SEQUENCE:
+ return validate_av1_sequence(p);
+ case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
+ break;
+ case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
+ return validate_av1_film_grain(p);
case V4L2_CTRL_TYPE_AREA:
area = p;
@@ -870,6 +1324,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
return -EINVAL;
break;
+ case V4L2_CTRL_TYPE_RECT:
+ rect = p;
+ if (!rect->width || !rect->height)
+ return -EINVAL;
+ break;
+
default:
return -EINVAL;
}
@@ -877,8 +1337,8 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
return 0;
}
-static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
- union v4l2_ctrl_ptr ptr)
+static int std_validate_elem(const struct v4l2_ctrl *ctrl, u32 idx,
+ union v4l2_ctrl_ptr ptr)
{
size_t len;
u64 offset;
@@ -948,11 +1408,45 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
}
}
+int v4l2_ctrl_type_op_validate(const struct v4l2_ctrl *ctrl,
+ union v4l2_ctrl_ptr ptr)
+{
+ unsigned int i;
+ int ret = 0;
+
+ switch ((u32)ctrl->type) {
+ case V4L2_CTRL_TYPE_U8:
+ if (ctrl->maximum == 0xff && ctrl->minimum == 0 && ctrl->step == 1)
+ return 0;
+ break;
+ case V4L2_CTRL_TYPE_U16:
+ if (ctrl->maximum == 0xffff && ctrl->minimum == 0 && ctrl->step == 1)
+ return 0;
+ break;
+ case V4L2_CTRL_TYPE_U32:
+ if (ctrl->maximum == 0xffffffff && ctrl->minimum == 0 && ctrl->step == 1)
+ return 0;
+ break;
+
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ memset(ptr.p_s32, 0, ctrl->new_elems * sizeof(s32));
+ return 0;
+ }
+
+ for (i = 0; !ret && i < ctrl->new_elems; i++)
+ ret = std_validate_elem(ctrl, i, ptr);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
+
static const struct v4l2_ctrl_type_ops std_type_ops = {
- .equal = std_equal,
- .init = std_init,
- .log = std_log,
- .validate = std_validate,
+ .equal = v4l2_ctrl_type_op_equal,
+ .init = v4l2_ctrl_type_op_init,
+ .minimum = v4l2_ctrl_type_op_minimum,
+ .maximum = v4l2_ctrl_type_op_maximum,
+ .log = v4l2_ctrl_type_op_log,
+ .validate = v4l2_ctrl_type_op_validate,
};
void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv)
@@ -973,11 +1467,12 @@ EXPORT_SYMBOL(v4l2_ctrl_notify);
/* Copy the one value to another. */
static void ptr_to_ptr(struct v4l2_ctrl *ctrl,
- union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to)
+ union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to,
+ unsigned int elems)
{
if (ctrl == NULL)
return;
- memcpy(to.p, from.p_const, ctrl->elems * ctrl->elem_size);
+ memcpy(to.p, from.p_const, elems * ctrl->elem_size);
}
/* Copy the new value to the current value. */
@@ -990,8 +1485,11 @@ void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
/* has_changed is set by cluster_changed */
changed = ctrl->has_changed;
- if (changed)
- ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur);
+ if (changed) {
+ if (ctrl->is_dyn_array)
+ ctrl->elems = ctrl->new_elems;
+ ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur, ctrl->elems);
+ }
if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) {
/* Note: CH_FLAGS is only set for auto clusters. */
@@ -1021,36 +1519,129 @@ void cur_to_new(struct v4l2_ctrl *ctrl)
{
if (ctrl == NULL)
return;
- ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
+ if (ctrl->is_dyn_array)
+ ctrl->new_elems = ctrl->elems;
+ ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems);
+}
+
+static bool req_alloc_array(struct v4l2_ctrl_ref *ref, u32 elems)
+{
+ void *tmp;
+
+ if (elems == ref->p_req_array_alloc_elems)
+ return true;
+ if (ref->ctrl->is_dyn_array &&
+ elems < ref->p_req_array_alloc_elems)
+ return true;
+
+ tmp = kvmalloc(elems * ref->ctrl->elem_size, GFP_KERNEL);
+
+ if (!tmp) {
+ ref->p_req_array_enomem = true;
+ return false;
+ }
+ ref->p_req_array_enomem = false;
+ kvfree(ref->p_req.p);
+ ref->p_req.p = tmp;
+ ref->p_req_array_alloc_elems = elems;
+ return true;
}
/* Copy the new value to the request value */
void new_to_req(struct v4l2_ctrl_ref *ref)
{
+ struct v4l2_ctrl *ctrl;
+
if (!ref)
return;
- ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req);
- ref->valid_p_req = true;
+
+ ctrl = ref->ctrl;
+ if (ctrl->is_array && !req_alloc_array(ref, ctrl->new_elems))
+ return;
+
+ ref->p_req_elems = ctrl->new_elems;
+ ptr_to_ptr(ctrl, ctrl->p_new, ref->p_req, ref->p_req_elems);
+ ref->p_req_valid = true;
}
/* Copy the current value to the request value */
void cur_to_req(struct v4l2_ctrl_ref *ref)
{
+ struct v4l2_ctrl *ctrl;
+
if (!ref)
return;
- ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->p_req);
- ref->valid_p_req = true;
+
+ ctrl = ref->ctrl;
+ if (ctrl->is_array && !req_alloc_array(ref, ctrl->elems))
+ return;
+
+ ref->p_req_elems = ctrl->elems;
+ ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req, ctrl->elems);
+ ref->p_req_valid = true;
}
/* Copy the request value to the new value */
-void req_to_new(struct v4l2_ctrl_ref *ref)
+int req_to_new(struct v4l2_ctrl_ref *ref)
{
+ struct v4l2_ctrl *ctrl;
+
if (!ref)
- return;
- if (ref->valid_p_req)
- ptr_to_ptr(ref->ctrl, ref->p_req, ref->ctrl->p_new);
- else
- ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new);
+ return 0;
+
+ ctrl = ref->ctrl;
+
+ /*
+ * This control was never set in the request, so just use the current
+ * value.
+ */
+ if (!ref->p_req_valid) {
+ if (ctrl->is_dyn_array)
+ ctrl->new_elems = ctrl->elems;
+ ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems);
+ return 0;
+ }
+
+ /* Not an array, so just copy the request value */
+ if (!ctrl->is_array) {
+ ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems);
+ return 0;
+ }
+
+ /* Sanity check, should never happen */
+ if (WARN_ON(!ref->p_req_array_alloc_elems))
+ return -ENOMEM;
+
+ if (!ctrl->is_dyn_array &&
+ ref->p_req_elems != ctrl->p_array_alloc_elems)
+ return -ENOMEM;
+
+ /*
+ * Check if the number of elements in the request is more than the
+ * elements in ctrl->p_array. If so, attempt to realloc ctrl->p_array.
+ * Note that p_array is allocated with twice the number of elements
+ * in the dynamic array since it has to store both the current and
+ * new value of such a control.
+ */
+ if (ref->p_req_elems > ctrl->p_array_alloc_elems) {
+ unsigned int sz = ref->p_req_elems * ctrl->elem_size;
+ void *old = ctrl->p_array;
+ void *tmp = kvzalloc(2 * sz, GFP_KERNEL);
+
+ if (!tmp)
+ return -ENOMEM;
+ memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size);
+ memcpy(tmp + sz, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size);
+ ctrl->p_new.p = tmp;
+ ctrl->p_cur.p = tmp + sz;
+ ctrl->p_array = tmp;
+ ctrl->p_array_alloc_elems = ref->p_req_elems;
+ kvfree(old);
+ }
+
+ ctrl->new_elems = ref->p_req_elems;
+ ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems);
+ return 0;
}
/* Control range checking */
@@ -1076,11 +1667,12 @@ int check_range(enum v4l2_ctrl_type type,
return 0;
case V4L2_CTRL_TYPE_MENU:
case V4L2_CTRL_TYPE_INTEGER_MENU:
- if (min > max || def < min || def > max)
+ if (min > max || def < min || def > max ||
+ min < 0 || (step && max >= BITS_PER_LONG_LONG))
return -ERANGE;
/* Note: step == menu_skip_mask for menu controls.
So here we check if the default value is masked out. */
- if (step && ((1 << def) & step))
+ if (def < BITS_PER_LONG_LONG && (step & BIT_ULL(def)))
return -EINVAL;
return 0;
case V4L2_CTRL_TYPE_STRING:
@@ -1092,17 +1684,6 @@ int check_range(enum v4l2_ctrl_type type,
}
}
-/* Validate a new control */
-int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new)
-{
- unsigned idx;
- int err = 0;
-
- for (idx = 0; !err && idx < ctrl->elems; idx++)
- err = ctrl->type_ops->validate(ctrl, idx, p_new);
- return err;
-}
-
/* Set the handler's error code if it wasn't set earlier already */
static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
{
@@ -1122,9 +1703,8 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
INIT_LIST_HEAD(&hdl->ctrls);
INIT_LIST_HEAD(&hdl->ctrl_refs);
hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
- hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
- sizeof(hdl->buckets[0]),
- GFP_KERNEL | __GFP_ZERO);
+ hdl->buckets = kvcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]),
+ GFP_KERNEL);
hdl->error = hdl->buckets ? 0 : -ENOMEM;
v4l2_ctrl_handler_init_request(hdl);
return hdl->error;
@@ -1132,14 +1712,17 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
EXPORT_SYMBOL(v4l2_ctrl_handler_init_class);
/* Free all controls and control refs */
-void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+int v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
{
struct v4l2_ctrl_ref *ref, *next_ref;
struct v4l2_ctrl *ctrl, *next_ctrl;
struct v4l2_subscribed_event *sev, *next_sev;
- if (hdl == NULL || hdl->buckets == NULL)
- return;
+ if (!hdl)
+ return 0;
+
+ if (!hdl->buckets)
+ return hdl->error;
v4l2_ctrl_handler_free_request(hdl);
@@ -1147,6 +1730,8 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
/* Free all nodes */
list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
list_del(&ref->node);
+ if (ref->p_req_array_alloc_elems)
+ kvfree(ref->p_req.p);
kfree(ref);
}
/* Free all controls owned by the handler */
@@ -1154,14 +1739,16 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
list_del(&ctrl->node);
list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
list_del(&sev->node);
+ kvfree(ctrl->p_array);
kvfree(ctrl);
}
kvfree(hdl->buckets);
hdl->buckets = NULL;
hdl->cached = NULL;
- hdl->error = 0;
mutex_unlock(hdl->lock);
mutex_destroy(&hdl->_lock);
+
+ return hdl->error;
}
EXPORT_SYMBOL(v4l2_ctrl_handler_free);
@@ -1269,7 +1856,7 @@ int handler_new_ref(struct v4l2_ctrl_handler *hdl,
if (hdl->error)
return hdl->error;
- if (allocate_req)
+ if (allocate_req && !ctrl->is_array)
size_extra_req = ctrl->elems * ctrl->elem_size;
new_ref = kzalloc(sizeof(*new_ref) + size_extra_req, GFP_KERNEL);
if (!new_ref)
@@ -1334,7 +1921,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
s64 min, s64 max, u64 step, s64 def,
const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
u32 flags, const char * const *qmenu,
- const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
+ const s64 *qmenu_int,
+ const union v4l2_ctrl_ptr p_def,
+ const union v4l2_ctrl_ptr p_min,
+ const union v4l2_ctrl_ptr p_max,
void *priv)
{
struct v4l2_ctrl *ctrl;
@@ -1343,7 +1933,6 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
unsigned elems = 1;
bool is_array;
unsigned tot_ctrl_size;
- unsigned idx;
void *data;
int err;
@@ -1435,15 +2024,35 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
case V4L2_CTRL_TYPE_VP9_FRAME:
elem_size = sizeof(struct v4l2_ctrl_vp9_frame);
break;
+ case V4L2_CTRL_TYPE_AV1_SEQUENCE:
+ elem_size = sizeof(struct v4l2_ctrl_av1_sequence);
+ break;
+ case V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY:
+ elem_size = sizeof(struct v4l2_ctrl_av1_tile_group_entry);
+ break;
+ case V4L2_CTRL_TYPE_AV1_FRAME:
+ elem_size = sizeof(struct v4l2_ctrl_av1_frame);
+ break;
+ case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
+ elem_size = sizeof(struct v4l2_ctrl_av1_film_grain);
+ break;
case V4L2_CTRL_TYPE_AREA:
elem_size = sizeof(struct v4l2_area);
break;
+ case V4L2_CTRL_TYPE_RECT:
+ elem_size = sizeof(struct v4l2_rect);
+ break;
default:
if (type < V4L2_CTRL_COMPOUND_TYPES)
elem_size = sizeof(s32);
break;
}
- tot_ctrl_size = elem_size * elems;
+
+ if (type < V4L2_CTRL_COMPOUND_TYPES &&
+ type != V4L2_CTRL_TYPE_BUTTON &&
+ type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+ type != V4L2_CTRL_TYPE_STRING)
+ flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
/* Sanity checks */
if (id == 0 || name == NULL || !elem_size ||
@@ -1453,6 +2062,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
handler_set_err(hdl, -ERANGE);
return NULL;
}
+
err = check_range(type, min, max, step, def);
if (err) {
handler_set_err(hdl, err);
@@ -1464,21 +2074,40 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
handler_set_err(hdl, -EINVAL);
return NULL;
}
+ if (flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) {
+ /*
+ * For now only support this for one-dimensional arrays only.
+ *
+ * This can be relaxed in the future, but this will
+ * require more effort.
+ */
+ if (nr_of_dims != 1) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ /* Start with just 1 element */
+ elems = 1;
+ }
+ tot_ctrl_size = elem_size * elems;
sz_extra = 0;
if (type == V4L2_CTRL_TYPE_BUTTON)
flags |= V4L2_CTRL_FLAG_WRITE_ONLY |
V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
flags |= V4L2_CTRL_FLAG_READ_ONLY;
- else if (type == V4L2_CTRL_TYPE_INTEGER64 ||
- type == V4L2_CTRL_TYPE_STRING ||
- type >= V4L2_CTRL_COMPOUND_TYPES ||
- is_array)
+ else if (!is_array &&
+ (type == V4L2_CTRL_TYPE_INTEGER64 ||
+ type == V4L2_CTRL_TYPE_STRING ||
+ type >= V4L2_CTRL_COMPOUND_TYPES))
sz_extra += 2 * tot_ctrl_size;
if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
sz_extra += elem_size;
+ if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
+ sz_extra += elem_size;
+ if (type >= V4L2_CTRL_COMPOUND_TYPES && p_max.p_const)
+ sz_extra += elem_size;
ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
if (ctrl == NULL) {
@@ -1503,7 +2132,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string;
ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64;
ctrl->is_array = is_array;
+ ctrl->is_dyn_array = !!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY);
ctrl->elems = elems;
+ ctrl->new_elems = elems;
ctrl->nr_of_dims = nr_of_dims;
if (nr_of_dims)
memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0]));
@@ -1516,6 +2147,16 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
ctrl->cur.val = ctrl->val = def;
data = &ctrl[1];
+ if (ctrl->is_array) {
+ ctrl->p_array_alloc_elems = elems;
+ ctrl->p_array = kvzalloc(2 * elems * elem_size, GFP_KERNEL);
+ if (!ctrl->p_array) {
+ kvfree(ctrl);
+ return NULL;
+ }
+ data = ctrl->p_array;
+ }
+
if (!ctrl->is_int) {
ctrl->p_new.p = data;
ctrl->p_cur.p = data + tot_ctrl_size;
@@ -1525,16 +2166,34 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
}
if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) {
- ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
+ if (ctrl->is_array)
+ ctrl->p_def.p = &ctrl[1];
+ else
+ ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
}
- for (idx = 0; idx < elems; idx++) {
- ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
- ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
+ if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) {
+ void *ptr = ctrl->p_def.p;
+
+ if (p_min.p_const) {
+ ptr += elem_size;
+ ctrl->p_min.p = ptr;
+ memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
+ }
+
+ if (p_max.p_const) {
+ ptr += elem_size;
+ ctrl->p_max.p = ptr;
+ memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
+ }
}
+ ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
+ cur_to_new(ctrl);
+
if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
+ kvfree(ctrl->p_array);
kvfree(ctrl);
return NULL;
}
@@ -1580,7 +2239,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
type, min, max,
is_menu ? cfg->menu_skip_mask : step, def,
cfg->dims, cfg->elem_size,
- flags, qmenu, qmenu_int, cfg->p_def, priv);
+ flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
+ cfg->p_max, priv);
if (ctrl)
ctrl->is_private = cfg->is_private;
return ctrl;
@@ -1605,7 +2265,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
}
return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
min, max, step, def, NULL, 0,
- flags, NULL, NULL, ptr_null, NULL);
+ flags, NULL, NULL, ptr_null, ptr_null,
+ ptr_null, NULL);
}
EXPORT_SYMBOL(v4l2_ctrl_new_std);
@@ -1632,13 +2293,14 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
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)) {
+ if ((!qmenu && !qmenu_int) || (qmenu_int && max >= qmenu_int_len)) {
handler_set_err(hdl, -EINVAL);
return NULL;
}
return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
0, max, mask, def, NULL, 0,
- flags, qmenu, qmenu_int, ptr_null, NULL);
+ flags, qmenu, qmenu_int, ptr_null, ptr_null,
+ ptr_null, NULL);
}
EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
@@ -1670,7 +2332,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
}
return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
0, max, mask, def, NULL, 0,
- flags, qmenu, NULL, ptr_null, NULL);
+ flags, qmenu, NULL, ptr_null, ptr_null,
+ ptr_null, NULL);
}
EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
@@ -1678,7 +2341,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
/* Helper function for standard compound controls */
struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops, u32 id,
- const union v4l2_ctrl_ptr p_def)
+ const union v4l2_ctrl_ptr p_def,
+ const union v4l2_ctrl_ptr p_min,
+ const union v4l2_ctrl_ptr p_max)
{
const char *name;
enum v4l2_ctrl_type type;
@@ -1692,7 +2357,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
}
return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
min, max, step, def, NULL, 0,
- flags, NULL, NULL, p_def, NULL);
+ flags, NULL, NULL, p_def, p_min, p_max, NULL);
}
EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
@@ -1716,7 +2381,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
}
return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
0, max, 0, def, NULL, 0,
- flags, NULL, qmenu_int, ptr_null, NULL);
+ flags, NULL, qmenu_int, ptr_null, ptr_null,
+ ptr_null, NULL);
}
EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
@@ -1848,7 +2514,6 @@ void update_from_auto_cluster(struct v4l2_ctrl *master)
static int cluster_changed(struct v4l2_ctrl *master)
{
bool changed = false;
- unsigned int idx;
int i;
for (i = 0; i < master->ncontrols; i++) {
@@ -1872,8 +2537,10 @@ static int cluster_changed(struct v4l2_ctrl *master)
continue;
}
- for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++)
- ctrl_changed = !ctrl->type_ops->equal(ctrl, idx,
+ if (ctrl->elems != ctrl->new_elems)
+ ctrl_changed = true;
+ if (!ctrl_changed)
+ ctrl_changed = !ctrl->type_ops->equal(ctrl,
ctrl->p_cur, ctrl->p_new);
ctrl->has_changed = ctrl_changed;
changed |= ctrl->has_changed;
@@ -2096,6 +2763,9 @@ int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ctrl_ops,
const struct v4l2_fwnode_device_properties *p)
{
+ if (hdl->error)
+ return hdl->error;
+
if (p->orientation != V4L2_FWNODE_PROPERTY_UNSET) {
u32 orientation_ctrl;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
index 54ca4e6b820b..ad41f65374e2 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
@@ -2,7 +2,7 @@
/*
* V4L2 controls framework control definitions.
*
- * Copyright (C) 2010-2021 Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ * Copyright (C) 2010-2021 Hans Verkuil <hverkuil@kernel.org>
*/
#include <linux/export.h>
@@ -499,6 +499,40 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
NULL,
};
+ static const char * const av1_profile[] = {
+ "Main",
+ "High",
+ "Professional",
+ NULL,
+ };
+ static const char * const av1_level[] = {
+ "2.0",
+ "2.1",
+ "2.2",
+ "2.3",
+ "3.0",
+ "3.1",
+ "3.2",
+ "3.3",
+ "4.0",
+ "4.1",
+ "4.2",
+ "4.3",
+ "5.0",
+ "5.1",
+ "5.2",
+ "5.3",
+ "6.0",
+ "6.1",
+ "6.2",
+ "6.3",
+ "7.0",
+ "7.1",
+ "7.2",
+ "7.3",
+ NULL,
+ };
+
static const char * const hevc_profile[] = {
"Main",
"Main Still Picture",
@@ -572,6 +606,11 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
"VBV/CPB Limit",
NULL,
};
+ static const char * const intra_refresh_period_type[] = {
+ "Random",
+ "Cyclic",
+ NULL,
+ };
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -699,12 +738,18 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
return hevc_tier;
case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
return hevc_loop_filter_mode;
- case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:
+ case V4L2_CID_MPEG_VIDEO_AV1_PROFILE:
+ return av1_profile;
+ case V4L2_CID_MPEG_VIDEO_AV1_LEVEL:
+ return av1_level;
+ case V4L2_CID_STATELESS_HEVC_DECODE_MODE:
return hevc_decode_mode;
- case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:
+ case V4L2_CID_STATELESS_HEVC_START_CODE:
return hevc_start_code;
case V4L2_CID_CAMERA_ORIENTATION:
return camera_orientation;
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE:
+ return intra_refresh_period_type;
default:
return NULL;
}
@@ -834,6 +879,7 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface";
case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable";
case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs";
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE: return "Intra Refresh Period Type";
case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: return "Intra Refresh Period";
case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable";
case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control";
@@ -924,6 +970,7 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_LTR_COUNT: return "LTR Count";
case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: return "Frame LTR Index";
case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES: return "Use LTR Frames";
+ case V4L2_CID_MPEG_VIDEO_AVERAGE_QP: return "Average QP Value";
case V4L2_CID_FWHT_I_FRAME_QP: return "FWHT I-Frame QP Value";
case V4L2_CID_FWHT_P_FRAME_QP: return "FWHT P-Frame QP Value";
@@ -995,13 +1042,10 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field";
case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame";
case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR";
- case V4L2_CID_MPEG_VIDEO_HEVC_SPS: return "HEVC Sequence Parameter Set";
- case V4L2_CID_MPEG_VIDEO_HEVC_PPS: return "HEVC Picture Parameter Set";
- case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters";
- case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX: return "HEVC Scaling Matrix";
- case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS: return "HEVC Decode Parameters";
- case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: return "HEVC Decode Mode";
- case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return "HEVC Start Code";
+
+ /* AV1 controls */
+ case V4L2_CID_MPEG_VIDEO_AV1_PROFILE: return "AV1 Profile";
+ case V4L2_CID_MPEG_VIDEO_AV1_LEVEL: return "AV1 Level";
/* CAMERA controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -1042,6 +1086,7 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_UNIT_CELL_SIZE: return "Unit Cell Size";
case V4L2_CID_CAMERA_ORIENTATION: return "Camera Orientation";
case V4L2_CID_CAMERA_SENSOR_ROTATION: return "Camera Sensor Rotation";
+ case V4L2_CID_HDR_SENSOR_MODE: return "HDR Sensor Mode";
/* FM Radio Modulator controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -1180,6 +1225,18 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_STATELESS_MPEG2_QUANTISATION: return "MPEG-2 Quantisation Matrices";
case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR: return "VP9 Probabilities Updates";
case V4L2_CID_STATELESS_VP9_FRAME: return "VP9 Frame Decode Parameters";
+ case V4L2_CID_STATELESS_HEVC_SPS: return "HEVC Sequence Parameter Set";
+ case V4L2_CID_STATELESS_HEVC_PPS: return "HEVC Picture Parameter Set";
+ case V4L2_CID_STATELESS_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters";
+ case V4L2_CID_STATELESS_HEVC_SCALING_MATRIX: return "HEVC Scaling Matrix";
+ case V4L2_CID_STATELESS_HEVC_DECODE_PARAMS: return "HEVC Decode Parameters";
+ case V4L2_CID_STATELESS_HEVC_DECODE_MODE: return "HEVC Decode Mode";
+ case V4L2_CID_STATELESS_HEVC_START_CODE: return "HEVC Start Code";
+ case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS: return "HEVC Entry Point Offsets";
+ case V4L2_CID_STATELESS_AV1_SEQUENCE: return "AV1 Sequence Parameters";
+ case V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY: return "AV1 Tile Group Entry";
+ case V4L2_CID_STATELESS_AV1_FRAME: return "AV1 Frame Parameters";
+ case V4L2_CID_STATELESS_AV1_FILM_GRAIN: return "AV1 Film Grain";
/* Colorimetry controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -1355,11 +1412,15 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
- case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:
- case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:
+ case V4L2_CID_MPEG_VIDEO_AV1_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_AV1_LEVEL:
+ case V4L2_CID_STATELESS_HEVC_DECODE_MODE:
+ case V4L2_CID_STATELESS_HEVC_START_CODE:
case V4L2_CID_STATELESS_H264_DECODE_MODE:
case V4L2_CID_STATELESS_H264_START_CODE:
case V4L2_CID_CAMERA_ORIENTATION:
+ case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE:
+ case V4L2_CID_HDR_SENSOR_MODE:
*type = V4L2_CTRL_TYPE_MENU;
break;
case V4L2_CID_LINK_FREQ:
@@ -1447,6 +1508,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
*max = 0xffffffffffffLL;
*step = 1;
break;
+ case V4L2_CID_MPEG_VIDEO_AVERAGE_QP:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
case V4L2_CID_PIXEL_RATE:
*type = V4L2_CTRL_TYPE_INTEGER64;
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -1493,27 +1558,45 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_STATELESS_VP8_FRAME:
*type = V4L2_CTRL_TYPE_VP8_FRAME;
break;
- case V4L2_CID_MPEG_VIDEO_HEVC_SPS:
+ case V4L2_CID_STATELESS_HEVC_SPS:
*type = V4L2_CTRL_TYPE_HEVC_SPS;
break;
- case V4L2_CID_MPEG_VIDEO_HEVC_PPS:
+ case V4L2_CID_STATELESS_HEVC_PPS:
*type = V4L2_CTRL_TYPE_HEVC_PPS;
break;
- case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:
+ case V4L2_CID_STATELESS_HEVC_SLICE_PARAMS:
*type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS;
+ *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY;
break;
- case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:
+ case V4L2_CID_STATELESS_HEVC_SCALING_MATRIX:
*type = V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX;
break;
- case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS:
+ case V4L2_CID_STATELESS_HEVC_DECODE_PARAMS:
*type = V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS;
break;
+ case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS:
+ *type = V4L2_CTRL_TYPE_U32;
+ *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY;
+ break;
case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR:
*type = V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR;
break;
case V4L2_CID_STATELESS_VP9_FRAME:
*type = V4L2_CTRL_TYPE_VP9_FRAME;
break;
+ case V4L2_CID_STATELESS_AV1_SEQUENCE:
+ *type = V4L2_CTRL_TYPE_AV1_SEQUENCE;
+ break;
+ case V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY:
+ *type = V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY;
+ *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY;
+ break;
+ case V4L2_CID_STATELESS_AV1_FRAME:
+ *type = V4L2_CTRL_TYPE_AV1_FRAME;
+ break;
+ case V4L2_CID_STATELESS_AV1_FILM_GRAIN:
+ *type = V4L2_CTRL_TYPE_AV1_FILM_GRAIN;
+ break;
case V4L2_CID_UNIT_CELL_SIZE:
*type = V4L2_CTRL_TYPE_AREA;
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-priv.h b/drivers/media/v4l2-core/v4l2-ctrls-priv.h
index d4bf2c716f97..f4cf273ecf30 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-priv.h
+++ b/drivers/media/v4l2-core/v4l2-ctrls-priv.h
@@ -2,7 +2,7 @@
/*
* V4L2 controls framework private header.
*
- * Copyright (C) 2010-2021 Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ * Copyright (C) 2010-2021 Hans Verkuil <hverkuil@kernel.org>
*/
#ifndef _V4L2_CTRLS_PRIV_H_
@@ -57,10 +57,9 @@ void cur_to_new(struct v4l2_ctrl *ctrl);
void cur_to_req(struct v4l2_ctrl_ref *ref);
void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags);
void new_to_req(struct v4l2_ctrl_ref *ref);
-void req_to_new(struct v4l2_ctrl_ref *ref);
+int req_to_new(struct v4l2_ctrl_ref *ref);
void send_initial_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl);
void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes);
-int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new);
int handler_new_ref(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl *ctrl,
struct v4l2_ctrl_ref **ctrl_ref,
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c
index 7d098f287fd9..e77f722b36a4 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-request.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c
@@ -2,7 +2,7 @@
/*
* V4L2 controls framework Request API implementation.
*
- * Copyright (C) 2018-2021 Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ * Copyright (C) 2018-2021 Hans Verkuil <hverkuil@kernel.org>
*/
#define pr_fmt(fmt) "v4l2-ctrls: " fmt
@@ -143,7 +143,7 @@ v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
{
struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
- return (ref && ref->valid_p_req) ? ref->ctrl : NULL;
+ return (ref && ref->p_req_valid) ? ref->ctrl : NULL;
}
EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find);
@@ -373,7 +373,7 @@ void v4l2_ctrl_request_complete(struct media_request *req,
v4l2_ctrl_unlock(master);
continue;
}
- if (ref->valid_p_req)
+ if (ref->p_req_valid)
continue;
/* Copy the current control value into the request */
@@ -442,7 +442,7 @@ int v4l2_ctrl_request_setup(struct media_request *req,
struct v4l2_ctrl_ref *r =
find_ref(hdl, master->cluster[i]->id);
- if (r->valid_p_req) {
+ if (r->p_req_valid) {
have_new_data = true;
break;
}
@@ -458,7 +458,11 @@ int v4l2_ctrl_request_setup(struct media_request *req,
struct v4l2_ctrl_ref *r =
find_ref(hdl, master->cluster[i]->id);
- req_to_new(r);
+ ret = req_to_new(r);
+ if (ret) {
+ v4l2_ctrl_unlock(master);
+ goto error;
+ }
master->cluster[i]->is_new = 1;
r->req_done = true;
}
@@ -490,6 +494,7 @@ int v4l2_ctrl_request_setup(struct media_request *req,
break;
}
+error:
media_request_object_put(obj);
return ret;
}
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index d03ace324db0..10a126e50c1c 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -93,6 +93,8 @@ static struct attribute *video_device_attrs[] = {
};
ATTRIBUTE_GROUPS(video_device);
+static struct dentry *v4l2_debugfs_root_dir;
+
/*
* Active devices
*/
@@ -227,7 +229,7 @@ static void v4l2_device_release(struct device *cd)
v4l2_device_put(v4l2_dev);
}
-static struct class video_class = {
+static const struct class video_class = {
.name = VIDEO_NAME,
.dev_groups = video_device_groups,
};
@@ -409,7 +411,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
- int ret = 0;
+ int ret;
/* Check if the video device is available */
mutex_lock(&videodev_lock);
@@ -422,16 +424,27 @@ static int v4l2_open(struct inode *inode, struct file *filp)
/* and increase the device refcount */
video_get(vdev);
mutex_unlock(&videodev_lock);
- if (vdev->fops->open) {
- if (video_is_registered(vdev))
- ret = vdev->fops->open(filp);
- else
- ret = -ENODEV;
+
+ if (!video_is_registered(vdev)) {
+ ret = -ENODEV;
+ goto done;
}
+ ret = vdev->fops->open(filp);
+ if (ret)
+ goto done;
+
+ /* All drivers must use v4l2_fh. */
+ if (WARN_ON(!test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))) {
+ vdev->fops->release(filp);
+ ret = -ENODEV;
+ }
+
+done:
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
dprintk("%s: open (%d)\n",
video_device_node_name(vdev), ret);
+
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
@@ -442,7 +455,7 @@ static int v4l2_open(struct inode *inode, struct file *filp)
static int v4l2_release(struct inode *inode, struct file *filp)
{
struct video_device *vdev = video_devdata(filp);
- int ret = 0;
+ int ret;
/*
* We need to serialize the release() with queueing new requests.
@@ -450,14 +463,12 @@ static int v4l2_release(struct inode *inode, struct file *filp)
* operation, and that should not be mixed with queueing a new
* request at the same time.
*/
- if (vdev->fops->release) {
- if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
- mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
- ret = vdev->fops->release(filp);
- mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
- } else {
- ret = vdev->fops->release(filp);
- }
+ if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
+ mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
+ ret = vdev->fops->release(filp);
+ mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
+ } else {
+ ret = vdev->fops->release(filp);
}
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
@@ -483,7 +494,6 @@ static const struct file_operations v4l2_fops = {
#endif
.release = v4l2_release,
.poll = v4l2_poll,
- .llseek = no_llseek,
};
/**
@@ -511,7 +521,7 @@ static int get_index(struct video_device *vdev)
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
if (video_devices[i] != NULL &&
video_devices[i]->v4l2_dev == vdev->v4l2_dev) {
- set_bit(video_devices[i]->index, used);
+ __set_bit(video_devices[i]->index, used);
}
}
@@ -519,7 +529,7 @@ static int get_index(struct video_device *vdev)
}
#define SET_VALID_IOCTL(ops, cmd, op) \
- do { if ((ops)->op) set_bit(_IOC_NR(cmd), valid_ioctls); } while (0)
+ do { if ((ops)->op) __set_bit(_IOC_NR(cmd), valid_ioctls); } while (0)
/* This determines which ioctls are actually implemented in the driver.
It's a one-time thing which simplifies video_ioctl2 as it can just do
@@ -556,79 +566,81 @@ static void determine_valid_ioctls(struct video_device *vdev)
bool is_rx = vdev->vfl_dir != VFL_DIR_TX;
bool is_tx = vdev->vfl_dir != VFL_DIR_RX;
bool is_io_mc = vdev->device_caps & V4L2_CAP_IO_MC;
+ bool has_streaming = vdev->device_caps & V4L2_CAP_STREAMING;
+ bool is_edid = vdev->device_caps & V4L2_CAP_EDID;
bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE);
/* vfl_type and vfl_dir independent ioctls */
SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
- set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
/* Note: the control handler can also be passed through the filehandle,
and that can't be tested here. If the bit for these control ioctls
is set, then the ioctl is valid. But if it is 0, then it can still
be valid if the filehandle passed the control handler. */
- if (vdev->ctrl_handler || ops->vidioc_queryctrl)
- set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl)
- set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl)
+ __set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
+ __set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
+ __set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_querymenu)
- set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
if (!is_tch) {
SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
}
SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status);
#ifdef CONFIG_VIDEO_ADV_DEBUG
- set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls);
#endif
/* yes, really vidioc_subscribe_event */
SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
- set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
if (is_vid) {
/* video specific ioctls */
if ((is_rx && (ops->vidioc_enum_fmt_vid_cap ||
ops->vidioc_enum_fmt_vid_overlay)) ||
(is_tx && ops->vidioc_enum_fmt_vid_out))
- set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
ops->vidioc_g_fmt_vid_cap_mplane ||
ops->vidioc_g_fmt_vid_overlay)) ||
(is_tx && (ops->vidioc_g_fmt_vid_out ||
ops->vidioc_g_fmt_vid_out_mplane ||
ops->vidioc_g_fmt_vid_out_overlay)))
- set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
ops->vidioc_s_fmt_vid_cap_mplane ||
ops->vidioc_s_fmt_vid_overlay)) ||
(is_tx && (ops->vidioc_s_fmt_vid_out ||
ops->vidioc_s_fmt_vid_out_mplane ||
ops->vidioc_s_fmt_vid_out_overlay)))
- set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
ops->vidioc_try_fmt_vid_cap_mplane ||
ops->vidioc_try_fmt_vid_overlay)) ||
(is_tx && (ops->vidioc_try_fmt_vid_out ||
ops->vidioc_try_fmt_vid_out_mplane ||
ops->vidioc_try_fmt_vid_out_overlay)))
- set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
@@ -641,12 +653,14 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
- if (ops->vidioc_g_selection) {
- set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
+ if (ops->vidioc_g_selection &&
+ !test_bit(_IOC_NR(VIDIOC_G_SELECTION), vdev->valid_ioctls)) {
+ __set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
}
- if (ops->vidioc_s_selection)
- set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
+ if (ops->vidioc_s_selection &&
+ !test_bit(_IOC_NR(VIDIOC_S_SELECTION), vdev->valid_ioctls))
+ __set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
}
@@ -669,17 +683,17 @@ static void determine_valid_ioctls(struct video_device *vdev)
ops->vidioc_g_fmt_sliced_vbi_cap)) ||
(is_tx && (ops->vidioc_g_fmt_vbi_out ||
ops->vidioc_g_fmt_sliced_vbi_out)))
- set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_s_fmt_vbi_cap ||
ops->vidioc_s_fmt_sliced_vbi_cap)) ||
(is_tx && (ops->vidioc_s_fmt_vbi_out ||
ops->vidioc_s_fmt_sliced_vbi_out)))
- set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_try_fmt_vbi_cap ||
ops->vidioc_try_fmt_sliced_vbi_cap)) ||
(is_tx && (ops->vidioc_try_fmt_vbi_out ||
ops->vidioc_try_fmt_sliced_vbi_out)))
- set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
} else if (is_tch) {
/* touch specific ioctls */
@@ -708,8 +722,8 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_sdr_out);
}
- if (is_vid || is_vbi || is_sdr || is_tch || is_meta) {
- /* ioctls valid for video, vbi, sdr, touch and metadata */
+ if (has_streaming) {
+ /* ioctls valid for streaming I/O */
SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
@@ -719,20 +733,23 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
+ /* VIDIOC_CREATE_BUFS support is mandatory to enable VIDIOC_REMOVE_BUFS */
+ if (ops->vidioc_create_bufs)
+ SET_VALID_IOCTL(ops, VIDIOC_REMOVE_BUFS, vidioc_remove_bufs);
}
if (is_vid || is_vbi || is_meta) {
/* ioctls valid for video, vbi and metadata */
if (ops->vidioc_s_std)
- set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
SET_VALID_IOCTL(ops, VIDIOC_G_STD, vidioc_g_std);
if (is_rx) {
SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd);
if (is_io_mc) {
- set_bit(_IOC_NR(VIDIOC_ENUMINPUT), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_G_INPUT), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_S_INPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_ENUMINPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_INPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_INPUT), valid_ioctls);
} else {
SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
@@ -746,9 +763,9 @@ static void determine_valid_ioctls(struct video_device *vdev)
}
if (is_tx) {
if (is_io_mc) {
- set_bit(_IOC_NR(VIDIOC_ENUMOUTPUT), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_G_OUTPUT), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_S_OUTPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_ENUMOUTPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_OUTPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_OUTPUT), valid_ioctls);
} else {
SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
@@ -759,7 +776,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
}
if (ops->vidioc_g_parm || ops->vidioc_g_std)
- set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
@@ -778,6 +795,20 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner);
SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek);
}
+ if (is_edid) {
+ SET_VALID_IOCTL(ops, VIDIOC_G_EDID, vidioc_g_edid);
+ if (is_tx) {
+ SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
+ SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
+ }
+ if (is_rx) {
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
+ SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_EDID, vidioc_s_edid);
+ }
+ }
bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
BASE_VIDIOC_PRIVATE);
@@ -900,6 +931,9 @@ int __video_register_device(struct video_device *vdev,
/* the device_caps field MUST be set for all but subdevs */
if (WARN_ON(type != VFL_TYPE_SUBDEV && !vdev->device_caps))
return -EINVAL;
+ /* the open and release file operations are mandatory */
+ if (WARN_ON(!vdev->fops || !vdev->fops->open || !vdev->fops->release))
+ return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
@@ -1032,28 +1066,31 @@ int __video_register_device(struct video_device *vdev,
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
+ vdev->dev.release = v4l2_device_release;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+
+ /* Increase v4l2_device refcount */
+ v4l2_device_get(vdev->v4l2_dev);
+
+ mutex_lock(&videodev_lock);
ret = device_register(&vdev->dev);
if (ret < 0) {
+ mutex_unlock(&videodev_lock);
pr_err("%s: device_register failed\n", __func__);
- goto cleanup;
+ put_device(&vdev->dev);
+ return ret;
}
- /* Register the release callback that will be called when the last
- reference to the device goes away. */
- vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
pr_warn("%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
- /* Increase v4l2_device refcount */
- v4l2_device_get(vdev->v4l2_dev);
-
/* Part 5: Register the entity. */
ret = video_register_media_controller(vdev);
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
+ mutex_unlock(&videodev_lock);
return 0;
@@ -1089,12 +1126,93 @@ void video_unregister_device(struct video_device *vdev)
*/
clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_unlock(&videodev_lock);
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))
- v4l2_event_wake_all(vdev);
+ v4l2_event_wake_all(vdev);
device_unregister(&vdev->dev);
}
EXPORT_SYMBOL(video_unregister_device);
+#ifdef CONFIG_DEBUG_FS
+struct dentry *v4l2_debugfs_root(void)
+{
+ if (!v4l2_debugfs_root_dir)
+ v4l2_debugfs_root_dir = debugfs_create_dir("v4l2", NULL);
+ return v4l2_debugfs_root_dir;
+}
+EXPORT_SYMBOL_GPL(v4l2_debugfs_root);
+#endif
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+
+__must_check int video_device_pipeline_start(struct video_device *vdev,
+ struct media_pipeline *pipe)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return media_pipeline_start(&entity->pads[0], pipe);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline_start);
+
+__must_check int __video_device_pipeline_start(struct video_device *vdev,
+ struct media_pipeline *pipe)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return __media_pipeline_start(&entity->pads[0], pipe);
+}
+EXPORT_SYMBOL_GPL(__video_device_pipeline_start);
+
+void video_device_pipeline_stop(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (WARN_ON(entity->num_pads != 1))
+ return;
+
+ return media_pipeline_stop(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline_stop);
+
+void __video_device_pipeline_stop(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (WARN_ON(entity->num_pads != 1))
+ return;
+
+ return __media_pipeline_stop(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(__video_device_pipeline_stop);
+
+__must_check int video_device_pipeline_alloc_start(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return media_pipeline_alloc_start(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline_alloc_start);
+
+struct media_pipeline *video_device_pipeline(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (WARN_ON(entity->num_pads != 1))
+ return NULL;
+
+ return media_pad_pipeline(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline);
+
+#endif /* CONFIG_MEDIA_CONTROLLER */
+
/*
* Initialise video for linux
*/
@@ -1127,6 +1245,8 @@ static void __exit videodev_exit(void)
class_unregister(&video_class);
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+ debugfs_remove_recursive(v4l2_debugfs_root_dir);
+ v4l2_debugfs_root_dir = NULL;
}
subsys_initcall(videodev_init);
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index d2e58ae91f9b..63b12ef9d4d9 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -2,7 +2,7 @@
/*
V4L2 device support.
- Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@kernel.org>
*/
@@ -108,8 +108,8 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister);
-int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
- struct v4l2_subdev *sd)
+int __v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd, struct module *module)
{
int err;
@@ -125,9 +125,9 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
* try_module_get() such sub-device owners.
*/
sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver &&
- sd->owner == v4l2_dev->dev->driver->owner;
+ module == v4l2_dev->dev->driver->owner;
- if (!sd->owner_v4l2_dev && !try_module_get(sd->owner))
+ if (!sd->owner_v4l2_dev && !try_module_get(module))
return -ENODEV;
sd->v4l2_dev = v4l2_dev;
@@ -152,6 +152,8 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
goto error_unregister;
}
+ sd->owner = module;
+
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
spin_unlock(&v4l2_dev->lock);
@@ -168,7 +170,7 @@ error_module:
sd->v4l2_dev = NULL;
return err;
}
-EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+EXPORT_SYMBOL_GPL(__v4l2_device_register_subdev);
static void v4l2_subdev_release(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 af48705c704f..346d1b0e10ce 100644
--- a/drivers/media/v4l2-core/v4l2-dv-timings.c
+++ b/drivers/media/v4l2-core/v4l2-dv-timings.c
@@ -145,6 +145,8 @@ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t,
const struct v4l2_bt_timings *bt = &t->bt;
const struct v4l2_bt_timings_cap *cap = &dvcap->bt;
u32 caps = cap->capabilities;
+ const u32 max_vert = 10240;
+ u32 max_hor = 3 * bt->width;
if (t->type != V4L2_DV_BT_656_1120)
return false;
@@ -161,6 +163,26 @@ 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;
+
+ /* sanity checks for the blanking timings */
+ if (!bt->interlaced &&
+ (bt->il_vbackporch || bt->il_vsync || bt->il_vfrontporch))
+ return false;
+ /*
+ * Some video receivers cannot properly separate the frontporch,
+ * backporch and sync values, and instead they only have the total
+ * blanking. That can be assigned to any of these three fields.
+ * So just check that none of these are way out of range.
+ */
+ if (bt->hfrontporch > max_hor ||
+ bt->hsync > max_hor || bt->hbackporch > max_hor)
+ return false;
+ if (bt->vfrontporch > max_vert ||
+ bt->vsync > max_vert || bt->vbackporch > max_vert)
+ return false;
+ if (bt->interlaced && (bt->il_vfrontporch > max_vert ||
+ bt->il_vsync > max_vert || bt->il_vbackporch > max_vert))
+ return false;
return fnc == NULL || fnc(t, fnc_handle);
}
EXPORT_SYMBOL_GPL(v4l2_valid_dv_timings);
@@ -459,25 +481,28 @@ EXPORT_SYMBOL_GPL(v4l2_calc_timeperframe);
* @polarities - the horizontal and vertical polarities (same as struct
* v4l2_bt_timings polarities).
* @interlaced - if this flag is true, it indicates interlaced format
- * @fmt - the resulting timings.
+ * @cap - the v4l2_dv_timings_cap capabilities.
+ * @timings - 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,
- unsigned active_width,
+bool v4l2_detect_cvt(unsigned int frame_height,
+ unsigned int hfreq,
+ unsigned int vsync,
+ unsigned int active_width,
u32 polarities,
bool interlaced,
- struct v4l2_dv_timings *fmt)
+ const struct v4l2_dv_timings_cap *cap,
+ struct v4l2_dv_timings *timings)
{
- int v_fp, v_bp, h_fp, h_bp, hsync;
- int frame_width, image_height, image_width;
+ struct v4l2_dv_timings t = {};
+ int v_fp, v_bp, h_fp, h_bp, hsync;
+ int frame_width, image_height, image_width;
bool reduced_blanking;
bool rb_v2 = false;
- unsigned pix_clk;
+ unsigned int pix_clk;
if (vsync < 4 || vsync > 8)
return false;
@@ -603,36 +628,39 @@ bool v4l2_detect_cvt(unsigned frame_height,
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;
- 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;
+ t.type = V4L2_DV_BT_656_1120;
+ t.bt.polarities = polarities;
+ t.bt.width = image_width;
+ t.bt.height = image_height;
+ t.bt.hfrontporch = h_fp;
+ t.bt.vfrontporch = v_fp;
+ t.bt.hsync = hsync;
+ t.bt.vsync = vsync;
+ t.bt.hbackporch = frame_width - image_width - h_fp - hsync;
if (!interlaced) {
- fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync;
- fmt->bt.interlaced = V4L2_DV_PROGRESSIVE;
+ t.bt.vbackporch = frame_height - image_height - v_fp - vsync;
+ t.bt.interlaced = V4L2_DV_PROGRESSIVE;
} else {
- fmt->bt.vbackporch = (frame_height - image_height - 2 * v_fp -
+ t.bt.vbackporch = (frame_height - image_height - 2 * v_fp -
2 * vsync) / 2;
- fmt->bt.il_vbackporch = frame_height - image_height - 2 * v_fp -
- 2 * vsync - fmt->bt.vbackporch;
- fmt->bt.il_vfrontporch = v_fp;
- fmt->bt.il_vsync = vsync;
- fmt->bt.flags |= V4L2_DV_FL_HALF_LINE;
- fmt->bt.interlaced = V4L2_DV_INTERLACED;
+ t.bt.il_vbackporch = frame_height - image_height - 2 * v_fp -
+ 2 * vsync - t.bt.vbackporch;
+ t.bt.il_vfrontporch = v_fp;
+ t.bt.il_vsync = vsync;
+ t.bt.flags |= V4L2_DV_FL_HALF_LINE;
+ t.bt.interlaced = V4L2_DV_INTERLACED;
}
- fmt->bt.pixelclock = pix_clk;
- fmt->bt.standards = V4L2_DV_BT_STD_CVT;
+ t.bt.pixelclock = pix_clk;
+ t.bt.standards = V4L2_DV_BT_STD_CVT;
if (reduced_blanking)
- fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING;
+ t.bt.flags |= V4L2_DV_FL_REDUCED_BLANKING;
+ if (!v4l2_valid_dv_timings(&t, cap, NULL, NULL))
+ return false;
+ *timings = t;
return true;
}
EXPORT_SYMBOL_GPL(v4l2_detect_cvt);
@@ -677,22 +705,25 @@ EXPORT_SYMBOL_GPL(v4l2_detect_cvt);
* 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.
+ * @cap - the v4l2_dv_timings_cap capabilities.
+ * @timings - 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,
- bool interlaced,
- struct v4l2_fract aspect,
- struct v4l2_dv_timings *fmt)
+bool v4l2_detect_gtf(unsigned int frame_height,
+ unsigned int hfreq,
+ unsigned int vsync,
+ u32 polarities,
+ bool interlaced,
+ struct v4l2_fract aspect,
+ const struct v4l2_dv_timings_cap *cap,
+ struct v4l2_dv_timings *timings)
{
+ struct v4l2_dv_timings t = {};
int pix_clk;
- int v_fp, v_bp, h_fp, hsync;
+ int v_fp, v_bp, h_fp, hsync;
int frame_width, image_height, image_width;
bool default_gtf;
int h_blank;
@@ -733,7 +764,7 @@ bool v4l2_detect_gtf(unsigned frame_height,
u64 num;
u32 den;
- num = ((image_width * GTF_D_C_PRIME * (u64)hfreq) -
+ num = (((u64)image_width * GTF_D_C_PRIME * hfreq) -
((u64)image_width * GTF_D_M_PRIME * 1000));
den = (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) *
(2 * GTF_CELL_GRAN);
@@ -743,7 +774,7 @@ bool v4l2_detect_gtf(unsigned frame_height,
u64 num;
u32 den;
- num = ((image_width * GTF_S_C_PRIME * (u64)hfreq) -
+ num = (((u64)image_width * GTF_S_C_PRIME * hfreq) -
((u64)image_width * GTF_S_M_PRIME * 1000));
den = (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) *
(2 * GTF_CELL_GRAN);
@@ -761,36 +792,39 @@ 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;
- 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;
+ t.type = V4L2_DV_BT_656_1120;
+ t.bt.polarities = polarities;
+ t.bt.width = image_width;
+ t.bt.height = image_height;
+ t.bt.hfrontporch = h_fp;
+ t.bt.vfrontporch = v_fp;
+ t.bt.hsync = hsync;
+ t.bt.vsync = vsync;
+ t.bt.hbackporch = frame_width - image_width - h_fp - hsync;
if (!interlaced) {
- fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync;
- fmt->bt.interlaced = V4L2_DV_PROGRESSIVE;
+ t.bt.vbackporch = frame_height - image_height - v_fp - vsync;
+ t.bt.interlaced = V4L2_DV_PROGRESSIVE;
} else {
- fmt->bt.vbackporch = (frame_height - image_height - 2 * v_fp -
+ t.bt.vbackporch = (frame_height - image_height - 2 * v_fp -
2 * vsync) / 2;
- fmt->bt.il_vbackporch = frame_height - image_height - 2 * v_fp -
- 2 * vsync - fmt->bt.vbackporch;
- fmt->bt.il_vfrontporch = v_fp;
- fmt->bt.il_vsync = vsync;
- fmt->bt.flags |= V4L2_DV_FL_HALF_LINE;
- fmt->bt.interlaced = V4L2_DV_INTERLACED;
+ t.bt.il_vbackporch = frame_height - image_height - 2 * v_fp -
+ 2 * vsync - t.bt.vbackporch;
+ t.bt.il_vfrontporch = v_fp;
+ t.bt.il_vsync = vsync;
+ t.bt.flags |= V4L2_DV_FL_HALF_LINE;
+ t.bt.interlaced = V4L2_DV_INTERLACED;
}
- fmt->bt.pixelclock = pix_clk;
- fmt->bt.standards = V4L2_DV_BT_STD_GTF;
+ t.bt.pixelclock = pix_clk;
+ t.bt.standards = V4L2_DV_BT_STD_GTF;
if (!default_gtf)
- fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING;
+ t.bt.flags |= V4L2_DV_FL_REDUCED_BLANKING;
+ if (!v4l2_valid_dv_timings(&t, cap, NULL, NULL))
+ return false;
+ *timings = t;
return true;
}
EXPORT_SYMBOL_GPL(v4l2_detect_gtf);
@@ -984,6 +1018,42 @@ v4l2_hdmi_rx_colorimetry(const struct hdmi_avi_infoframe *avi,
EXPORT_SYMBOL_GPL(v4l2_hdmi_rx_colorimetry);
/**
+ * v4l2_num_edid_blocks() - return the number of EDID blocks
+ *
+ * @edid: pointer to the EDID data
+ * @max_blocks: maximum number of supported EDID blocks
+ *
+ * Return: the number of EDID blocks based on the contents of the EDID.
+ * This supports the HDMI Forum EDID Extension Override Data Block.
+ */
+unsigned int v4l2_num_edid_blocks(const u8 *edid, unsigned int max_blocks)
+{
+ unsigned int blocks;
+
+ if (!edid || !max_blocks)
+ return 0;
+
+ // The number of extension blocks is recorded at byte 126 of the
+ // first 128-byte block in the EDID.
+ //
+ // If there is an HDMI Forum EDID Extension Override Data Block
+ // present, then it is in bytes 4-6 of the first CTA-861 extension
+ // block of the EDID.
+ blocks = edid[126] + 1;
+ // Check for HDMI Forum EDID Extension Override Data Block
+ if (blocks >= 2 && // The EDID must be at least 2 blocks
+ max_blocks >= 3 && // The caller supports at least 3 blocks
+ edid[128] == 2 && // The first extension block is type CTA-861
+ edid[133] == 0x78 && // Identifier for the EEODB
+ (edid[132] & 0xe0) == 0xe0 && // Tag Code == 7
+ (edid[132] & 0x1f) >= 2 && // Length >= 2
+ edid[134] > 1) // Number of extension blocks is sane
+ blocks = edid[134] + 1;
+ return blocks > max_blocks ? max_blocks : blocks;
+}
+EXPORT_SYMBOL_GPL(v4l2_num_edid_blocks);
+
+/**
* v4l2_get_edid_phys_addr() - find and return the physical address
*
* @edid: pointer to the EDID data
@@ -1132,3 +1202,74 @@ int v4l2_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_phys_addr_validate);
+
+#ifdef CONFIG_DEBUG_FS
+
+#define DEBUGFS_FOPS(type, flag) \
+static ssize_t \
+infoframe_read_##type(struct file *filp, \
+ char __user *ubuf, size_t count, loff_t *ppos) \
+{ \
+ struct v4l2_debugfs_if *infoframes = filp->private_data; \
+ \
+ return infoframes->if_read((flag), infoframes->priv, filp, \
+ ubuf, count, ppos); \
+} \
+ \
+static const struct file_operations infoframe_##type##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = simple_open, \
+ .read = infoframe_read_##type, \
+}
+
+DEBUGFS_FOPS(avi, V4L2_DEBUGFS_IF_AVI);
+DEBUGFS_FOPS(audio, V4L2_DEBUGFS_IF_AUDIO);
+DEBUGFS_FOPS(spd, V4L2_DEBUGFS_IF_SPD);
+DEBUGFS_FOPS(hdmi, V4L2_DEBUGFS_IF_HDMI);
+DEBUGFS_FOPS(drm, V4L2_DEBUGFS_IF_DRM);
+
+struct v4l2_debugfs_if *v4l2_debugfs_if_alloc(struct dentry *root, u32 if_types,
+ void *priv,
+ v4l2_debugfs_if_read_t if_read)
+{
+ struct v4l2_debugfs_if *infoframes;
+
+ if (IS_ERR_OR_NULL(root) || !if_types || !if_read)
+ return NULL;
+
+ infoframes = kzalloc(sizeof(*infoframes), GFP_KERNEL);
+ if (!infoframes)
+ return NULL;
+
+ infoframes->if_dir = debugfs_create_dir("infoframes", root);
+ infoframes->priv = priv;
+ infoframes->if_read = if_read;
+ if (if_types & V4L2_DEBUGFS_IF_AVI)
+ debugfs_create_file("avi", 0400, infoframes->if_dir,
+ infoframes, &infoframe_avi_fops);
+ if (if_types & V4L2_DEBUGFS_IF_AUDIO)
+ debugfs_create_file("audio", 0400, infoframes->if_dir,
+ infoframes, &infoframe_audio_fops);
+ if (if_types & V4L2_DEBUGFS_IF_SPD)
+ debugfs_create_file("spd", 0400, infoframes->if_dir,
+ infoframes, &infoframe_spd_fops);
+ if (if_types & V4L2_DEBUGFS_IF_HDMI)
+ debugfs_create_file("hdmi", 0400, infoframes->if_dir,
+ infoframes, &infoframe_hdmi_fops);
+ if (if_types & V4L2_DEBUGFS_IF_DRM)
+ debugfs_create_file("hdr_drm", 0400, infoframes->if_dir,
+ infoframes, &infoframe_drm_fops);
+ return infoframes;
+}
+EXPORT_SYMBOL_GPL(v4l2_debugfs_if_alloc);
+
+void v4l2_debugfs_if_free(struct v4l2_debugfs_if *infoframes)
+{
+ if (infoframes) {
+ debugfs_remove_recursive(infoframes->if_dir);
+ kfree(infoframes);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_debugfs_if_free);
+
+#endif
diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c
index c5ce9f11ad7b..3898ff7edddb 100644
--- a/drivers/media/v4l2-core/v4l2-event.c
+++ b/drivers/media/v4l2-core/v4l2-event.c
@@ -238,6 +238,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
sev = kvzalloc(struct_size(sev, events, elems), GFP_KERNEL);
if (!sev)
return -ENOMEM;
+ sev->elems = elems;
for (i = 0; i < elems; i++)
sev->events[i].sev = sev;
sev->type = sub->type;
@@ -245,7 +246,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
sev->flags = sub->flags;
sev->fh = fh;
sev->ops = ops;
- sev->elems = elems;
mutex_lock(&fh->subscribe_lock);
diff --git a/drivers/media/v4l2-core/v4l2-fh.c b/drivers/media/v4l2-core/v4l2-fh.c
index 90eec79ee995..df3ba9d4674b 100644
--- a/drivers/media/v4l2-core/v4l2-fh.c
+++ b/drivers/media/v4l2-core/v4l2-fh.c
@@ -41,10 +41,12 @@ void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
}
EXPORT_SYMBOL_GPL(v4l2_fh_init);
-void v4l2_fh_add(struct v4l2_fh *fh)
+void v4l2_fh_add(struct v4l2_fh *fh, struct file *filp)
{
unsigned long flags;
+ filp->private_data = fh;
+
v4l2_prio_open(fh->vdev->prio, &fh->prio);
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
list_add(&fh->list, &fh->vdev->fh_list);
@@ -57,16 +59,15 @@ int v4l2_fh_open(struct file *filp)
struct video_device *vdev = video_devdata(filp);
struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- filp->private_data = fh;
if (fh == NULL)
return -ENOMEM;
v4l2_fh_init(fh, vdev);
- v4l2_fh_add(fh);
+ v4l2_fh_add(fh, filp);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_fh_open);
-void v4l2_fh_del(struct v4l2_fh *fh)
+void v4l2_fh_del(struct v4l2_fh *fh, struct file *filp)
{
unsigned long flags;
@@ -74,6 +75,8 @@ void v4l2_fh_del(struct v4l2_fh *fh)
list_del_init(&fh->list);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
v4l2_prio_close(fh->vdev->prio, fh->prio);
+
+ filp->private_data = NULL;
}
EXPORT_SYMBOL_GPL(v4l2_fh_del);
@@ -90,13 +93,12 @@ EXPORT_SYMBOL_GPL(v4l2_fh_exit);
int v4l2_fh_release(struct file *filp)
{
- struct v4l2_fh *fh = filp->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(filp);
if (fh) {
- v4l2_fh_del(fh);
+ v4l2_fh_del(fh, filp);
v4l2_fh_exit(fh);
kfree(fh);
- filp->private_data = NULL;
}
return 0;
}
diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
index e70e128ccc9c..355595a0fefa 100644
--- a/drivers/media/v4l2-core/v4l2-flash-led-class.c
+++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
@@ -94,7 +94,7 @@ static int v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
* brightness <-> intensity conversion, it also must have defined
* related v4l2 control step == 1. In such a case a backward conversion
* from led brightness to v4l2 intensity is required to find out the
- * the aligned intensity value.
+ * aligned intensity value.
*/
if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
ctrl->val = call_flash_op(v4l2_flash,
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 00457e1e93f6..cb153ce42c45 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -28,6 +28,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "v4l2-subdev-priv.h"
+
static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type;
enum v4l2_mbus_type mbus_type;
@@ -61,6 +63,10 @@ static const struct v4l2_fwnode_bus_conv {
V4L2_FWNODE_BUS_TYPE_BT656,
V4L2_MBUS_BT656,
"Bt.656",
+ }, {
+ V4L2_FWNODE_BUS_TYPE_DPI,
+ V4L2_MBUS_DPI,
+ "DPI",
}
};
@@ -119,11 +125,11 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
struct v4l2_fwnode_endpoint *vep,
enum v4l2_mbus_type bus_type)
{
- struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2;
+ struct v4l2_mbus_config_mipi_csi2 *bus = &vep->bus.mipi_csi2;
bool have_clk_lane = false, have_data_lanes = false,
- have_lane_polarities = false;
+ have_lane_polarities = false, have_line_orders = false;
unsigned int flags = 0, lanes_used = 0;
- u32 array[1 + V4L2_FWNODE_CSI2_MAX_DATA_LANES];
+ u32 array[1 + V4L2_MBUS_CSI2_MAX_DATA_LANES];
u32 clock_lane = 0;
unsigned int num_data_lanes = 0;
bool use_default_lane_mapping = false;
@@ -136,7 +142,7 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
use_default_lane_mapping = true;
num_data_lanes = min_t(u32, bus->num_data_lanes,
- V4L2_FWNODE_CSI2_MAX_DATA_LANES);
+ V4L2_MBUS_CSI2_MAX_DATA_LANES);
clock_lane = bus->clock_lane;
if (clock_lane)
@@ -155,7 +161,7 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
rval = fwnode_property_count_u32(fwnode, "data-lanes");
if (rval > 0) {
num_data_lanes =
- min_t(int, V4L2_FWNODE_CSI2_MAX_DATA_LANES, rval);
+ min_t(int, V4L2_MBUS_CSI2_MAX_DATA_LANES, rval);
fwnode_property_read_u32_array(fwnode, "data-lanes", array,
num_data_lanes);
@@ -191,6 +197,17 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
have_lane_polarities = true;
}
+ rval = fwnode_property_count_u32(fwnode, "line-orders");
+ if (rval > 0) {
+ if (rval != num_data_lanes) {
+ pr_warn("invalid number of line-orders entries (need %u, got %u)\n",
+ num_data_lanes, rval);
+ return -EINVAL;
+ }
+
+ have_line_orders = true;
+ }
+
if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) {
clock_lane = v;
pr_debug("clock lane position %u\n", v);
@@ -207,13 +224,11 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
if (fwnode_property_present(fwnode, "clock-noncontinuous")) {
flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
pr_debug("non-continuous clock\n");
- } else {
- flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
}
if (bus_type == V4L2_MBUS_CSI2_DPHY ||
- bus_type == V4L2_MBUS_CSI2_CPHY || lanes_used ||
- have_clk_lane || (flags & ~V4L2_MBUS_CSI2_CONTINUOUS_CLOCK)) {
+ bus_type == V4L2_MBUS_CSI2_CPHY ||
+ lanes_used || have_clk_lane || flags) {
/* Only D-PHY has a clock lane. */
unsigned int dfl_data_lane_index =
bus_type == V4L2_MBUS_CSI2_DPHY;
@@ -246,6 +261,36 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode,
} else {
pr_debug("no lane polarities defined, assuming not inverted\n");
}
+
+ if (have_line_orders) {
+ fwnode_property_read_u32_array(fwnode,
+ "line-orders", array,
+ num_data_lanes);
+
+ for (i = 0; i < num_data_lanes; i++) {
+ static const char * const orders[] = {
+ "ABC", "ACB", "BAC", "BCA", "CAB", "CBA"
+ };
+
+ if (array[i] >= ARRAY_SIZE(orders)) {
+ pr_warn("lane %u invalid line-order assuming ABC (got %u)\n",
+ i, array[i]);
+ bus->line_orders[i] =
+ V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC;
+ continue;
+ }
+
+ bus->line_orders[i] = array[i];
+ pr_debug("lane %u line order %s", i,
+ orders[array[i]]);
+ }
+ } else {
+ for (i = 0; i < num_data_lanes; i++)
+ bus->line_orders[i] =
+ V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC;
+
+ pr_debug("no line orders defined, assuming ABC\n");
+ }
}
return 0;
@@ -263,7 +308,7 @@ v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode,
struct v4l2_fwnode_endpoint *vep,
enum v4l2_mbus_type bus_type)
{
- struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
+ struct v4l2_mbus_config_parallel *bus = &vep->bus.parallel;
unsigned int flags = 0;
u32 v;
@@ -296,10 +341,25 @@ v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode,
if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) {
flags &= ~(V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_PCLK_SAMPLE_FALLING);
- flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING :
- V4L2_MBUS_PCLK_SAMPLE_FALLING;
- pr_debug("pclk-sample %s\n", v ? "high" : "low");
+ V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_PCLK_SAMPLE_DUALEDGE);
+ switch (v) {
+ case 0:
+ flags |= V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ pr_debug("pclk-sample low\n");
+ break;
+ case 1:
+ flags |= V4L2_MBUS_PCLK_SAMPLE_RISING;
+ pr_debug("pclk-sample high\n");
+ break;
+ case 2:
+ flags |= V4L2_MBUS_PCLK_SAMPLE_DUALEDGE;
+ pr_debug("pclk-sample dual edge\n");
+ break;
+ default:
+ pr_warn("invalid argument for pclk-sample");
+ break;
+ }
}
if (!fwnode_property_read_u32(fwnode, "data-active", &v)) {
@@ -369,7 +429,7 @@ v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode,
struct v4l2_fwnode_endpoint *vep,
enum v4l2_mbus_type bus_type)
{
- struct v4l2_fwnode_bus_mipi_csi1 *bus = &vep->bus.mipi_csi1;
+ struct v4l2_mbus_config_mipi_csi1 *bus = &vep->bus.mipi_csi1;
u32 v;
if (!fwnode_property_read_u32(fwnode, "clock-inv", &v)) {
@@ -549,19 +609,29 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode,
link->local_id = fwep.id;
link->local_port = fwep.port;
link->local_node = fwnode_graph_get_port_parent(fwnode);
+ if (!link->local_node)
+ return -ENOLINK;
fwnode = fwnode_graph_get_remote_endpoint(fwnode);
- if (!fwnode) {
- fwnode_handle_put(fwnode);
- return -ENOLINK;
- }
+ if (!fwnode)
+ goto err_put_local_node;
fwnode_graph_parse_endpoint(fwnode, &fwep);
link->remote_id = fwep.id;
link->remote_port = fwep.port;
link->remote_node = fwnode_graph_get_port_parent(fwnode);
+ if (!link->remote_node)
+ goto err_put_remote_endpoint;
return 0;
+
+err_put_remote_endpoint:
+ fwnode_handle_put(fwnode);
+
+err_put_local_node:
+ fwnode_handle_put(link->local_node);
+
+ return -ENOLINK;
}
EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link);
@@ -779,103 +849,6 @@ int v4l2_fwnode_device_parse(struct device *dev,
}
EXPORT_SYMBOL_GPL(v4l2_fwnode_device_parse);
-static int
-v4l2_async_nf_fwnode_parse_endpoint(struct device *dev,
- struct v4l2_async_notifier *notifier,
- struct fwnode_handle *endpoint,
- unsigned int asd_struct_size,
- parse_endpoint_func parse_endpoint)
-{
- struct v4l2_fwnode_endpoint vep = { .bus_type = 0 };
- struct v4l2_async_subdev *asd;
- int ret;
-
- asd = kzalloc(asd_struct_size, GFP_KERNEL);
- if (!asd)
- return -ENOMEM;
-
- asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
- asd->match.fwnode =
- fwnode_graph_get_remote_port_parent(endpoint);
- if (!asd->match.fwnode) {
- dev_dbg(dev, "no remote endpoint found\n");
- ret = -ENOTCONN;
- goto out_err;
- }
-
- ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &vep);
- if (ret) {
- dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n",
- ret);
- goto out_err;
- }
-
- ret = parse_endpoint ? parse_endpoint(dev, &vep, asd) : 0;
- if (ret == -ENOTCONN)
- dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep.base.port,
- vep.base.id);
- else if (ret < 0)
- dev_warn(dev,
- "driver could not parse port@%u/endpoint@%u (%d)\n",
- vep.base.port, vep.base.id, ret);
- v4l2_fwnode_endpoint_free(&vep);
- if (ret < 0)
- goto out_err;
-
- ret = __v4l2_async_nf_add_subdev(notifier, asd);
- if (ret < 0) {
- /* not an error if asd already exists */
- if (ret == -EEXIST)
- ret = 0;
- goto out_err;
- }
-
- return 0;
-
-out_err:
- fwnode_handle_put(asd->match.fwnode);
- kfree(asd);
-
- return ret == -ENOTCONN ? 0 : ret;
-}
-
-int
-v4l2_async_nf_parse_fwnode_endpoints(struct device *dev,
- struct v4l2_async_notifier *notifier,
- size_t asd_struct_size,
- parse_endpoint_func parse_endpoint)
-{
- struct fwnode_handle *fwnode;
- int ret = 0;
-
- if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
- return -EINVAL;
-
- fwnode_graph_for_each_endpoint(dev_fwnode(dev), fwnode) {
- struct fwnode_handle *dev_fwnode;
- bool is_available;
-
- dev_fwnode = fwnode_graph_get_port_parent(fwnode);
- is_available = fwnode_device_is_available(dev_fwnode);
- fwnode_handle_put(dev_fwnode);
- if (!is_available)
- continue;
-
-
- ret = v4l2_async_nf_fwnode_parse_endpoint(dev, notifier,
- fwnode,
- asd_struct_size,
- parse_endpoint);
- if (ret < 0)
- break;
- }
-
- fwnode_handle_put(fwnode);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_async_nf_parse_fwnode_endpoints);
-
/*
* v4l2_fwnode_reference_parse - parse references for async sub-devices
* @dev: the device node the properties of which are parsed for references
@@ -896,30 +869,13 @@ static int v4l2_fwnode_reference_parse(struct device *dev,
int ret;
for (index = 0;
- !(ret = fwnode_property_get_reference_args(dev_fwnode(dev),
- prop, NULL, 0,
- index, &args));
- index++)
- fwnode_handle_put(args.fwnode);
-
- if (!index)
- return -ENOENT;
-
- /*
- * Note that right now both -ENODATA and -ENOENT may signal
- * out-of-bounds access. Return the error in cases other than that.
- */
- if (ret != -ENOENT && ret != -ENODATA)
- return ret;
-
- for (index = 0;
- !fwnode_property_get_reference_args(dev_fwnode(dev), prop, NULL,
- 0, index, &args);
+ !(ret = fwnode_property_get_reference_args(dev_fwnode(dev), prop,
+ NULL, 0, index, &args));
index++) {
- struct v4l2_async_subdev *asd;
+ struct v4l2_async_connection *asd;
asd = v4l2_async_nf_add_fwnode(notifier, args.fwnode,
- struct v4l2_async_subdev);
+ struct v4l2_async_connection);
fwnode_handle_put(args.fwnode);
if (IS_ERR(asd)) {
/* not an error if asd already exists */
@@ -930,7 +886,12 @@ static int v4l2_fwnode_reference_parse(struct device *dev,
}
}
- return 0;
+ /* -ENOENT here means successful parsing */
+ if (ret != -ENOENT)
+ return ret;
+
+ /* Return -ENOENT if no references were found */
+ return index ? 0 : -ENOENT;
}
/*
@@ -1216,10 +1177,10 @@ v4l2_fwnode_reference_parse_int_props(struct device *dev,
props,
nprops)));
index++) {
- struct v4l2_async_subdev *asd;
+ struct v4l2_async_connection *asd;
asd = v4l2_async_nf_add_fwnode(notifier, fwnode,
- struct v4l2_async_subdev);
+ struct v4l2_async_connection);
fwnode_handle_put(fwnode);
if (IS_ERR(asd)) {
ret = PTR_ERR(asd);
@@ -1259,7 +1220,9 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev,
static const char * const led_props[] = { "led" };
static const struct v4l2_fwnode_int_props props[] = {
{ "flash-leds", led_props, ARRAY_SIZE(led_props) },
- { "lens-focus", NULL, 0 },
+ { "mipi-img-flash-leds", },
+ { "lens-focus", },
+ { "mipi-img-lens-focus", },
};
unsigned int i;
@@ -1295,13 +1258,17 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
if (!notifier)
return -ENOMEM;
- v4l2_async_nf_init(notifier);
+ v4l2_async_subdev_nf_init(notifier, sd);
+
+ ret = v4l2_subdev_get_privacy_led(sd);
+ if (ret < 0)
+ goto out_cleanup;
ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
if (ret < 0)
goto out_cleanup;
- ret = v4l2_async_subdev_nf_register(sd, notifier);
+ ret = v4l2_async_nf_register(notifier);
if (ret < 0)
goto out_cleanup;
@@ -1317,6 +1284,7 @@ out_unregister:
v4l2_async_nf_unregister(notifier);
out_cleanup:
+ v4l2_subdev_put_privacy_led(sd);
v4l2_async_nf_cleanup(notifier);
kfree(notifier);
@@ -1324,6 +1292,7 @@ out_cleanup:
}
EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor);
+MODULE_DESCRIPTION("V4L2 fwnode binding parsing library");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
diff --git a/drivers/media/v4l2-core/v4l2-h264.c b/drivers/media/v4l2-core/v4l2-h264.c
index 5633a242520a..c00197d095e7 100644
--- a/drivers/media/v4l2-core/v4l2-h264.c
+++ b/drivers/media/v4l2-core/v4l2-h264.c
@@ -12,6 +12,12 @@
#include <media/v4l2-h264.h>
+/*
+ * Size of the tempory buffer allocated when printing reference lists. The
+ * output will be truncated if the size is too small.
+ */
+static const int tmp_str_size = 1024;
+
/**
* v4l2_h264_init_reflist_builder() - Initialize a P/B0/B1 reference list
* builder
@@ -34,62 +40,96 @@ v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b,
cur_frame_num = dec_params->frame_num;
memset(b, 0, sizeof(*b));
- if (!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC))
+ if (!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) {
b->cur_pic_order_count = min(dec_params->bottom_field_order_cnt,
dec_params->top_field_order_cnt);
- else if (dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)
+ b->cur_pic_fields = V4L2_H264_FRAME_REF;
+ } else if (dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) {
b->cur_pic_order_count = dec_params->bottom_field_order_cnt;
- else
+ b->cur_pic_fields = V4L2_H264_BOTTOM_FIELD_REF;
+ } else {
b->cur_pic_order_count = dec_params->top_field_order_cnt;
+ b->cur_pic_fields = V4L2_H264_TOP_FIELD_REF;
+ }
for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
- u32 pic_order_count;
-
if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
continue;
- b->refs[i].pic_num = dpb[i].pic_num;
if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
b->refs[i].longterm = true;
/*
* Handle frame_num wraparound as described in section
* '8.2.4.1 Decoding process for picture numbers' of the spec.
- * TODO: This logic will have to be adjusted when we start
- * supporting interlaced content.
+ * For long term references, frame_num is set to
+ * long_term_frame_idx which requires no wrapping.
*/
- if (dpb[i].frame_num > cur_frame_num)
+ if (!b->refs[i].longterm && dpb[i].frame_num > cur_frame_num)
b->refs[i].frame_num = (int)dpb[i].frame_num -
max_frame_num;
else
b->refs[i].frame_num = dpb[i].frame_num;
- if (dpb[i].fields == V4L2_H264_FRAME_REF)
- pic_order_count = min(dpb[i].top_field_order_cnt,
- dpb[i].bottom_field_order_cnt);
- else if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)
- pic_order_count = dpb[i].bottom_field_order_cnt;
- else
- pic_order_count = dpb[i].top_field_order_cnt;
+ b->refs[i].top_field_order_cnt = dpb[i].top_field_order_cnt;
+ b->refs[i].bottom_field_order_cnt = dpb[i].bottom_field_order_cnt;
+
+ if (b->cur_pic_fields == V4L2_H264_FRAME_REF) {
+ u8 fields = V4L2_H264_FRAME_REF;
+
+ b->unordered_reflist[b->num_valid].index = i;
+ b->unordered_reflist[b->num_valid].fields = fields;
+ b->num_valid++;
+ continue;
+ }
+
+ if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) {
+ u8 fields = V4L2_H264_TOP_FIELD_REF;
+
+ b->unordered_reflist[b->num_valid].index = i;
+ b->unordered_reflist[b->num_valid].fields = fields;
+ b->num_valid++;
+ }
+
+ if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) {
+ u8 fields = V4L2_H264_BOTTOM_FIELD_REF;
- b->refs[i].pic_order_count = pic_order_count;
- b->unordered_reflist[b->num_valid] = i;
- b->num_valid++;
+ b->unordered_reflist[b->num_valid].index = i;
+ b->unordered_reflist[b->num_valid].fields = fields;
+ b->num_valid++;
+ }
}
for (i = b->num_valid; i < ARRAY_SIZE(b->unordered_reflist); i++)
- b->unordered_reflist[i] = i;
+ b->unordered_reflist[i].index = i;
}
EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder);
+static s32 v4l2_h264_get_poc(const struct v4l2_h264_reflist_builder *b,
+ const struct v4l2_h264_reference *ref)
+{
+ switch (ref->fields) {
+ case V4L2_H264_FRAME_REF:
+ return min(b->refs[ref->index].top_field_order_cnt,
+ b->refs[ref->index].bottom_field_order_cnt);
+ case V4L2_H264_TOP_FIELD_REF:
+ return b->refs[ref->index].top_field_order_cnt;
+ case V4L2_H264_BOTTOM_FIELD_REF:
+ return b->refs[ref->index].bottom_field_order_cnt;
+ }
+
+ /* not reached */
+ return 0;
+}
+
static int v4l2_h264_p_ref_list_cmp(const void *ptra, const void *ptrb,
const void *data)
{
const struct v4l2_h264_reflist_builder *builder = data;
u8 idxa, idxb;
- idxa = *((u8 *)ptra);
- idxb = *((u8 *)ptrb);
+ idxa = ((struct v4l2_h264_reference *)ptra)->index;
+ idxb = ((struct v4l2_h264_reference *)ptrb)->index;
if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||
idxb >= V4L2_H264_NUM_DPB_ENTRIES))
@@ -104,15 +144,19 @@ static int v4l2_h264_p_ref_list_cmp(const void *ptra, const void *ptrb,
}
/*
- * Short term pics in descending pic num order, long term ones in
- * ascending order.
+ * For frames, short term pics are in descending pic num order and long
+ * term ones in ascending order. For fields, the same direction is used
+ * but with frame_num (wrapped). For frames, the value of pic_num and
+ * frame_num are the same (see formula (8-28) and (8-29)). For this
+ * reason we can use frame_num only and share this function between
+ * frames and fields reflist.
*/
if (!builder->refs[idxa].longterm)
return builder->refs[idxb].frame_num <
builder->refs[idxa].frame_num ?
-1 : 1;
- return builder->refs[idxa].pic_num < builder->refs[idxb].pic_num ?
+ return builder->refs[idxa].frame_num < builder->refs[idxb].frame_num ?
-1 : 1;
}
@@ -123,8 +167,8 @@ static int v4l2_h264_b0_ref_list_cmp(const void *ptra, const void *ptrb,
s32 poca, pocb;
u8 idxa, idxb;
- idxa = *((u8 *)ptra);
- idxb = *((u8 *)ptrb);
+ idxa = ((struct v4l2_h264_reference *)ptra)->index;
+ idxb = ((struct v4l2_h264_reference *)ptrb)->index;
if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||
idxb >= V4L2_H264_NUM_DPB_ENTRIES))
@@ -138,14 +182,14 @@ static int v4l2_h264_b0_ref_list_cmp(const void *ptra, const void *ptrb,
return 1;
}
- /* Long term pics in ascending pic num order. */
+ /* Long term pics in ascending frame num order. */
if (builder->refs[idxa].longterm)
- return builder->refs[idxa].pic_num <
- builder->refs[idxb].pic_num ?
+ return builder->refs[idxa].frame_num <
+ builder->refs[idxb].frame_num ?
-1 : 1;
- poca = builder->refs[idxa].pic_order_count;
- pocb = builder->refs[idxb].pic_order_count;
+ poca = v4l2_h264_get_poc(builder, ptra);
+ pocb = v4l2_h264_get_poc(builder, ptrb);
/*
* Short term pics with POC < cur POC first in POC descending order
@@ -168,8 +212,8 @@ static int v4l2_h264_b1_ref_list_cmp(const void *ptra, const void *ptrb,
s32 poca, pocb;
u8 idxa, idxb;
- idxa = *((u8 *)ptra);
- idxb = *((u8 *)ptrb);
+ idxa = ((struct v4l2_h264_reference *)ptra)->index;
+ idxb = ((struct v4l2_h264_reference *)ptrb)->index;
if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||
idxb >= V4L2_H264_NUM_DPB_ENTRIES))
@@ -183,14 +227,14 @@ static int v4l2_h264_b1_ref_list_cmp(const void *ptra, const void *ptrb,
return 1;
}
- /* Long term pics in ascending pic num order. */
+ /* Long term pics in ascending frame num order. */
if (builder->refs[idxa].longterm)
- return builder->refs[idxa].pic_num <
- builder->refs[idxb].pic_num ?
+ return builder->refs[idxa].frame_num <
+ builder->refs[idxb].frame_num ?
-1 : 1;
- poca = builder->refs[idxa].pic_order_count;
- pocb = builder->refs[idxb].pic_order_count;
+ poca = v4l2_h264_get_poc(builder, ptra);
+ pocb = v4l2_h264_get_poc(builder, ptrb);
/*
* Short term pics with POC > cur POC first in POC ascending order
@@ -206,12 +250,139 @@ static int v4l2_h264_b1_ref_list_cmp(const void *ptra, const void *ptrb,
return poca < pocb ? -1 : 1;
}
+/*
+ * The references need to be reordered so that references are alternating
+ * between top and bottom field references starting with the current picture
+ * parity. This has to be done for short term and long term references
+ * separately.
+ */
+static void reorder_field_reflist(const struct v4l2_h264_reflist_builder *b,
+ struct v4l2_h264_reference *reflist)
+{
+ struct v4l2_h264_reference tmplist[V4L2_H264_REF_LIST_LEN];
+ u8 lt, i = 0, j = 0, k = 0;
+
+ memcpy(tmplist, reflist, sizeof(tmplist[0]) * b->num_valid);
+
+ for (lt = 0; lt <= 1; lt++) {
+ do {
+ for (; i < b->num_valid && b->refs[tmplist[i].index].longterm == lt; i++) {
+ if (tmplist[i].fields == b->cur_pic_fields) {
+ reflist[k++] = tmplist[i++];
+ break;
+ }
+ }
+
+ for (; j < b->num_valid && b->refs[tmplist[j].index].longterm == lt; j++) {
+ if (tmplist[j].fields != b->cur_pic_fields) {
+ reflist[k++] = tmplist[j++];
+ break;
+ }
+ }
+ } while ((i < b->num_valid && b->refs[tmplist[i].index].longterm == lt) ||
+ (j < b->num_valid && b->refs[tmplist[j].index].longterm == lt));
+ }
+}
+
+static char ref_type_to_char(u8 ref_type)
+{
+ switch (ref_type) {
+ case V4L2_H264_FRAME_REF:
+ return 'f';
+ case V4L2_H264_TOP_FIELD_REF:
+ return 't';
+ case V4L2_H264_BOTTOM_FIELD_REF:
+ return 'b';
+ }
+
+ return '?';
+}
+
+static const char *format_ref_list_p(const struct v4l2_h264_reflist_builder *builder,
+ struct v4l2_h264_reference *reflist,
+ char **out_str)
+{
+ int n = 0, i;
+
+ *out_str = kmalloc(tmp_str_size, GFP_KERNEL);
+ if (!(*out_str))
+ return NULL;
+
+ n += snprintf(*out_str + n, tmp_str_size - n, "|");
+
+ for (i = 0; i < builder->num_valid; i++) {
+ /* this is pic_num for frame and frame_num (wrapped) for field,
+ * but for frame pic_num is equal to frame_num (wrapped).
+ */
+ int frame_num = builder->refs[reflist[i].index].frame_num;
+ bool longterm = builder->refs[reflist[i].index].longterm;
+
+ n += scnprintf(*out_str + n, tmp_str_size - n, "%i%c%c|",
+ frame_num, longterm ? 'l' : 's',
+ ref_type_to_char(reflist[i].fields));
+ }
+
+ return *out_str;
+}
+
+static void print_ref_list_p(const struct v4l2_h264_reflist_builder *builder,
+ struct v4l2_h264_reference *reflist)
+{
+ char *buf = NULL;
+
+ pr_debug("ref_pic_list_p (cur_poc %u%c) %s\n",
+ builder->cur_pic_order_count,
+ ref_type_to_char(builder->cur_pic_fields),
+ format_ref_list_p(builder, reflist, &buf));
+
+ kfree(buf);
+}
+
+static const char *format_ref_list_b(const struct v4l2_h264_reflist_builder *builder,
+ struct v4l2_h264_reference *reflist,
+ char **out_str)
+{
+ int n = 0, i;
+
+ *out_str = kmalloc(tmp_str_size, GFP_KERNEL);
+ if (!(*out_str))
+ return NULL;
+
+ n += snprintf(*out_str + n, tmp_str_size - n, "|");
+
+ for (i = 0; i < builder->num_valid; i++) {
+ int frame_num = builder->refs[reflist[i].index].frame_num;
+ u32 poc = v4l2_h264_get_poc(builder, reflist + i);
+ bool longterm = builder->refs[reflist[i].index].longterm;
+
+ n += scnprintf(*out_str + n, tmp_str_size - n, "%i%c%c|",
+ longterm ? frame_num : poc,
+ longterm ? 'l' : 's',
+ ref_type_to_char(reflist[i].fields));
+ }
+
+ return *out_str;
+}
+
+static void print_ref_list_b(const struct v4l2_h264_reflist_builder *builder,
+ struct v4l2_h264_reference *reflist, u8 list_num)
+{
+ char *buf = NULL;
+
+ pr_debug("ref_pic_list_b%u (cur_poc %u%c) %s",
+ list_num, builder->cur_pic_order_count,
+ ref_type_to_char(builder->cur_pic_fields),
+ format_ref_list_b(builder, reflist, &buf));
+
+ kfree(buf);
+}
+
/**
* v4l2_h264_build_p_ref_list() - Build the P reference list
*
* @builder: reference list builder context
- * @reflist: 16-bytes array used to store the P reference list. Each entry
- * is an index in the DPB
+ * @reflist: 32 sized array used to store the P reference list. Each entry
+ * is a v4l2_h264_reference structure
*
* This functions builds the P reference lists. This procedure is describe in
* section '8.2.4 Decoding process for reference picture lists construction'
@@ -220,12 +391,17 @@ static int v4l2_h264_b1_ref_list_cmp(const void *ptra, const void *ptrb,
*/
void
v4l2_h264_build_p_ref_list(const struct v4l2_h264_reflist_builder *builder,
- u8 *reflist)
+ struct v4l2_h264_reference *reflist)
{
memcpy(reflist, builder->unordered_reflist,
sizeof(builder->unordered_reflist[0]) * builder->num_valid);
sort_r(reflist, builder->num_valid, sizeof(*reflist),
v4l2_h264_p_ref_list_cmp, NULL, builder);
+
+ if (builder->cur_pic_fields != V4L2_H264_FRAME_REF)
+ reorder_field_reflist(builder, reflist);
+
+ print_ref_list_p(builder, reflist);
}
EXPORT_SYMBOL_GPL(v4l2_h264_build_p_ref_list);
@@ -233,10 +409,10 @@ EXPORT_SYMBOL_GPL(v4l2_h264_build_p_ref_list);
* v4l2_h264_build_b_ref_lists() - Build the B0/B1 reference lists
*
* @builder: reference list builder context
- * @b0_reflist: 16-bytes array used to store the B0 reference list. Each entry
- * is an index in the DPB
- * @b1_reflist: 16-bytes array used to store the B1 reference list. Each entry
- * is an index in the DPB
+ * @b0_reflist: 32 sized array used to store the B0 reference list. Each entry
+ * is a v4l2_h264_reference structure
+ * @b1_reflist: 32 sized array used to store the B1 reference list. Each entry
+ * is a v4l2_h264_reference structure
*
* This functions builds the B0/B1 reference lists. This procedure is described
* in section '8.2.4 Decoding process for reference picture lists construction'
@@ -245,7 +421,8 @@ EXPORT_SYMBOL_GPL(v4l2_h264_build_p_ref_list);
*/
void
v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder *builder,
- u8 *b0_reflist, u8 *b1_reflist)
+ struct v4l2_h264_reference *b0_reflist,
+ struct v4l2_h264_reference *b1_reflist)
{
memcpy(b0_reflist, builder->unordered_reflist,
sizeof(builder->unordered_reflist[0]) * builder->num_valid);
@@ -257,9 +434,17 @@ v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder *builder,
sort_r(b1_reflist, builder->num_valid, sizeof(*b1_reflist),
v4l2_h264_b1_ref_list_cmp, NULL, builder);
+ if (builder->cur_pic_fields != V4L2_H264_FRAME_REF) {
+ reorder_field_reflist(builder, b0_reflist);
+ reorder_field_reflist(builder, b1_reflist);
+ }
+
if (builder->num_valid > 1 &&
!memcmp(b1_reflist, b0_reflist, builder->num_valid))
swap(b1_reflist[0], b1_reflist[1]);
+
+ print_ref_list_b(builder, b0_reflist, 0);
+ print_ref_list_b(builder, b1_reflist, 1);
}
EXPORT_SYMBOL_GPL(v4l2_h264_build_b_ref_lists);
diff --git a/drivers/media/v4l2-core/v4l2-i2c.c b/drivers/media/v4l2-core/v4l2-i2c.c
index b4acca75644b..ffc64e10fcae 100644
--- a/drivers/media/v4l2-core/v4l2-i2c.c
+++ b/drivers/media/v4l2-core/v4l2-i2c.c
@@ -5,6 +5,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
@@ -24,7 +25,7 @@ void v4l2_i2c_subdev_unregister(struct v4l2_subdev *sd)
* registered by us, and would not be
* re-created by just probing the V4L2 driver.
*/
- if (client && !client->dev.of_node && !client->dev.fwnode)
+ if (client && !dev_fwnode(&client->dev))
i2c_unregister_device(client);
}
@@ -100,7 +101,7 @@ struct v4l2_subdev
* Register with the v4l2_device which increases the module's
* use count as well.
*/
- if (v4l2_device_register_subdev(v4l2_dev, sd))
+ if (__v4l2_device_register_subdev(v4l2_dev, sd, sd->owner))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
module_put(client->dev.driver->owner);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 9ac557b8e146..98512ea4cc5b 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -16,8 +16,10 @@
#include <linux/kernel.h>
#include <linux/version.h>
+#include <linux/v4l2-subdev.h>
#include <linux/videodev2.h>
+#include <media/media-device.h> /* for media_set_bus_info() */
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
@@ -30,12 +32,6 @@
#include <trace/events/v4l2.h>
-/* Zero out the end of the struct pointed to by p. Everything after, but
- * not including, the specified field is cleared. */
-#define CLEAR_AFTER_FIELD(p, field) \
- memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
- 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
-
#define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls)
struct std_descr {
@@ -279,8 +275,8 @@ static void v4l_print_format(const void *arg, bool write_only)
const struct v4l2_vbi_format *vbi;
const struct v4l2_sliced_vbi_format *sliced;
const struct v4l2_window *win;
- const struct v4l2_sdr_format *sdr;
const struct v4l2_meta_format *meta;
+ u32 pixelformat;
u32 planes;
unsigned i;
@@ -299,8 +295,9 @@ static void v4l_print_format(const void *arg, bool write_only)
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
mp = &p->fmt.pix_mp;
+ pixelformat = mp->pixelformat;
pr_cont(", width=%u, height=%u, format=%p4cc, field=%s, colorspace=%d, num_planes=%u, flags=0x%x, ycbcr_enc=%u, quantization=%u, xfer_func=%u\n",
- mp->width, mp->height, &mp->pixelformat,
+ mp->width, mp->height, &pixelformat,
prt_names(mp->field, v4l2_field_names),
mp->colorspace, mp->num_planes, mp->flags,
mp->ycbcr_enc, mp->quantization, mp->xfer_func);
@@ -313,14 +310,10 @@ static void v4l_print_format(const void *arg, bool write_only)
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
win = &p->fmt.win;
- /* Note: we can't print the clip list here since the clips
- * pointer is a userspace pointer, not a kernelspace
- * pointer. */
- pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, chromakey=0x%08x, clipcount=%u, clips=%p, bitmap=%p, global_alpha=0x%02x\n",
- win->w.width, win->w.height, win->w.left, win->w.top,
+ pr_cont(", (%d,%d)/%ux%u, field=%s, chromakey=0x%08x, global_alpha=0x%02x\n",
+ win->w.left, win->w.top, win->w.width, win->w.height,
prt_names(win->field, v4l2_field_names),
- win->chromakey, win->clipcount, win->clips,
- win->bitmap, win->global_alpha);
+ win->chromakey, win->global_alpha);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
case V4L2_BUF_TYPE_VBI_OUTPUT:
@@ -343,14 +336,16 @@ static void v4l_print_format(const void *arg, bool write_only)
break;
case V4L2_BUF_TYPE_SDR_CAPTURE:
case V4L2_BUF_TYPE_SDR_OUTPUT:
- sdr = &p->fmt.sdr;
- pr_cont(", pixelformat=%p4cc\n", &sdr->pixelformat);
+ pixelformat = p->fmt.sdr.pixelformat;
+ pr_cont(", pixelformat=%p4cc\n", &pixelformat);
break;
case V4L2_BUF_TYPE_META_CAPTURE:
case V4L2_BUF_TYPE_META_OUTPUT:
meta = &p->fmt.meta;
- pr_cont(", dataformat=%p4cc, buffersize=%u\n",
- &meta->dataformat, meta->buffersize);
+ pixelformat = meta->dataformat;
+ pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n",
+ &pixelformat, meta->buffersize, meta->width,
+ meta->height, meta->bytesperline);
break;
}
}
@@ -489,12 +484,20 @@ static void v4l_print_create_buffers(const void *arg, bool write_only)
{
const struct v4l2_create_buffers *p = arg;
- pr_cont("index=%d, count=%d, memory=%s, capabilities=0x%08x, ",
+ pr_cont("index=%d, count=%d, memory=%s, capabilities=0x%08x, max num buffers=%u, ",
p->index, p->count, prt_names(p->memory, v4l2_memory_names),
- p->capabilities);
+ p->capabilities, p->max_num_buffers);
v4l_print_format(&p->format, write_only);
}
+static void v4l_print_remove_buffers(const void *arg, bool write_only)
+{
+ const struct v4l2_remove_buffers *p = arg;
+
+ pr_cont("type=%s, index=%u, count=%u\n",
+ prt_names(p->type, v4l2_type_names), p->index, p->count);
+}
+
static void v4l_print_streamparm(const void *arg, bool write_only)
{
const struct v4l2_streamparm *p = arg;
@@ -586,12 +589,12 @@ static void v4l_print_cropcap(const void *arg, bool write_only)
{
const struct v4l2_cropcap *p = arg;
- pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, defrect wxh=%dx%d, x,y=%d,%d, pixelaspect %d/%d\n",
+ pr_cont("type=%s, bounds (%d,%d)/%ux%u, defrect (%d,%d)/%ux%u, pixelaspect %d/%d\n",
prt_names(p->type, v4l2_type_names),
- p->bounds.width, p->bounds.height,
p->bounds.left, p->bounds.top,
- p->defrect.width, p->defrect.height,
+ p->bounds.width, p->bounds.height,
p->defrect.left, p->defrect.top,
+ p->defrect.width, p->defrect.height,
p->pixelaspect.numerator, p->pixelaspect.denominator);
}
@@ -599,20 +602,20 @@ static void v4l_print_crop(const void *arg, bool write_only)
{
const struct v4l2_crop *p = arg;
- pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
+ pr_cont("type=%s, crop=(%d,%d)/%ux%u\n",
prt_names(p->type, v4l2_type_names),
- p->c.width, p->c.height,
- p->c.left, p->c.top);
+ p->c.left, p->c.top,
+ p->c.width, p->c.height);
}
static void v4l_print_selection(const void *arg, bool write_only)
{
const struct v4l2_selection *p = arg;
- pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
+ pr_cont("type=%s, target=%d, flags=0x%x, rect=(%d,%d)/%ux%u\n",
prt_names(p->type, v4l2_type_names),
p->target, p->flags,
- p->r.width, p->r.height, p->r.left, p->r.top);
+ p->r.left, p->r.top, p->r.width, p->r.height);
}
static void v4l_print_jpegcompression(const void *arg, bool write_only)
@@ -890,7 +893,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
return false;
break;
case V4L2_CTRL_WHICH_DEF_VAL:
- /* Default value cannot be changed */
+ case V4L2_CTRL_WHICH_MIN_VAL:
+ case V4L2_CTRL_WHICH_MAX_VAL:
+ /* Default, minimum or maximum value cannot be changed */
if (ioctl == VIDIOC_S_EXT_CTRLS ||
ioctl == VIDIOC_TRY_EXT_CTRLS) {
c->error_idx = c->count;
@@ -1004,6 +1009,31 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
return -EINVAL;
}
+static void v4l_sanitize_colorspace(u32 pixelformat, u32 *colorspace,
+ u32 *encoding, u32 *quantization,
+ u32 *xfer_func)
+{
+ bool is_hsv = pixelformat == V4L2_PIX_FMT_HSV24 ||
+ pixelformat == V4L2_PIX_FMT_HSV32;
+
+ if (!v4l2_is_colorspace_valid(*colorspace)) {
+ *colorspace = V4L2_COLORSPACE_DEFAULT;
+ *encoding = V4L2_YCBCR_ENC_DEFAULT;
+ *quantization = V4L2_QUANTIZATION_DEFAULT;
+ *xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ }
+
+ if ((!is_hsv && !v4l2_is_ycbcr_enc_valid(*encoding)) ||
+ (is_hsv && !v4l2_is_hsv_enc_valid(*encoding)))
+ *encoding = V4L2_YCBCR_ENC_DEFAULT;
+
+ if (!v4l2_is_quant_valid(*quantization))
+ *quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ if (!v4l2_is_xfer_func_valid(*xfer_func))
+ *xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
static void v4l_sanitize_format(struct v4l2_format *fmt)
{
unsigned int offset;
@@ -1017,30 +1047,50 @@ static void v4l_sanitize_format(struct v4l2_format *fmt)
/*
* The v4l2_pix_format structure has been extended with fields that were
* not previously required to be set to zero by applications. The priv
- * field, when set to a magic value, indicates the the extended fields
+ * field, when set to a magic value, indicates that the extended fields
* are valid. Otherwise they will contain undefined values. To simplify
* the API towards drivers zero the extended fields and set the priv
* field to the magic value when the extended pixel format structure
* isn't used by applications.
*/
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (fmt->fmt.pix.priv != V4L2_PIX_FMT_PRIV_MAGIC) {
+ fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
+
+ offset = offsetof(struct v4l2_pix_format, priv)
+ + sizeof(fmt->fmt.pix.priv);
+ memset(((void *)&fmt->fmt.pix) + offset, 0,
+ sizeof(fmt->fmt.pix) - offset);
+ }
+ }
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return;
-
- if (fmt->fmt.pix.priv == V4L2_PIX_FMT_PRIV_MAGIC)
- return;
+ /* Replace invalid colorspace values with defaults. */
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l_sanitize_colorspace(fmt->fmt.pix.pixelformat,
+ &fmt->fmt.pix.colorspace,
+ &fmt->fmt.pix.ycbcr_enc,
+ &fmt->fmt.pix.quantization,
+ &fmt->fmt.pix.xfer_func);
+ } else if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ u32 ycbcr_enc = fmt->fmt.pix_mp.ycbcr_enc;
+ u32 quantization = fmt->fmt.pix_mp.quantization;
+ u32 xfer_func = fmt->fmt.pix_mp.xfer_func;
- fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
+ v4l_sanitize_colorspace(fmt->fmt.pix_mp.pixelformat,
+ &fmt->fmt.pix_mp.colorspace, &ycbcr_enc,
+ &quantization, &xfer_func);
- offset = offsetof(struct v4l2_pix_format, priv)
- + sizeof(fmt->fmt.pix.priv);
- memset(((void *)&fmt->fmt.pix) + offset, 0,
- sizeof(fmt->fmt.pix) - offset);
+ fmt->fmt.pix_mp.ycbcr_enc = ycbcr_enc;
+ fmt->fmt.pix_mp.quantization = quantization;
+ fmt->fmt.pix_mp.xfer_func = xfer_func;
+ }
}
-static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_querycap(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_capability *cap = (struct v4l2_capability *)arg;
struct video_device *vfd = video_devdata(file);
@@ -1050,7 +1100,10 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
cap->device_caps = vfd->device_caps;
cap->capabilities = vfd->device_caps | V4L2_CAP_DEVICE_CAPS;
- ret = ops->vidioc_querycap(file, fh, cap);
+ media_set_bus_info(cap->bus_info, sizeof(cap->bus_info),
+ vfd->dev_parent);
+
+ ret = ops->vidioc_querycap(file, NULL, cap);
/*
* Drivers must not change device_caps, so check for this and
@@ -1070,8 +1123,8 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
return ret;
}
-static int v4l_g_input(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_g_input(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
@@ -1080,11 +1133,11 @@ static int v4l_g_input(const struct v4l2_ioctl_ops *ops,
return 0;
}
- return ops->vidioc_g_input(file, fh, arg);
+ return ops->vidioc_g_input(file, NULL, arg);
}
-static int v4l_g_output(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_g_output(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
@@ -1093,11 +1146,11 @@ static int v4l_g_output(const struct v4l2_ioctl_ops *ops,
return 0;
}
- return ops->vidioc_g_output(file, fh, arg);
+ return ops->vidioc_g_output(file, NULL, arg);
}
-static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_input(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
int ret;
@@ -1109,22 +1162,22 @@ static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
if (vfd->device_caps & V4L2_CAP_IO_MC)
return *(int *)arg ? -EINVAL : 0;
- return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
+ return ops->vidioc_s_input(file, NULL, *(unsigned int *)arg);
}
-static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_output(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
if (vfd->device_caps & V4L2_CAP_IO_MC)
return *(int *)arg ? -EINVAL : 0;
- return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
+ return ops->vidioc_s_output(file, NULL, *(unsigned int *)arg);
}
-static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_g_priority(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd;
u32 *p = arg;
@@ -1134,22 +1187,20 @@ static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
return 0;
}
-static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_priority(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd;
struct v4l2_fh *vfh;
u32 *p = arg;
vfd = video_devdata(file);
- if (!test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
- return -ENOTTY;
- vfh = file->private_data;
+ vfh = file_to_v4l2_fh(file);
return v4l2_prio_change(vfd->prio, &vfh->prio, *p);
}
-static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_enuminput(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_input *p = arg;
@@ -1171,11 +1222,11 @@ static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
return 0;
}
- return ops->vidioc_enum_input(file, fh, p);
+ return ops->vidioc_enum_input(file, NULL, p);
}
-static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_output *p = arg;
@@ -1197,7 +1248,7 @@ static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
return 0;
}
- return ops->vidioc_enum_output(file, fh, p);
+ return ops->vidioc_enum_output(file, NULL, p);
}
static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
@@ -1253,18 +1304,30 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_BGRX32: descr = "32-bit XBGR 8-8-8-8"; break;
case V4L2_PIX_FMT_RGBA32: descr = "32-bit RGBA 8-8-8-8"; break;
case V4L2_PIX_FMT_RGBX32: descr = "32-bit RGBX 8-8-8-8"; break;
+ case V4L2_PIX_FMT_RGBX1010102: descr = "32-bit RGBX 10-10-10-2"; break;
+ case V4L2_PIX_FMT_RGBA1010102: descr = "32-bit RGBA 10-10-10-2"; break;
+ case V4L2_PIX_FMT_ARGB2101010: descr = "32-bit ARGB 2-10-10-10"; break;
+ case V4L2_PIX_FMT_BGR48: descr = "48-bit BGR 16-16-16"; break;
+ case V4L2_PIX_FMT_RGB48: descr = "48-bit RGB 16-16-16"; break;
+ case V4L2_PIX_FMT_BGR48_12: descr = "12-bit Depth BGR"; break;
+ case V4L2_PIX_FMT_ABGR64_12: descr = "12-bit Depth BGRA"; break;
case V4L2_PIX_FMT_GREY: descr = "8-bit Greyscale"; break;
case V4L2_PIX_FMT_Y4: descr = "4-bit Greyscale"; break;
case V4L2_PIX_FMT_Y6: descr = "6-bit Greyscale"; break;
case V4L2_PIX_FMT_Y10: descr = "10-bit Greyscale"; break;
case V4L2_PIX_FMT_Y12: descr = "12-bit Greyscale"; break;
+ case V4L2_PIX_FMT_Y012: descr = "12-bit Greyscale (bits 15-4)"; break;
case V4L2_PIX_FMT_Y14: descr = "14-bit Greyscale"; break;
case V4L2_PIX_FMT_Y16: descr = "16-bit Greyscale"; break;
case V4L2_PIX_FMT_Y16_BE: descr = "16-bit Greyscale BE"; break;
case V4L2_PIX_FMT_Y10BPACK: descr = "10-bit Greyscale (Packed)"; break;
case V4L2_PIX_FMT_Y10P: descr = "10-bit Greyscale (MIPI Packed)"; break;
+ case V4L2_PIX_FMT_IPU3_Y10: descr = "10-bit greyscale (IPU3 Packed)"; break;
+ case V4L2_PIX_FMT_Y12P: descr = "12-bit Greyscale (MIPI Packed)"; break;
+ case V4L2_PIX_FMT_Y14P: descr = "14-bit Greyscale (MIPI Packed)"; break;
case V4L2_PIX_FMT_Y8I: descr = "Interleaved 8-bit Greyscale"; break;
case V4L2_PIX_FMT_Y12I: descr = "Interleaved 12-bit Greyscale"; break;
+ case V4L2_PIX_FMT_Y16I: descr = "Interleaved 16-bit Greyscale"; break;
case V4L2_PIX_FMT_Z16: descr = "16-bit Depth"; break;
case V4L2_PIX_FMT_INZI: descr = "Planar 10:16 Greyscale Depth"; break;
case V4L2_PIX_FMT_CNF4: descr = "4-bit Depth Confidence (Packed)"; break;
@@ -1289,25 +1352,35 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_XYUV32: descr = "32-bit XYUV 8-8-8-8"; break;
case V4L2_PIX_FMT_VUYA32: descr = "32-bit VUYA 8-8-8-8"; break;
case V4L2_PIX_FMT_VUYX32: descr = "32-bit VUYX 8-8-8-8"; break;
+ case V4L2_PIX_FMT_YUVA32: descr = "32-bit YUVA 8-8-8-8"; break;
+ case V4L2_PIX_FMT_YUVX32: descr = "32-bit YUVX 8-8-8-8"; break;
case V4L2_PIX_FMT_YUV410: descr = "Planar YUV 4:1:0"; break;
case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break;
case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break;
case V4L2_PIX_FMT_M420: descr = "YUV 4:2:0 (M420)"; break;
- case V4L2_PIX_FMT_NV12: descr = "Y/CbCr 4:2:0"; break;
- case V4L2_PIX_FMT_NV21: descr = "Y/CrCb 4:2:0"; break;
- case V4L2_PIX_FMT_NV16: descr = "Y/CbCr 4:2:2"; break;
- case V4L2_PIX_FMT_NV61: descr = "Y/CrCb 4:2:2"; break;
- case V4L2_PIX_FMT_NV24: descr = "Y/CbCr 4:4:4"; break;
- case V4L2_PIX_FMT_NV42: descr = "Y/CrCb 4:4:4"; break;
- case V4L2_PIX_FMT_NV12_4L4: descr = "Y/CbCr 4:2:0 (4x4 Linear)"; break;
- case V4L2_PIX_FMT_NV12_16L16: descr = "Y/CbCr 4:2:0 (16x16 Linear)"; break;
- case V4L2_PIX_FMT_NV12_32L32: descr = "Y/CbCr 4:2:0 (32x32 Linear)"; break;
- case V4L2_PIX_FMT_NV12M: descr = "Y/CbCr 4:2:0 (N-C)"; break;
- case V4L2_PIX_FMT_NV21M: descr = "Y/CrCb 4:2:0 (N-C)"; break;
- case V4L2_PIX_FMT_NV16M: descr = "Y/CbCr 4:2:2 (N-C)"; break;
- case V4L2_PIX_FMT_NV61M: descr = "Y/CrCb 4:2:2 (N-C)"; break;
- case V4L2_PIX_FMT_NV12MT: descr = "Y/CbCr 4:2:0 (64x32 MB, N-C)"; break;
- case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/CbCr 4:2:0 (16x16 MB, N-C)"; break;
+ case V4L2_PIX_FMT_YUV48_12: descr = "12-bit YUV 4:4:4 Packed"; break;
+ case V4L2_PIX_FMT_NV12: descr = "Y/UV 4:2:0"; break;
+ case V4L2_PIX_FMT_NV21: descr = "Y/VU 4:2:0"; break;
+ case V4L2_PIX_FMT_NV15: descr = "10-bit Y/UV 4:2:0 (Packed)"; break;
+ case V4L2_PIX_FMT_NV16: descr = "Y/UV 4:2:2"; break;
+ case V4L2_PIX_FMT_NV61: descr = "Y/VU 4:2:2"; break;
+ case V4L2_PIX_FMT_NV20: descr = "10-bit Y/UV 4:2:2 (Packed)"; break;
+ case V4L2_PIX_FMT_NV24: descr = "Y/UV 4:4:4"; break;
+ case V4L2_PIX_FMT_NV42: descr = "Y/VU 4:4:4"; break;
+ case V4L2_PIX_FMT_P010: descr = "10-bit Y/UV 4:2:0"; break;
+ case V4L2_PIX_FMT_P012: descr = "12-bit Y/UV 4:2:0"; break;
+ case V4L2_PIX_FMT_NV12_4L4: descr = "Y/UV 4:2:0 (4x4 Linear)"; break;
+ case V4L2_PIX_FMT_NV12_16L16: descr = "Y/UV 4:2:0 (16x16 Linear)"; break;
+ case V4L2_PIX_FMT_NV12_32L32: descr = "Y/UV 4:2:0 (32x32 Linear)"; break;
+ case V4L2_PIX_FMT_NV15_4L4: descr = "10-bit Y/UV 4:2:0 (4x4 Linear)"; break;
+ case V4L2_PIX_FMT_P010_4L4: descr = "10-bit Y/UV 4:2:0 (4x4 Linear)"; break;
+ case V4L2_PIX_FMT_NV12M: descr = "Y/UV 4:2:0 (N-C)"; break;
+ case V4L2_PIX_FMT_NV21M: descr = "Y/VU 4:2:0 (N-C)"; break;
+ case V4L2_PIX_FMT_NV16M: descr = "Y/UV 4:2:2 (N-C)"; break;
+ case V4L2_PIX_FMT_NV61M: descr = "Y/VU 4:2:2 (N-C)"; break;
+ case V4L2_PIX_FMT_NV12MT: descr = "Y/UV 4:2:0 (64x32 MB, N-C)"; break;
+ case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/UV 4:2:0 (16x16 MB, N-C)"; break;
+ case V4L2_PIX_FMT_P012M: descr = "12-bit Y/UV 4:2:0 (N-C)"; break;
case V4L2_PIX_FMT_YUV420M: descr = "Planar YUV 4:2:0 (N-C)"; break;
case V4L2_PIX_FMT_YVU420M: descr = "Planar YVU 4:2:0 (N-C)"; break;
case V4L2_PIX_FMT_YUV422M: descr = "Planar YUV 4:2:2 (N-C)"; break;
@@ -1338,6 +1411,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG10DPCM8: descr = "8-bit Bayer GBGB/RGRG (DPCM)"; break;
case V4L2_PIX_FMT_SGRBG10DPCM8: descr = "8-bit Bayer GRGR/BGBG (DPCM)"; break;
case V4L2_PIX_FMT_SRGGB10DPCM8: descr = "8-bit Bayer RGRG/GBGB (DPCM)"; break;
+ case V4L2_PIX_FMT_RAW_CRU10: descr = "10-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR12: descr = "12-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG12: descr = "12-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG12: descr = "12-bit Bayer GRGR/BGBG"; break;
@@ -1346,6 +1420,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG12P: descr = "12-bit Bayer GBGB/RGRG Packed"; break;
case V4L2_PIX_FMT_SGRBG12P: descr = "12-bit Bayer GRGR/BGBG Packed"; break;
case V4L2_PIX_FMT_SRGGB12P: descr = "12-bit Bayer RGRG/GBGB Packed"; break;
+ case V4L2_PIX_FMT_RAW_CRU12: descr = "12-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR14: descr = "14-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG14: descr = "14-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG14: descr = "14-bit Bayer GRGR/BGBG"; break;
@@ -1354,10 +1429,12 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG14P: descr = "14-bit Bayer GBGB/RGRG Packed"; break;
case V4L2_PIX_FMT_SGRBG14P: descr = "14-bit Bayer GRGR/BGBG Packed"; break;
case V4L2_PIX_FMT_SRGGB14P: descr = "14-bit Bayer RGRG/GBGB Packed"; break;
+ case V4L2_PIX_FMT_RAW_CRU14: descr = "14-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR16: descr = "16-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG16: descr = "16-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG16: descr = "16-bit Bayer GRGR/BGBG"; break;
case V4L2_PIX_FMT_SRGGB16: descr = "16-bit Bayer RGRG/GBGB"; break;
+ case V4L2_PIX_FMT_RAW_CRU20: descr = "14-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SN9C20X_I420: descr = "GSPCA SN9C20X I420"; break;
case V4L2_PIX_FMT_SPCA501: descr = "GSPCA SPCA501"; break;
case V4L2_PIX_FMT_SPCA505: descr = "GSPCA SPCA505"; break;
@@ -1384,10 +1461,33 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_VSP1_HGO: descr = "R-Car VSP1 1-D Histogram"; break;
case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break;
case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break;
+ case V4L2_META_FMT_UVC_MSXU_1_5: descr = "UVC MSXU Metadata"; break;
case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break;
case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break;
case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break;
case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break;
+ case V4L2_META_FMT_RK_ISP1_EXT_PARAMS: descr = "Rockchip ISP1 Ext 3A Params"; break;
+ case V4L2_META_FMT_C3ISP_PARAMS: descr = "Amlogic C3 ISP Parameters"; break;
+ case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break;
+ case V4L2_META_FMT_MALI_C55_PARAMS: descr = "ARM Mali-C55 ISP Parameters"; break;
+ case V4L2_META_FMT_MALI_C55_STATS: descr = "ARM Mali-C55 ISP 3A Statistics"; break;
+ case V4L2_PIX_FMT_NV12_8L128: descr = "NV12 (8x128 Linear)"; break;
+ case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break;
+ case V4L2_PIX_FMT_NV12_10BE_8L128: descr = "10-bit NV12 (8x128 Linear, BE)"; break;
+ case V4L2_PIX_FMT_NV12M_10BE_8L128: descr = "10-bit NV12M (8x128 Linear, BE)"; break;
+ case V4L2_PIX_FMT_Y210: descr = "10-bit YUYV Packed"; break;
+ case V4L2_PIX_FMT_Y212: descr = "12-bit YUYV Packed"; break;
+ case V4L2_PIX_FMT_Y216: descr = "16-bit YUYV Packed"; break;
+ case V4L2_META_FMT_RPI_BE_CFG: descr = "RPi PiSP BE Config format"; break;
+ case V4L2_META_FMT_RPI_FE_CFG: descr = "RPi PiSP FE Config format"; break;
+ case V4L2_META_FMT_RPI_FE_STATS: descr = "RPi PiSP FE Statistics format"; break;
+ case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_14: descr = "8-bit Generic Meta, 14b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_16: descr = "8-bit Generic Meta, 16b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_20: descr = "8-bit Generic Meta, 20b CSI-2"; break;
+ case V4L2_META_FMT_GENERIC_CSI2_24: descr = "8-bit Generic Meta, 24b CSI-2"; break;
default:
/* Compressed formats */
@@ -1418,6 +1518,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_HEVC_SLICE: descr = "HEVC Parsed Slice Data"; break;
case V4L2_PIX_FMT_FWHT: descr = "FWHT"; break; /* used in vicodec */
case V4L2_PIX_FMT_FWHT_STATELESS: descr = "FWHT Stateless"; break; /* used in vicodec */
+ case V4L2_PIX_FMT_SPK: descr = "Sorenson Spark"; break;
+ case V4L2_PIX_FMT_RV30: descr = "RealVideo 8"; break;
+ case V4L2_PIX_FMT_RV40: descr = "RealVideo 9 & 10"; break;
case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break;
case V4L2_PIX_FMT_WNVA: descr = "WNVA"; break;
case V4L2_PIX_FMT_SN9C10X: descr = "GSPCA SN9C10X"; break;
@@ -1437,6 +1540,23 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SE401: descr = "GSPCA SE401"; break;
case V4L2_PIX_FMT_S5C_UYVY_JPG: descr = "S5C73MX interleaved UYVY/JPEG"; break;
case V4L2_PIX_FMT_MT21C: descr = "Mediatek Compressed Format"; break;
+ case V4L2_PIX_FMT_QC08C: descr = "QCOM Compressed 8-bit Format"; break;
+ case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break;
+ case V4L2_PIX_FMT_AJPG: descr = "Aspeed JPEG"; break;
+ case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break;
+ case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break;
+ case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break;
+ case V4L2_PIX_FMT_HEXTILE: descr = "Hextile Compressed Format"; break;
+ case V4L2_PIX_FMT_PISP_COMP1_RGGB: descr = "PiSP 8b RGRG/GBGB mode1 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP1_GRBG: descr = "PiSP 8b GRGR/BGBG mode1 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP1_GBRG: descr = "PiSP 8b GBGB/RGRG mode1 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP1_BGGR: descr = "PiSP 8b BGBG/GRGR mode1 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP1_MONO: descr = "PiSP 8b monochrome mode1 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP2_RGGB: descr = "PiSP 8b RGRG/GBGB mode2 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP2_GRBG: descr = "PiSP 8b GRGR/BGBG mode2 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP2_GBRG: descr = "PiSP 8b GBGB/RGRG mode2 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP 8b BGBG/GRGR mode2 compr"; break;
+ case V4L2_PIX_FMT_PISP_COMP2_MONO: descr = "PiSP 8b monochrome mode2 compr"; break;
default:
if (fmt->description[0])
return;
@@ -1448,13 +1568,29 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
}
}
+ if (fmt->type == V4L2_BUF_TYPE_META_CAPTURE) {
+ switch (fmt->pixelformat) {
+ case V4L2_META_FMT_GENERIC_8:
+ case V4L2_META_FMT_GENERIC_CSI2_10:
+ case V4L2_META_FMT_GENERIC_CSI2_12:
+ case V4L2_META_FMT_GENERIC_CSI2_14:
+ case V4L2_META_FMT_GENERIC_CSI2_16:
+ case V4L2_META_FMT_GENERIC_CSI2_20:
+ case V4L2_META_FMT_GENERIC_CSI2_24:
+ fmt->flags |= V4L2_FMT_FLAG_META_LINE_BASED;
+ break;
+ default:
+ fmt->flags &= ~V4L2_FMT_FLAG_META_LINE_BASED;
+ }
+ }
+
if (descr)
WARN_ON(strscpy(fmt->description, descr, sz) < 0);
fmt->flags |= flags;
}
-static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vdev = video_devdata(file);
struct v4l2_fmtdesc *p = arg;
@@ -1470,7 +1606,7 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
p->mbus_code = 0;
mbus_code = p->mbus_code;
- CLEAR_AFTER_FIELD(p, type);
+ memset_after(p, 0, type);
p->mbus_code = mbus_code;
switch (p->type) {
@@ -1484,12 +1620,12 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
break;
- ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
+ ret = ops->vidioc_enum_fmt_vid_cap(file, NULL, arg);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
break;
- ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
+ ret = ops->vidioc_enum_fmt_vid_overlay(file, NULL, arg);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
@@ -1501,27 +1637,27 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
if (unlikely(!ops->vidioc_enum_fmt_vid_out))
break;
- ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg);
+ ret = ops->vidioc_enum_fmt_vid_out(file, NULL, arg);
break;
case V4L2_BUF_TYPE_SDR_CAPTURE:
if (unlikely(!ops->vidioc_enum_fmt_sdr_cap))
break;
- ret = ops->vidioc_enum_fmt_sdr_cap(file, fh, arg);
+ ret = ops->vidioc_enum_fmt_sdr_cap(file, NULL, arg);
break;
case V4L2_BUF_TYPE_SDR_OUTPUT:
if (unlikely(!ops->vidioc_enum_fmt_sdr_out))
break;
- ret = ops->vidioc_enum_fmt_sdr_out(file, fh, arg);
+ ret = ops->vidioc_enum_fmt_sdr_out(file, NULL, arg);
break;
case V4L2_BUF_TYPE_META_CAPTURE:
if (unlikely(!ops->vidioc_enum_fmt_meta_cap))
break;
- ret = ops->vidioc_enum_fmt_meta_cap(file, fh, arg);
+ ret = ops->vidioc_enum_fmt_meta_cap(file, NULL, arg);
break;
case V4L2_BUF_TYPE_META_OUTPUT:
if (unlikely(!ops->vidioc_enum_fmt_meta_out))
break;
- ret = ops->vidioc_enum_fmt_meta_out(file, fh, arg);
+ ret = ops->vidioc_enum_fmt_meta_out(file, NULL, arg);
break;
}
if (ret == 0)
@@ -1544,8 +1680,8 @@ static void v4l_pix_format_touch(struct v4l2_pix_format *p)
p->xfer_func = 0;
}
-static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_format *p = arg;
struct video_device *vfd = video_devdata(file);
@@ -1554,79 +1690,57 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
if (ret)
return ret;
- /*
- * fmt can't be cleared for these overlay types due to the 'clips'
- * 'clipcount' and 'bitmap' pointers in struct v4l2_window.
- * Those are provided by the user. So handle these two overlay types
- * first, and then just do a simple memset for the other types.
- */
- switch (p->type) {
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: {
- struct v4l2_clip *clips = p->fmt.win.clips;
- u32 clipcount = p->fmt.win.clipcount;
- void __user *bitmap = p->fmt.win.bitmap;
-
- memset(&p->fmt, 0, sizeof(p->fmt));
- p->fmt.win.clips = clips;
- p->fmt.win.clipcount = clipcount;
- p->fmt.win.bitmap = bitmap;
- break;
- }
- default:
- memset(&p->fmt, 0, sizeof(p->fmt));
- break;
- }
+ memset(&p->fmt, 0, sizeof(p->fmt));
switch (p->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (unlikely(!ops->vidioc_g_fmt_vid_cap))
break;
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
- ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
+ ret = ops->vidioc_g_fmt_vid_cap(file, NULL, arg);
/* just in case the driver zeroed it again */
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
if (vfd->vfl_type == VFL_TYPE_TOUCH)
v4l_pix_format_touch(&p->fmt.pix);
return ret;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
+ return ops->vidioc_g_fmt_vid_cap_mplane(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
+ return ops->vidioc_g_fmt_vid_overlay(file, NULL, arg);
case V4L2_BUF_TYPE_VBI_CAPTURE:
- return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
+ return ops->vidioc_g_fmt_vbi_cap(file, NULL, arg);
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
+ return ops->vidioc_g_fmt_sliced_vbi_cap(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
if (unlikely(!ops->vidioc_g_fmt_vid_out))
break;
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
- ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
+ ret = ops->vidioc_g_fmt_vid_out(file, NULL, arg);
/* just in case the driver zeroed it again */
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
return ret;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
+ return ops->vidioc_g_fmt_vid_out_mplane(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
+ return ops->vidioc_g_fmt_vid_out_overlay(file, NULL, arg);
case V4L2_BUF_TYPE_VBI_OUTPUT:
- return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
+ return ops->vidioc_g_fmt_vbi_out(file, NULL, arg);
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
+ return ops->vidioc_g_fmt_sliced_vbi_out(file, NULL, arg);
case V4L2_BUF_TYPE_SDR_CAPTURE:
- return ops->vidioc_g_fmt_sdr_cap(file, fh, arg);
+ return ops->vidioc_g_fmt_sdr_cap(file, NULL, arg);
case V4L2_BUF_TYPE_SDR_OUTPUT:
- return ops->vidioc_g_fmt_sdr_out(file, fh, arg);
+ return ops->vidioc_g_fmt_sdr_out(file, NULL, arg);
case V4L2_BUF_TYPE_META_CAPTURE:
- return ops->vidioc_g_fmt_meta_cap(file, fh, arg);
+ return ops->vidioc_g_fmt_meta_cap(file, NULL, arg);
case V4L2_BUF_TYPE_META_OUTPUT:
- return ops->vidioc_g_fmt_meta_out(file, fh, arg);
+ return ops->vidioc_g_fmt_meta_out(file, NULL, arg);
}
return -EINVAL;
}
-static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_format *p = arg;
struct video_device *vfd = video_devdata(file);
@@ -1645,8 +1759,8 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (unlikely(!ops->vidioc_s_fmt_vid_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
+ memset_after(p, 0, fmt.pix);
+ ret = ops->vidioc_s_fmt_vid_cap(file, NULL, arg);
/* just in case the driver zeroed it again */
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
if (vfd->vfl_type == VFL_TYPE_TOUCH)
@@ -1655,83 +1769,89 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
+ memset_after(p, 0, fmt.pix_mp.xfer_func);
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
- CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
- bytesperline);
- return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
+ memset_after(&p->fmt.pix_mp.plane_fmt[i],
+ 0, bytesperline);
+ return ops->vidioc_s_fmt_vid_cap_mplane(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
+ memset_after(p, 0, fmt.win);
+ p->fmt.win.clips = NULL;
+ p->fmt.win.clipcount = 0;
+ p->fmt.win.bitmap = NULL;
+ return ops->vidioc_s_fmt_vid_overlay(file, NULL, arg);
case V4L2_BUF_TYPE_VBI_CAPTURE:
if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
- return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
+ memset_after(p, 0, fmt.vbi.flags);
+ return ops->vidioc_s_fmt_vbi_cap(file, NULL, arg);
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
- return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
+ memset_after(p, 0, fmt.sliced.io_size);
+ return ops->vidioc_s_fmt_sliced_vbi_cap(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
if (unlikely(!ops->vidioc_s_fmt_vid_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
+ memset_after(p, 0, fmt.pix);
+ ret = ops->vidioc_s_fmt_vid_out(file, NULL, arg);
/* just in case the driver zeroed it again */
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
return ret;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
+ memset_after(p, 0, fmt.pix_mp.xfer_func);
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
- CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
- bytesperline);
- return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
+ memset_after(&p->fmt.pix_mp.plane_fmt[i],
+ 0, bytesperline);
+ return ops->vidioc_s_fmt_vid_out_mplane(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
+ memset_after(p, 0, fmt.win);
+ p->fmt.win.clips = NULL;
+ p->fmt.win.clipcount = 0;
+ p->fmt.win.bitmap = NULL;
+ return ops->vidioc_s_fmt_vid_out_overlay(file, NULL, arg);
case V4L2_BUF_TYPE_VBI_OUTPUT:
if (unlikely(!ops->vidioc_s_fmt_vbi_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
- return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
+ memset_after(p, 0, fmt.vbi.flags);
+ return ops->vidioc_s_fmt_vbi_out(file, NULL, arg);
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
- return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
+ memset_after(p, 0, fmt.sliced.io_size);
+ return ops->vidioc_s_fmt_sliced_vbi_out(file, NULL, arg);
case V4L2_BUF_TYPE_SDR_CAPTURE:
if (unlikely(!ops->vidioc_s_fmt_sdr_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
- return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
+ memset_after(p, 0, fmt.sdr.buffersize);
+ return ops->vidioc_s_fmt_sdr_cap(file, NULL, arg);
case V4L2_BUF_TYPE_SDR_OUTPUT:
if (unlikely(!ops->vidioc_s_fmt_sdr_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
- return ops->vidioc_s_fmt_sdr_out(file, fh, arg);
+ memset_after(p, 0, fmt.sdr.buffersize);
+ return ops->vidioc_s_fmt_sdr_out(file, NULL, arg);
case V4L2_BUF_TYPE_META_CAPTURE:
if (unlikely(!ops->vidioc_s_fmt_meta_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.meta);
- return ops->vidioc_s_fmt_meta_cap(file, fh, arg);
+ memset_after(p, 0, fmt.meta);
+ return ops->vidioc_s_fmt_meta_cap(file, NULL, arg);
case V4L2_BUF_TYPE_META_OUTPUT:
if (unlikely(!ops->vidioc_s_fmt_meta_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.meta);
- return ops->vidioc_s_fmt_meta_out(file, fh, arg);
+ memset_after(p, 0, fmt.meta);
+ return ops->vidioc_s_fmt_meta_out(file, NULL, arg);
}
return -EINVAL;
}
-static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_format *p = arg;
struct video_device *vfd = video_devdata(file);
@@ -1747,8 +1867,8 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (unlikely(!ops->vidioc_try_fmt_vid_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
+ memset_after(p, 0, fmt.pix);
+ ret = ops->vidioc_try_fmt_vid_cap(file, NULL, arg);
/* just in case the driver zeroed it again */
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
if (vfd->vfl_type == VFL_TYPE_TOUCH)
@@ -1757,95 +1877,101 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
+ memset_after(p, 0, fmt.pix_mp.xfer_func);
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
- CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
- bytesperline);
- return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
+ memset_after(&p->fmt.pix_mp.plane_fmt[i],
+ 0, bytesperline);
+ return ops->vidioc_try_fmt_vid_cap_mplane(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
+ memset_after(p, 0, fmt.win);
+ p->fmt.win.clips = NULL;
+ p->fmt.win.clipcount = 0;
+ p->fmt.win.bitmap = NULL;
+ return ops->vidioc_try_fmt_vid_overlay(file, NULL, arg);
case V4L2_BUF_TYPE_VBI_CAPTURE:
if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
- return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
+ memset_after(p, 0, fmt.vbi.flags);
+ return ops->vidioc_try_fmt_vbi_cap(file, NULL, arg);
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
- return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
+ memset_after(p, 0, fmt.sliced.io_size);
+ return ops->vidioc_try_fmt_sliced_vbi_cap(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
if (unlikely(!ops->vidioc_try_fmt_vid_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
+ memset_after(p, 0, fmt.pix);
+ ret = ops->vidioc_try_fmt_vid_out(file, NULL, arg);
/* just in case the driver zeroed it again */
p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
return ret;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
+ memset_after(p, 0, fmt.pix_mp.xfer_func);
for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
- CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
- bytesperline);
- return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
+ memset_after(&p->fmt.pix_mp.plane_fmt[i],
+ 0, bytesperline);
+ return ops->vidioc_try_fmt_vid_out_mplane(file, NULL, arg);
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
+ memset_after(p, 0, fmt.win);
+ p->fmt.win.clips = NULL;
+ p->fmt.win.clipcount = 0;
+ p->fmt.win.bitmap = NULL;
+ return ops->vidioc_try_fmt_vid_out_overlay(file, NULL, arg);
case V4L2_BUF_TYPE_VBI_OUTPUT:
if (unlikely(!ops->vidioc_try_fmt_vbi_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
- return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
+ memset_after(p, 0, fmt.vbi.flags);
+ return ops->vidioc_try_fmt_vbi_out(file, NULL, arg);
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
- return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
+ memset_after(p, 0, fmt.sliced.io_size);
+ return ops->vidioc_try_fmt_sliced_vbi_out(file, NULL, arg);
case V4L2_BUF_TYPE_SDR_CAPTURE:
if (unlikely(!ops->vidioc_try_fmt_sdr_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
- return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
+ memset_after(p, 0, fmt.sdr.buffersize);
+ return ops->vidioc_try_fmt_sdr_cap(file, NULL, arg);
case V4L2_BUF_TYPE_SDR_OUTPUT:
if (unlikely(!ops->vidioc_try_fmt_sdr_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
- return ops->vidioc_try_fmt_sdr_out(file, fh, arg);
+ memset_after(p, 0, fmt.sdr.buffersize);
+ return ops->vidioc_try_fmt_sdr_out(file, NULL, arg);
case V4L2_BUF_TYPE_META_CAPTURE:
if (unlikely(!ops->vidioc_try_fmt_meta_cap))
break;
- CLEAR_AFTER_FIELD(p, fmt.meta);
- return ops->vidioc_try_fmt_meta_cap(file, fh, arg);
+ memset_after(p, 0, fmt.meta);
+ return ops->vidioc_try_fmt_meta_cap(file, NULL, arg);
case V4L2_BUF_TYPE_META_OUTPUT:
if (unlikely(!ops->vidioc_try_fmt_meta_out))
break;
- CLEAR_AFTER_FIELD(p, fmt.meta);
- return ops->vidioc_try_fmt_meta_out(file, fh, arg);
+ memset_after(p, 0, fmt.meta);
+ return ops->vidioc_try_fmt_meta_out(file, NULL, arg);
}
return -EINVAL;
}
-static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_streamon(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
- return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
+ return ops->vidioc_streamon(file, NULL, *(unsigned int *)arg);
}
-static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_streamoff(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
- return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
+ return ops->vidioc_streamoff(file, NULL, *(unsigned int *)arg);
}
-static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_tuner *p = arg;
@@ -1853,14 +1979,14 @@ static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- err = ops->vidioc_g_tuner(file, fh, p);
+ err = ops->vidioc_g_tuner(file, NULL, p);
if (!err)
p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
return err;
}
-static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_tuner *p = arg;
@@ -1871,11 +1997,11 @@ static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
return ret;
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- return ops->vidioc_s_tuner(file, fh, p);
+ return ops->vidioc_s_tuner(file, NULL, p);
}
static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_modulator *p = arg;
@@ -1884,14 +2010,14 @@ static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
if (vfd->vfl_type == VFL_TYPE_RADIO)
p->type = V4L2_TUNER_RADIO;
- err = ops->vidioc_g_modulator(file, fh, p);
+ err = ops->vidioc_g_modulator(file, NULL, p);
if (!err)
p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
return err;
}
static int v4l_s_modulator(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_modulator *p = arg;
@@ -1899,11 +2025,11 @@ static int v4l_s_modulator(const struct v4l2_ioctl_ops *ops,
if (vfd->vfl_type == VFL_TYPE_RADIO)
p->type = V4L2_TUNER_RADIO;
- return ops->vidioc_s_modulator(file, fh, p);
+ return ops->vidioc_s_modulator(file, NULL, p);
}
static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_frequency *p = arg;
@@ -1913,11 +2039,11 @@ static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
else
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- return ops->vidioc_g_frequency(file, fh, p);
+ return ops->vidioc_g_frequency(file, NULL, p);
}
static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
const struct v4l2_frequency *p = arg;
@@ -1936,11 +2062,11 @@ static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
if (type != p->type)
return -EINVAL;
}
- return ops->vidioc_s_frequency(file, fh, p);
+ return ops->vidioc_s_frequency(file, NULL, p);
}
-static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_enumstd(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_standard *p = arg;
@@ -1948,8 +2074,8 @@ static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
return v4l_video_std_enumstd(p, vfd->tvnorms);
}
-static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_std(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
v4l2_std_id id = *(v4l2_std_id *)arg, norm;
@@ -1963,11 +2089,11 @@ static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
return -EINVAL;
/* Calls the specific handler */
- return ops->vidioc_s_std(file, fh, norm);
+ return ops->vidioc_s_std(file, NULL, norm);
}
-static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_querystd(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
v4l2_std_id *p = arg;
@@ -1985,11 +2111,11 @@ static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
* their efforts to improve the standards detection.
*/
*p = vfd->tvnorms;
- return ops->vidioc_querystd(file, fh, arg);
+ return ops->vidioc_querystd(file, NULL, arg);
}
static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_hw_freq_seek *p = arg;
@@ -2007,70 +2133,89 @@ static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
if (p->type != type)
return -EINVAL;
- return ops->vidioc_s_hw_freq_seek(file, fh, p);
+ return ops->vidioc_s_hw_freq_seek(file, NULL, p);
+}
+
+static int v4l_s_fbuf(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
+{
+ struct v4l2_framebuffer *p = arg;
+
+ p->base = NULL;
+ return ops->vidioc_s_fbuf(file, NULL, p);
}
-static int v4l_overlay(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_overlay(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
- return ops->vidioc_overlay(file, fh, *(unsigned int *)arg);
+ return ops->vidioc_overlay(file, NULL, *(unsigned int *)arg);
}
-static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
+ struct video_device *vfd = video_devdata(file);
struct v4l2_requestbuffers *p = arg;
int ret = check_fmt(file, p->type);
if (ret)
return ret;
- CLEAR_AFTER_FIELD(p, flags);
+ memset_after(p, 0, flags);
+
+ p->capabilities = 0;
+ if (is_valid_ioctl(vfd, VIDIOC_REMOVE_BUFS))
+ p->capabilities = V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS;
- return ops->vidioc_reqbufs(file, fh, p);
+ return ops->vidioc_reqbufs(file, NULL, p);
}
-static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_querybuf(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_buffer *p = arg;
int ret = check_fmt(file, p->type);
- return ret ? ret : ops->vidioc_querybuf(file, fh, p);
+ return ret ? ret : ops->vidioc_querybuf(file, NULL, p);
}
-static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_qbuf(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_buffer *p = arg;
int ret = check_fmt(file, p->type);
- return ret ? ret : ops->vidioc_qbuf(file, fh, p);
+ return ret ? ret : ops->vidioc_qbuf(file, NULL, p);
}
-static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_buffer *p = arg;
int ret = check_fmt(file, p->type);
- return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
+ return ret ? ret : ops->vidioc_dqbuf(file, NULL, p);
}
static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
+ struct video_device *vfd = video_devdata(file);
struct v4l2_create_buffers *create = arg;
int ret = check_fmt(file, create->format.type);
if (ret)
return ret;
- CLEAR_AFTER_FIELD(create, flags);
+ memset_after(create, 0, flags);
v4l_sanitize_format(&create->format);
- ret = ops->vidioc_create_bufs(file, fh, create);
+ create->capabilities = 0;
+ if (is_valid_ioctl(vfd, VIDIOC_REMOVE_BUFS))
+ create->capabilities = V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS;
+
+ ret = ops->vidioc_create_bufs(file, NULL, create);
if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
create->format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
@@ -2080,16 +2225,27 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
}
static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct v4l2_buffer *b = arg;
int ret = check_fmt(file, b->type);
- return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
+ return ret ? ret : ops->vidioc_prepare_buf(file, NULL, b);
}
-static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_remove_bufs(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *arg)
+{
+ struct v4l2_remove_buffers *remove = arg;
+
+ if (ops->vidioc_remove_bufs)
+ return ops->vidioc_remove_bufs(file, NULL, remove);
+
+ return -ENOTTY;
+}
+
+static int v4l_g_parm(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_streamparm *p = arg;
@@ -2099,20 +2255,20 @@ static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
if (ret)
return ret;
if (ops->vidioc_g_parm)
- return ops->vidioc_g_parm(file, fh, p);
+ return ops->vidioc_g_parm(file, NULL, p);
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
if (vfd->device_caps & V4L2_CAP_READWRITE)
p->parm.capture.readbuffers = 2;
- ret = ops->vidioc_g_std(file, fh, &std);
+ ret = ops->vidioc_g_std(file, NULL, &std);
if (ret == 0)
v4l2_video_std_frame_period(std, &p->parm.capture.timeperframe);
return ret;
}
-static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_parm(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct v4l2_streamparm *p = arg;
int ret = check_fmt(file, p->type);
@@ -2132,67 +2288,72 @@ static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
p->parm.capture.extendedmode = 0;
p->parm.capture.capturemode &= V4L2_MODE_HIGHQUALITY;
}
- return ops->vidioc_s_parm(file, fh, p);
+ return ops->vidioc_s_parm(file, NULL, p);
}
-static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
+ struct v4l2_query_ext_ctrl qec = {};
struct v4l2_queryctrl *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
+ int ret;
if (vfh && vfh->ctrl_handler)
return v4l2_queryctrl(vfh->ctrl_handler, p);
if (vfd->ctrl_handler)
return v4l2_queryctrl(vfd->ctrl_handler, p);
- if (ops->vidioc_queryctrl)
- return ops->vidioc_queryctrl(file, fh, p);
- return -ENOTTY;
+ if (!ops->vidioc_query_ext_ctrl)
+ return -ENOTTY;
+
+ /* Simulate query_ext_ctr using query_ctrl. */
+ qec.id = p->id;
+ ret = ops->vidioc_query_ext_ctrl(file, NULL, &qec);
+ if (ret)
+ return ret;
+ v4l2_query_ext_ctrl_to_v4l2_queryctrl(p, &qec);
+ return ret;
}
static int v4l_query_ext_ctrl(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_query_ext_ctrl *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
if (vfh && vfh->ctrl_handler)
return v4l2_query_ext_ctrl(vfh->ctrl_handler, p);
if (vfd->ctrl_handler)
return v4l2_query_ext_ctrl(vfd->ctrl_handler, p);
if (ops->vidioc_query_ext_ctrl)
- return ops->vidioc_query_ext_ctrl(file, fh, p);
+ return ops->vidioc_query_ext_ctrl(file, NULL, p);
return -ENOTTY;
}
-static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_querymenu(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_querymenu *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
if (vfh && vfh->ctrl_handler)
return v4l2_querymenu(vfh->ctrl_handler, p);
if (vfd->ctrl_handler)
return v4l2_querymenu(vfd->ctrl_handler, p);
if (ops->vidioc_querymenu)
- return ops->vidioc_querymenu(file, fh, p);
+ return ops->vidioc_querymenu(file, NULL, p);
return -ENOTTY;
}
-static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_control *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
@@ -2200,8 +2361,6 @@ static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
return v4l2_g_ctrl(vfh->ctrl_handler, p);
if (vfd->ctrl_handler)
return v4l2_g_ctrl(vfd->ctrl_handler, p);
- if (ops->vidioc_g_ctrl)
- return ops->vidioc_g_ctrl(file, fh, p);
if (ops->vidioc_g_ext_ctrls == NULL)
return -ENOTTY;
@@ -2211,7 +2370,7 @@ static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
ctrl.id = p->id;
ctrl.value = p->value;
if (check_ext_ctrls(&ctrls, VIDIOC_G_CTRL)) {
- int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
+ int ret = ops->vidioc_g_ext_ctrls(file, NULL, &ctrls);
if (ret == 0)
p->value = ctrl.value;
@@ -2220,13 +2379,12 @@ static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
return -EINVAL;
}
-static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_control *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
int ret;
@@ -2235,8 +2393,6 @@ static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
if (vfd->ctrl_handler)
return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
- if (ops->vidioc_s_ctrl)
- return ops->vidioc_s_ctrl(file, fh, p);
if (ops->vidioc_s_ext_ctrls == NULL)
return -ENOTTY;
@@ -2247,18 +2403,17 @@ static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
ctrl.value = p->value;
if (!check_ext_ctrls(&ctrls, VIDIOC_S_CTRL))
return -EINVAL;
- ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
+ ret = ops->vidioc_s_ext_ctrls(file, NULL, &ctrls);
p->value = ctrl.value;
return ret;
}
static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_ext_controls *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
p->error_idx = p->count;
if (vfh && vfh->ctrl_handler)
@@ -2270,16 +2425,15 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
if (ops->vidioc_g_ext_ctrls == NULL)
return -ENOTTY;
return check_ext_ctrls(p, VIDIOC_G_EXT_CTRLS) ?
- ops->vidioc_g_ext_ctrls(file, fh, p) : -EINVAL;
+ ops->vidioc_g_ext_ctrls(file, NULL, p) : -EINVAL;
}
static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_ext_controls *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
p->error_idx = p->count;
if (vfh && vfh->ctrl_handler)
@@ -2291,16 +2445,15 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
if (ops->vidioc_s_ext_ctrls == NULL)
return -ENOTTY;
return check_ext_ctrls(p, VIDIOC_S_EXT_CTRLS) ?
- ops->vidioc_s_ext_ctrls(file, fh, p) : -EINVAL;
+ ops->vidioc_s_ext_ctrls(file, NULL, p) : -EINVAL;
}
static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_ext_controls *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
p->error_idx = p->count;
if (vfh && vfh->ctrl_handler)
@@ -2312,7 +2465,7 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
if (ops->vidioc_try_ext_ctrls == NULL)
return -ENOTTY;
return check_ext_ctrls(p, VIDIOC_TRY_EXT_CTRLS) ?
- ops->vidioc_try_ext_ctrls(file, fh, p) : -EINVAL;
+ ops->vidioc_try_ext_ctrls(file, NULL, p) : -EINVAL;
}
/*
@@ -2325,7 +2478,7 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
* type and drivers don't need to check for both.
*/
static int v4l_g_selection(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct v4l2_selection *p = arg;
u32 old_type = p->type;
@@ -2335,13 +2488,13 @@ static int v4l_g_selection(const struct v4l2_ioctl_ops *ops,
p->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
p->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- ret = ops->vidioc_g_selection(file, fh, p);
+ ret = ops->vidioc_g_selection(file, NULL, p);
p->type = old_type;
return ret;
}
static int v4l_s_selection(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct v4l2_selection *p = arg;
u32 old_type = p->type;
@@ -2351,13 +2504,13 @@ static int v4l_s_selection(const struct v4l2_ioctl_ops *ops,
p->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
p->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- ret = ops->vidioc_s_selection(file, fh, p);
+ ret = ops->vidioc_s_selection(file, NULL, p);
p->type = old_type;
return ret;
}
-static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_g_crop(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_crop *p = arg;
@@ -2378,7 +2531,7 @@ static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
s.target = s.target == V4L2_SEL_TGT_COMPOSE ?
V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE;
- ret = v4l_g_selection(ops, file, fh, &s);
+ ret = v4l_g_selection(ops, file, &s);
/* copying results to old structure on success */
if (!ret)
@@ -2386,8 +2539,8 @@ static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
return ret;
}
-static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_s_crop(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_crop *p = arg;
@@ -2408,11 +2561,11 @@ static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
s.target = s.target == V4L2_SEL_TGT_COMPOSE ?
V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE;
- return v4l_s_selection(ops, file, fh, &s);
+ return v4l_s_selection(ops, file, &s);
}
-static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_cropcap *p = arg;
@@ -2436,7 +2589,7 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
return -ENOTTY;
if (ops->vidioc_g_pixelaspect)
- ret = ops->vidioc_g_pixelaspect(file, fh, s.type,
+ ret = ops->vidioc_g_pixelaspect(file, NULL, s.type,
&p->pixelaspect);
/*
@@ -2458,7 +2611,7 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
s.target = s.target == V4L2_SEL_TGT_COMPOSE_BOUNDS ?
V4L2_SEL_TGT_CROP_BOUNDS : V4L2_SEL_TGT_COMPOSE_BOUNDS;
- ret = v4l_g_selection(ops, file, fh, &s);
+ ret = v4l_g_selection(ops, file, &s);
if (ret)
return ret;
p->bounds = s.r;
@@ -2469,7 +2622,7 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
else
s.target = V4L2_SEL_TGT_CROP_DEFAULT;
- ret = v4l_g_selection(ops, file, fh, &s);
+ ret = v4l_g_selection(ops, file, &s);
if (ret)
return ret;
p->defrect = s.r;
@@ -2477,8 +2630,8 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
return 0;
}
-static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_log_status(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
struct video_device *vfd = video_devdata(file);
int ret;
@@ -2486,7 +2639,7 @@ static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
if (vfd->v4l2_dev)
pr_info("%s: ================= START STATUS =================\n",
vfd->v4l2_dev->name);
- ret = ops->vidioc_log_status(file, fh);
+ ret = ops->vidioc_log_status(file, NULL);
if (vfd->v4l2_dev)
pr_info("%s: ================== END STATUS ==================\n",
vfd->v4l2_dev->name);
@@ -2494,7 +2647,7 @@ static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
}
static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
#ifdef CONFIG_VIDEO_ADV_DEBUG
struct v4l2_dbg_register *p = arg;
@@ -2514,7 +2667,7 @@ static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
}
if (ops->vidioc_g_register && p->match.type == V4L2_CHIP_MATCH_BRIDGE &&
(ops->vidioc_g_chip_info || p->match.addr == 0))
- return ops->vidioc_g_register(file, fh, p);
+ return ops->vidioc_g_register(file, NULL, p);
return -EINVAL;
#else
return -ENOTTY;
@@ -2522,7 +2675,7 @@ static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
}
static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
#ifdef CONFIG_VIDEO_ADV_DEBUG
const struct v4l2_dbg_register *p = arg;
@@ -2542,7 +2695,7 @@ static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
}
if (ops->vidioc_s_register && p->match.type == V4L2_CHIP_MATCH_BRIDGE &&
(ops->vidioc_g_chip_info || p->match.addr == 0))
- return ops->vidioc_s_register(file, fh, p);
+ return ops->vidioc_s_register(file, NULL, p);
return -EINVAL;
#else
return -ENOTTY;
@@ -2550,7 +2703,7 @@ static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
}
static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
#ifdef CONFIG_VIDEO_ADV_DEBUG
struct video_device *vfd = video_devdata(file);
@@ -2566,7 +2719,7 @@ static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
p->flags |= V4L2_CHIP_FL_READABLE;
strscpy(p->name, vfd->v4l2_dev->name, sizeof(p->name));
if (ops->vidioc_g_chip_info)
- return ops->vidioc_g_chip_info(file, fh, arg);
+ return ops->vidioc_g_chip_info(file, NULL, arg);
if (p->match.addr)
return -EINVAL;
return 0;
@@ -2592,26 +2745,32 @@ static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
#endif
}
-static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+static int v4l_dqevent(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *arg)
{
- return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
+
+ return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
}
static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
- return ops->vidioc_subscribe_event(fh, arg);
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
+
+ return ops->vidioc_subscribe_event(vfh, arg);
}
static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
- return ops->vidioc_unsubscribe_event(fh, arg);
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
+
+ return ops->vidioc_unsubscribe_event(vfh, arg);
}
static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct v4l2_sliced_vbi_cap *p = arg;
int ret = check_fmt(file, p->type);
@@ -2622,11 +2781,11 @@ static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
/* Clear up to type, everything after type is zeroed already */
memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
- return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
+ return ops->vidioc_g_sliced_vbi_cap(file, NULL, p);
}
static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
+ struct file *file, void *arg)
{
struct video_device *vfd = video_devdata(file);
struct v4l2_frequency_band *p = arg;
@@ -2644,7 +2803,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
return -EINVAL;
}
if (ops->vidioc_enum_freq_bands) {
- err = ops->vidioc_enum_freq_bands(file, fh, p);
+ err = ops->vidioc_enum_freq_bands(file, NULL, p);
if (err != -ENOTTY)
return err;
}
@@ -2656,7 +2815,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
if (p->index)
return -EINVAL;
- err = ops->vidioc_g_tuner(file, fh, &t);
+ err = ops->vidioc_g_tuner(file, NULL, &t);
if (err)
return err;
p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
@@ -2675,14 +2834,13 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
return -EINVAL;
if (p->index)
return -EINVAL;
- err = ops->vidioc_g_modulator(file, fh, &m);
+ err = ops->vidioc_g_modulator(file, NULL, &m);
if (err)
return err;
p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
p->rangelow = m.rangelow;
p->rangehigh = m.rangehigh;
- p->modulation = (type == V4L2_TUNER_RADIO) ?
- V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+ p->modulation = V4L2_BAND_MODULATION_FM;
return 0;
}
return -ENOTTY;
@@ -2693,7 +2851,7 @@ struct v4l2_ioctl_info {
u32 flags;
const char * const name;
int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file,
- void *fh, void *p);
+ void *p);
void (*debug)(const void *arg, bool write_only);
};
@@ -2714,9 +2872,9 @@ struct v4l2_ioctl_info {
#define DEFINE_V4L_STUB_FUNC(_vidioc) \
static int v4l_stub_ ## _vidioc( \
const struct v4l2_ioctl_ops *ops, \
- struct file *file, void *fh, void *p) \
+ struct file *file, void *p) \
{ \
- return ops->vidioc_ ## _vidioc(file, fh, p); \
+ return ops->vidioc_ ## _vidioc(file, NULL, p); \
}
#define IOCTL_INFO(_ioctl, _func, _debug, _flags) \
@@ -2729,7 +2887,6 @@ struct v4l2_ioctl_info {
}
DEFINE_V4L_STUB_FUNC(g_fbuf)
-DEFINE_V4L_STUB_FUNC(s_fbuf)
DEFINE_V4L_STUB_FUNC(expbuf)
DEFINE_V4L_STUB_FUNC(g_std)
DEFINE_V4L_STUB_FUNC(g_audio)
@@ -2763,7 +2920,7 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
IOCTL_INFO(VIDIOC_G_FBUF, v4l_stub_g_fbuf, v4l_print_framebuffer, 0),
- IOCTL_INFO(VIDIOC_S_FBUF, v4l_stub_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_S_FBUF, v4l_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
IOCTL_INFO(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
IOCTL_INFO(VIDIOC_EXPBUF, v4l_stub_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
@@ -2812,9 +2969,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
IOCTL_INFO(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
- IOCTL_INFO(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
- IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL | INFO_FL_ALWAYS_COPY),
+ IOCTL_INFO(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL | INFO_FL_ALWAYS_COPY),
+ IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL | INFO_FL_ALWAYS_COPY),
IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, v4l_stub_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, v4l_stub_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
IOCTL_INFO(VIDIOC_G_ENC_INDEX, v4l_stub_g_enc_index, v4l_print_enc_idx, 0),
@@ -2838,6 +2995,7 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
+ IOCTL_INFO(VIDIOC_REMOVE_BUFS, v4l_remove_bufs, v4l_print_remove_buffers, INFO_FL_PRIO | INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_remove_buffers, type)),
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -2879,7 +3037,7 @@ void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
type = "v4l2_int";
break;
case 'V':
- if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
+ if (!v4l2_is_known_ioctl(cmd)) {
type = "v4l2";
break;
}
@@ -2912,8 +3070,7 @@ static long __video_do_ioctl(struct file *file,
bool write_only = false;
struct v4l2_ioctl_info default_info;
const struct v4l2_ioctl_info *info;
- void *fh = file->private_data;
- struct v4l2_fh *vfh = NULL;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
int dev_debug = vfd->dev_debug;
long ret = -ENOTTY;
@@ -2923,9 +3080,6 @@ static long __video_do_ioctl(struct file *file,
return ret;
}
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
- vfh = file->private_data;
-
/*
* We need to serialize streamon/off with queueing new requests.
* These ioctls may trigger the cancellation of a streaming
@@ -2956,11 +3110,11 @@ static long __video_do_ioctl(struct file *file,
if (v4l2_is_known_ioctl(cmd)) {
info = &v4l2_ioctls[_IOC_NR(cmd)];
- if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
- !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
+ if (!is_valid_ioctl(vfd, cmd) &&
+ !((info->flags & INFO_FL_CTRL) && vfh->ctrl_handler))
goto done;
- if (vfh && (info->flags & INFO_FL_PRIO)) {
+ if (info->flags & INFO_FL_PRIO) {
ret = v4l2_prio_check(vfd->prio, vfh->prio);
if (ret)
goto done;
@@ -2974,12 +3128,12 @@ static long __video_do_ioctl(struct file *file,
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
if (info != &default_info) {
- ret = info->func(ops, file, fh, arg);
+ ret = info->func(ops, file, arg);
} else if (!ops->vidioc_default) {
ret = -ENOTTY;
} else {
- ret = ops->vidioc_default(file, fh,
- vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
+ ret = ops->vidioc_default(file, NULL,
+ v4l2_prio_check(vfd->prio, vfh->prio) >= 0,
cmd, arg);
}
@@ -3070,24 +3224,18 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
}
break;
}
- case VIDIOC_G_FMT:
- case VIDIOC_S_FMT:
- case VIDIOC_TRY_FMT: {
- struct v4l2_format *fmt = parg;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_OVERLAY &&
- fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
- break;
- if (fmt->fmt.win.clipcount > 2048)
- return -EINVAL;
- if (!fmt->fmt.win.clipcount)
- break;
+ case VIDIOC_SUBDEV_G_ROUTING:
+ case VIDIOC_SUBDEV_S_ROUTING: {
+ struct v4l2_subdev_routing *routing = parg;
- *user_ptr = (void __user *)fmt->fmt.win.clips;
- *kernel_ptr = (void **)&fmt->fmt.win.clips;
- *array_size = sizeof(struct v4l2_clip)
- * fmt->fmt.win.clipcount;
+ if (routing->len_routes > 256)
+ return -E2BIG;
+ *user_ptr = u64_to_user_ptr(routing->routes);
+ *kernel_ptr = (void **)&routing->routes;
+ *array_size = sizeof(struct v4l2_subdev_route)
+ * routing->len_routes;
ret = 1;
break;
}
@@ -3096,7 +3244,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
return ret;
}
-static unsigned int video_translate_cmd(unsigned int cmd)
+unsigned int v4l2_translate_cmd(unsigned int cmd)
{
#if !defined(CONFIG_64BIT) && defined(CONFIG_COMPAT_32BIT_TIME)
switch (cmd) {
@@ -3117,6 +3265,7 @@ static unsigned int video_translate_cmd(unsigned int cmd)
return cmd;
}
+EXPORT_SYMBOL_GPL(v4l2_translate_cmd);
static int video_get_user(void __user *arg, void *parg,
unsigned int real_cmd, unsigned int cmd,
@@ -3277,7 +3426,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
size_t array_size = 0;
void __user *user_ptr = NULL;
void **kernel_ptr = NULL;
- unsigned int cmd = video_translate_cmd(orig_cmd);
+ unsigned int cmd = v4l2_translate_cmd(orig_cmd);
const size_t ioc_size = _IOC_SIZE(cmd);
/* Copy arguments into temp kernel buffer */
@@ -3307,8 +3456,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
array_buf = kvmalloc(array_size, GFP_KERNEL);
err = -ENOMEM;
if (array_buf == NULL)
- goto out_array_args;
- err = -EFAULT;
+ goto out;
if (in_compat_syscall())
err = v4l2_compat_get_array_args(file, array_buf,
user_ptr, array_size,
@@ -3317,7 +3465,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
err = copy_from_user(array_buf, user_ptr, array_size) ?
-EFAULT : 0;
if (err)
- goto out_array_args;
+ goto out;
*kernel_ptr = array_buf;
}
@@ -3335,6 +3483,23 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
trace_v4l2_qbuf(video_devdata(file)->minor, parg);
}
+ /*
+ * Some ioctls can return an error, but still have valid
+ * results that must be returned.
+ *
+ * FIXME: subdev IOCTLS are partially handled here and partially in
+ * v4l2-subdev.c and the 'always_copy' flag can only be set for IOCTLS
+ * defined here as part of the 'v4l2_ioctls' array. As
+ * VIDIOC_SUBDEV_[GS]_ROUTING needs to return results to applications
+ * even in case of failure, but it is not defined here as part of the
+ * 'v4l2_ioctls' array, insert an ad-hoc check to address that.
+ */
+ if (cmd == VIDIOC_SUBDEV_G_ROUTING || cmd == VIDIOC_SUBDEV_S_ROUTING)
+ always_copy = true;
+
+ if (err < 0 && !always_copy)
+ goto out;
+
if (has_array_args) {
*kernel_ptr = (void __force *)user_ptr;
if (in_compat_syscall()) {
@@ -3349,16 +3514,8 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
} else if (copy_to_user(user_ptr, array_buf, array_size)) {
err = -EFAULT;
}
- goto out_array_args;
}
- /*
- * Some ioctls can return an error, but still have valid
- * results that must be returned.
- */
- if (err < 0 && !always_copy)
- goto out;
-out_array_args:
if (video_put_user((void __user *)arg, parg, cmd, orig_cmd))
err = -EFAULT;
out:
diff --git a/drivers/media/v4l2-core/v4l2-isp.c b/drivers/media/v4l2-core/v4l2-isp.c
new file mode 100644
index 000000000000..29831f7032e9
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-isp.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Video4Linux2 generic ISP parameters and statistics support
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Author: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+ */
+
+#include <media/v4l2-isp.h>
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+
+#include <media/videobuf2-core.h>
+
+int v4l2_isp_params_validate_buffer_size(struct device *dev,
+ struct vb2_buffer *vb,
+ size_t max_size)
+{
+ size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+
+ /* Payload size can't be greater than the destination buffer size */
+ if (payload_size > max_size) {
+ dev_dbg(dev, "Payload size is too large: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ /* Payload size can't be smaller than the header size */
+ if (payload_size < header_size) {
+ dev_dbg(dev, "Payload size is too small: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer_size);
+
+int v4l2_isp_params_validate_buffer(struct device *dev, struct vb2_buffer *vb,
+ const struct v4l2_isp_params_buffer *buffer,
+ const struct v4l2_isp_params_block_type_info *type_info,
+ size_t num_block_types)
+{
+ size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+ size_t block_offset = 0;
+ size_t buffer_size;
+
+ /*
+ * Currently only the first version of the V4L2 ISP parameters format is
+ * supported. We accept both V0 and V1 to support existing drivers
+ * compatible with V4L2 ISP that use either 0 or 1 as their "first
+ * version" identifiers.
+ */
+ if (buffer->version != V4L2_ISP_PARAMS_VERSION_V0 &&
+ buffer->version != V4L2_ISP_PARAMS_VERSION_V1) {
+ dev_dbg(dev,
+ "Unsupported V4L2 ISP parameters format version: %u\n",
+ buffer->version);
+ return -EINVAL;
+ }
+
+ /* Validate the size reported in the header */
+ buffer_size = header_size + buffer->data_size;
+ if (buffer_size != payload_size) {
+ dev_dbg(dev, "Data size %zu and payload size %zu are different\n",
+ buffer_size, payload_size);
+ return -EINVAL;
+ }
+
+ /* Walk the list of ISP configuration blocks and validate them. */
+ buffer_size = buffer->data_size;
+ while (buffer_size >= sizeof(struct v4l2_isp_params_block_header)) {
+ const struct v4l2_isp_params_block_type_info *info;
+ const struct v4l2_isp_params_block_header *block;
+
+ block = (const struct v4l2_isp_params_block_header *)
+ (buffer->data + block_offset);
+
+ if (block->type >= num_block_types) {
+ dev_dbg(dev,
+ "Invalid block type %u at offset %zu\n",
+ block->type, block_offset);
+ return -EINVAL;
+ }
+
+ if (block->size > buffer_size) {
+ dev_dbg(dev, "Premature end of parameters data\n");
+ return -EINVAL;
+ }
+
+ /* It's invalid to specify both ENABLE and DISABLE. */
+ if ((block->flags & (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
+ V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) ==
+ (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
+ V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) {
+ dev_dbg(dev, "Invalid block flags %x at offset %zu\n",
+ block->flags, block_offset);
+ return -EINVAL;
+ }
+
+ /*
+ * Match the block reported size against the type info provided
+ * one, but allow the block to only contain the header in
+ * case it is going to be disabled.
+ */
+ info = &type_info[block->type];
+ if (block->size != info->size &&
+ (!(block->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) ||
+ block->size != sizeof(*block))) {
+ dev_dbg(dev,
+ "Invalid block size %u (expected %zu) at offset %zu\n",
+ block->size, info->size, block_offset);
+ return -EINVAL;
+ }
+
+ block_offset += block->size;
+ buffer_size -= block->size;
+ }
+
+ if (buffer_size) {
+ dev_dbg(dev, "Unexpected data after the parameters buffer end\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com");
+MODULE_DESCRIPTION("V4L2 generic ISP parameters and statistics helpers");
diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c
index c2513b775f6a..36a0f1a1b0d9 100644
--- a/drivers/media/v4l2-core/v4l2-jpeg.c
+++ b/drivers/media/v4l2-core/v4l2-jpeg.c
@@ -9,7 +9,7 @@
* [1] https://www.w3.org/Graphics/JPEG/itu-t81.pdf
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -52,6 +52,122 @@ MODULE_LICENSE("GPL");
#define COM 0xfffe /* comment */
#define TEM 0xff01 /* temporary */
+/* Luma and chroma qp tables to achieve 50% compression quality
+ * This is as per example in Annex K.1 of ITU-T.81
+ */
+const u8 v4l2_jpeg_ref_table_luma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = {
+ 16, 11, 10, 16, 24, 40, 51, 61,
+ 12, 12, 14, 19, 26, 58, 60, 55,
+ 14, 13, 16, 24, 40, 57, 69, 56,
+ 14, 17, 22, 29, 51, 87, 80, 62,
+ 18, 22, 37, 56, 68, 109, 103, 77,
+ 24, 35, 55, 64, 81, 104, 113, 92,
+ 49, 64, 78, 87, 103, 121, 120, 101,
+ 72, 92, 95, 98, 112, 100, 103, 99
+};
+EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_qt);
+
+const u8 v4l2_jpeg_ref_table_chroma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = {
+ 17, 18, 24, 47, 99, 99, 99, 99,
+ 18, 21, 26, 66, 99, 99, 99, 99,
+ 24, 26, 56, 99, 99, 99, 99, 99,
+ 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+};
+EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_qt);
+
+/* Zigzag scan pattern indexes */
+const u8 v4l2_jpeg_zigzag_scan_index[V4L2_JPEG_PIXELS_IN_BLOCK] = {
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+};
+EXPORT_SYMBOL_GPL(v4l2_jpeg_zigzag_scan_index);
+
+/*
+ * Contains the data that needs to be sent in the marker segment of an
+ * interchange format JPEG stream or an abbreviated format table specification
+ * data stream. Specifies the huffman table used for encoding the luminance DC
+ * coefficient differences. The table represents Table K.3 of ITU-T.81
+ */
+const u8 v4l2_jpeg_ref_table_luma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = {
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B
+};
+EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_dc_ht);
+
+/*
+ * Contains the data that needs to be sent in the marker segment of an
+ * interchange format JPEG stream or an abbreviated format table specification
+ * data stream. Specifies the huffman table used for encoding the luminance AC
+ * coefficients. The table represents Table K.5 of ITU-T.81
+ */
+const u8 v4l2_jpeg_ref_table_luma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = {
+ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04,
+ 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32,
+ 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A,
+ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2,
+ 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
+ 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
+ 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
+ 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
+};
+EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_ac_ht);
+
+/*
+ * Contains the data that needs to be sent in the marker segment of an interchange format JPEG
+ * stream or an abbreviated format table specification data stream.
+ * Specifies the huffman table used for encoding the chrominance DC coefficient differences.
+ * The table represents Table K.4 of ITU-T.81
+ */
+const u8 v4l2_jpeg_ref_table_chroma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = {
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B
+};
+EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_dc_ht);
+
+/*
+ * Contains the data that needs to be sent in the marker segment of an
+ * interchange format JPEG stream or an abbreviated format table specification
+ * data stream. Specifies the huffman table used for encoding the chrominance
+ * AC coefficients. The table represents Table K.6 of ITU-T.81
+ */
+const u8 v4l2_jpeg_ref_table_chroma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = {
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
+ 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+ 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81,
+ 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
+ 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17,
+ 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
+ 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
+ 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+ 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9,
+ 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
+};
+EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_ac_ht);
+
/**
* struct jpeg_stream - JPEG byte stream
* @curr: current position in stream
@@ -460,7 +576,7 @@ static int jpeg_parse_app14_data(struct jpeg_stream *stream,
/* Check for "Adobe\0" in Ap1..6 */
if (stream->curr + 6 > stream->end ||
strncmp(stream->curr, "Adobe\0", 6))
- return -EINVAL;
+ return jpeg_skip(stream, lp - 2);
/* get to Ap12 */
ret = jpeg_skip(stream, 11);
@@ -474,7 +590,7 @@ static int jpeg_parse_app14_data(struct jpeg_stream *stream,
*tf = ret;
/* skip the rest of the segment, this ensures at least it is complete */
- skip = lp - 2 - 11;
+ skip = lp - 2 - 11 - 1;
return jpeg_skip(stream, skip);
}
@@ -595,83 +711,3 @@ int v4l2_jpeg_parse_header(void *buf, size_t len, struct v4l2_jpeg_header *out)
return marker;
}
EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_header);
-
-/**
- * v4l2_jpeg_parse_frame_header - parse frame header
- * @buf: address of the frame header, after the SOF0 marker
- * @len: length of the frame header
- * @frame_header: returns the parsed frame header
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_frame_header(void *buf, size_t len,
- struct v4l2_jpeg_frame_header *frame_header)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_frame_header(&stream, SOF0, frame_header);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_frame_header);
-
-/**
- * v4l2_jpeg_parse_scan_header - parse scan header
- * @buf: address of the scan header, after the SOS marker
- * @len: length of the scan header
- * @scan_header: returns the parsed scan header
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_scan_header(void *buf, size_t len,
- struct v4l2_jpeg_scan_header *scan_header)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_scan_header(&stream, scan_header);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_scan_header);
-
-/**
- * v4l2_jpeg_parse_quantization_tables - parse quantization tables segment
- * @buf: address of the quantization table segment, after the DQT marker
- * @len: length of the quantization table segment
- * @precision: sample precision (P) in bits per component
- * @q_tables: returns four references into the buffer for the
- * four possible quantization table destinations
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision,
- struct v4l2_jpeg_reference *q_tables)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_quantization_tables(&stream, precision, q_tables);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_quantization_tables);
-
-/**
- * v4l2_jpeg_parse_huffman_tables - parse huffman tables segment
- * @buf: address of the Huffman table segment, after the DHT marker
- * @len: length of the Huffman table segment
- * @huffman_tables: returns four references into the buffer for the
- * four possible Huffman table destinations, in
- * the order DC0, DC1, AC0, AC1
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_huffman_tables(void *buf, size_t len,
- struct v4l2_jpeg_reference *huffman_tables)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_huffman_tables(&stream, huffman_tables);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_huffman_tables);
diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c
index b01474717dca..937d358697e1 100644
--- a/drivers/media/v4l2-core/v4l2-mc.c
+++ b/drivers/media/v4l2-core/v4l2-mc.c
@@ -105,9 +105,11 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
/* Link the tuner and IF video output pads */
if (tuner) {
if (if_vid) {
- pad_source = media_get_pad_index(tuner, false,
+ pad_source = media_get_pad_index(tuner,
+ MEDIA_PAD_FL_SOURCE,
PAD_SIGNAL_ANALOG);
- pad_sink = media_get_pad_index(if_vid, true,
+ pad_sink = media_get_pad_index(if_vid,
+ MEDIA_PAD_FL_SINK,
PAD_SIGNAL_ANALOG);
if (pad_source < 0 || pad_sink < 0) {
dev_warn(mdev->dev, "Couldn't get tuner and/or PLL pad(s): (%d, %d)\n",
@@ -122,9 +124,11 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
return ret;
}
- pad_source = media_get_pad_index(if_vid, false,
+ pad_source = media_get_pad_index(if_vid,
+ MEDIA_PAD_FL_SOURCE,
PAD_SIGNAL_ANALOG);
- pad_sink = media_get_pad_index(decoder, true,
+ pad_sink = media_get_pad_index(decoder,
+ MEDIA_PAD_FL_SINK,
PAD_SIGNAL_ANALOG);
if (pad_source < 0 || pad_sink < 0) {
dev_warn(mdev->dev, "get decoder and/or PLL pad(s): (%d, %d)\n",
@@ -139,9 +143,11 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
return ret;
}
} else {
- pad_source = media_get_pad_index(tuner, false,
+ pad_source = media_get_pad_index(tuner,
+ MEDIA_PAD_FL_SOURCE,
PAD_SIGNAL_ANALOG);
- pad_sink = media_get_pad_index(decoder, true,
+ pad_sink = media_get_pad_index(decoder,
+ MEDIA_PAD_FL_SINK,
PAD_SIGNAL_ANALOG);
if (pad_source < 0 || pad_sink < 0) {
dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s): (%d, %d)\n",
@@ -156,9 +162,11 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
}
if (if_aud) {
- pad_source = media_get_pad_index(tuner, false,
+ pad_source = media_get_pad_index(tuner,
+ MEDIA_PAD_FL_SOURCE,
PAD_SIGNAL_AUDIO);
- pad_sink = media_get_pad_index(if_aud, true,
+ pad_sink = media_get_pad_index(if_aud,
+ MEDIA_PAD_FL_SINK,
PAD_SIGNAL_AUDIO);
if (pad_source < 0 || pad_sink < 0) {
dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s) for audio: (%d, %d)\n",
@@ -180,7 +188,8 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
/* Create demod to V4L, VBI and SDR radio links */
if (io_v4l) {
- pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV);
+ pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
+ PAD_SIGNAL_DV);
if (pad_source < 0) {
dev_warn(mdev->dev, "couldn't get decoder output pad for V4L I/O\n");
return -EINVAL;
@@ -195,7 +204,8 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
}
if (io_swradio) {
- pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV);
+ pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
+ PAD_SIGNAL_DV);
if (pad_source < 0) {
dev_warn(mdev->dev, "couldn't get decoder output pad for SDR\n");
return -EINVAL;
@@ -210,7 +220,8 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
}
if (io_vbi) {
- pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV);
+ pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
+ PAD_SIGNAL_DV);
if (pad_source < 0) {
dev_warn(mdev->dev, "couldn't get decoder output pad for VBI\n");
return -EINVAL;
@@ -231,7 +242,7 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
case MEDIA_ENT_F_CONN_RF:
if (!tuner)
continue;
- pad_sink = media_get_pad_index(tuner, true,
+ pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK,
PAD_SIGNAL_ANALOG);
if (pad_sink < 0) {
dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n");
@@ -243,7 +254,8 @@ int v4l2_mc_create_media_graph(struct media_device *mdev)
break;
case MEDIA_ENT_F_CONN_SVIDEO:
case MEDIA_ENT_F_CONN_COMPOSITE:
- pad_sink = media_get_pad_index(decoder, true,
+ pad_sink = media_get_pad_index(decoder,
+ MEDIA_PAD_FL_SINK,
PAD_SIGNAL_ANALOG);
if (pad_sink < 0) {
dev_warn(mdev->dev, "couldn't get decoder analog pad sink\n");
@@ -313,15 +325,11 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
struct media_pad *sink, u32 flags)
{
struct fwnode_handle *endpoint;
- struct v4l2_subdev *sink_sd;
- if (!(sink->flags & MEDIA_PAD_FL_SINK) ||
- !is_media_entity_v4l2_subdev(sink->entity))
+ if (!(sink->flags & MEDIA_PAD_FL_SINK))
return -EINVAL;
- sink_sd = media_entity_to_v4l2_subdev(sink->entity);
-
- fwnode_graph_for_each_endpoint(dev_fwnode(src_sd->dev), endpoint) {
+ fwnode_graph_for_each_endpoint(src_sd->fwnode, endpoint) {
struct fwnode_handle *remote_ep;
int src_idx, sink_idx, ret;
struct media_pad *src;
@@ -329,24 +337,34 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
src_idx = media_entity_get_fwnode_pad(&src_sd->entity,
endpoint,
MEDIA_PAD_FL_SOURCE);
- if (src_idx < 0)
+ if (src_idx < 0) {
+ dev_dbg(src_sd->dev, "no source pad found for %pfw\n",
+ endpoint);
continue;
+ }
remote_ep = fwnode_graph_get_remote_endpoint(endpoint);
- if (!remote_ep)
+ if (!remote_ep) {
+ dev_dbg(src_sd->dev, "no remote ep found for %pfw\n",
+ endpoint);
continue;
+ }
/*
* ask the sink to verify it owns the remote endpoint,
* and translate to a sink pad.
*/
- sink_idx = media_entity_get_fwnode_pad(&sink_sd->entity,
+ sink_idx = media_entity_get_fwnode_pad(sink->entity,
remote_ep,
MEDIA_PAD_FL_SINK);
fwnode_handle_put(remote_ep);
- if (sink_idx < 0 || sink_idx != sink->index)
+ if (sink_idx < 0 || sink_idx != sink->index) {
+ dev_dbg(src_sd->dev,
+ "sink pad index mismatch or error (is %d, expected %u)\n",
+ sink_idx, sink->index);
continue;
+ }
/*
* the source endpoint corresponds to one of its source pads,
@@ -359,20 +377,25 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
src = &src_sd->entity.pads[src_idx];
/* skip if link already exists */
- if (media_entity_find_link(src, sink))
+ if (media_entity_find_link(src, sink)) {
+ dev_dbg(src_sd->dev,
+ "link %s:%d -> %s:%d already exists\n",
+ src_sd->entity.name, src_idx,
+ sink->entity->name, sink_idx);
continue;
+ }
- dev_dbg(sink_sd->dev, "creating link %s:%d -> %s:%d\n",
+ dev_dbg(src_sd->dev, "creating link %s:%d -> %s:%d\n",
src_sd->entity.name, src_idx,
- sink_sd->entity.name, sink_idx);
+ sink->entity->name, sink_idx);
ret = media_create_pad_link(&src_sd->entity, src_idx,
- &sink_sd->entity, sink_idx, flags);
+ sink->entity, sink_idx, flags);
if (ret) {
- dev_err(sink_sd->dev,
+ dev_err(src_sd->dev,
"link %s:%d -> %s:%d failed with %d\n",
src_sd->entity.name, src_idx,
- sink_sd->entity.name, sink_idx, ret);
+ sink->entity->name, sink_idx, ret);
fwnode_handle_put(endpoint);
return ret;
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index e2654b422334..fec93c1a9231 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Memory-to-memory device framework for Video for Linux 2 and videobuf.
+ * Memory-to-memory device framework for Video for Linux 2 and vb2.
*
- * Helper functions for devices that use videobuf buffers for both their
+ * Helper functions for devices that use vb2 buffers for both their
* source and destination.
*
* Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
@@ -21,7 +21,7 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
-MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
+MODULE_DESCRIPTION("Mem to mem device framework for vb2");
MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
MODULE_LICENSE("GPL");
@@ -68,16 +68,16 @@ static const char * const m2m_entity_name[] = {
* struct v4l2_m2m_dev - per-device context
* @source: &struct media_entity pointer with the source entity
* Used only when the M2M device is registered via
- * v4l2_m2m_unregister_media_controller().
+ * v4l2_m2m_register_media_controller().
* @source_pad: &struct media_pad with the source pad.
* Used only when the M2M device is registered via
- * v4l2_m2m_unregister_media_controller().
+ * v4l2_m2m_register_media_controller().
* @sink: &struct media_entity pointer with the sink entity
* Used only when the M2M device is registered via
- * v4l2_m2m_unregister_media_controller().
+ * v4l2_m2m_register_media_controller().
* @sink_pad: &struct media_pad with the sink pad.
* Used only when the M2M device is registered via
- * v4l2_m2m_unregister_media_controller().
+ * v4l2_m2m_register_media_controller().
* @proc: &struct media_entity pointer with the M2M device itself.
* @proc_pads: &struct media_pad with the @proc pads.
* Used only when the M2M device is registered via
@@ -123,13 +123,7 @@ static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
enum v4l2_buf_type type)
{
- struct v4l2_m2m_queue_ctx *q_ctx;
-
- q_ctx = get_queue_ctx(m2m_ctx, type);
- if (!q_ctx)
- return NULL;
-
- return &q_ctx->q;
+ return &get_queue_ctx(m2m_ctx, type)->q;
}
EXPORT_SYMBOL(v4l2_m2m_get_vq);
@@ -301,9 +295,12 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,
dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
- if (!m2m_ctx->out_q_ctx.q.streaming
- || !m2m_ctx->cap_q_ctx.q.streaming) {
- dprintk("Streaming needs to be on for both queues\n");
+ if (!m2m_ctx->out_q_ctx.q.streaming ||
+ (!m2m_ctx->cap_q_ctx.q.streaming && !m2m_ctx->ignore_cap_streaming)) {
+ if (!m2m_ctx->ignore_cap_streaming)
+ dprintk("Streaming needs to be on for both queues\n");
+ else
+ dprintk("Streaming needs to be on for the OUTPUT queue\n");
return;
}
@@ -336,6 +333,7 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,
if (src && dst && dst->is_held &&
dst->vb2_buf.copied_timestamp &&
dst->vb2_buf.timestamp != src->vb2_buf.timestamp) {
+ dprintk("Timestamp mismatch, returning held capture buffer\n");
dst->is_held = false;
v4l2_m2m_dst_buf_remove(m2m_ctx);
v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
@@ -585,19 +583,14 @@ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
}
EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
-int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- struct v4l2_buffer *buf)
+static void v4l2_m2m_adjust_mem_offset(struct vb2_queue *vq,
+ struct v4l2_buffer *buf)
{
- struct vb2_queue *vq;
- int ret = 0;
- unsigned int i;
-
- vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- ret = vb2_querybuf(vq, buf);
-
/* Adjust MMAP memory offsets for the CAPTURE queue */
if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) {
if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+ unsigned int i;
+
for (i = 0; i < buf->length; ++i)
buf->m.planes[i].m.mem_offset
+= DST_QUEUE_OFF_BASE;
@@ -605,8 +598,23 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
buf->m.offset += DST_QUEUE_OFF_BASE;
}
}
+}
- return ret;
+int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+ ret = vb2_querybuf(vq, buf);
+ if (ret)
+ return ret;
+
+ /* Adjust MMAP memory offsets for the CAPTURE queue */
+ v4l2_m2m_adjust_mem_offset(vq, buf);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
@@ -763,6 +771,9 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
if (ret)
return ret;
+ /* Adjust MMAP memory offsets for the CAPTURE queue */
+ v4l2_m2m_adjust_mem_offset(vq, buf);
+
/*
* If the capture queue is streaming, but streaming hasn't started
* on the device, but was asked to stop, mark the previously queued
@@ -784,9 +795,17 @@ int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct v4l2_buffer *buf)
{
struct vb2_queue *vq;
+ int ret;
vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+ ret = vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+ if (ret)
+ return ret;
+
+ /* Adjust MMAP memory offsets for the CAPTURE queue */
+ v4l2_m2m_adjust_mem_offset(vq, buf);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
@@ -795,9 +814,17 @@ int v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
{
struct video_device *vdev = video_devdata(file);
struct vb2_queue *vq;
+ int ret;
vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
+ ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
+ if (ret)
+ return ret;
+
+ /* Adjust MMAP memory offsets for the CAPTURE queue */
+ v4l2_m2m_adjust_mem_offset(vq, buf);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf);
@@ -892,10 +919,10 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file,
* means either in driver already or waiting for driver to claim it
* and start processing.
*/
- if ((!src_q->streaming || src_q->error ||
+ if ((!vb2_is_streaming(src_q) || src_q->error ||
list_empty(&src_q->queued_list)) &&
- (!dst_q->streaming || dst_q->error ||
- list_empty(&dst_q->queued_list)))
+ (!vb2_is_streaming(dst_q) || dst_q->error ||
+ (list_empty(&dst_q->queued_list) && !dst_q->last_buffer_dequeued)))
return EPOLLERR;
spin_lock_irqsave(&src_q->done_lock, flags);
@@ -918,7 +945,7 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file,
__poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
struct poll_table_struct *wait)
{
- struct video_device *vfd = video_devdata(file);
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
struct vb2_queue *src_q = v4l2_m2m_get_src_vq(m2m_ctx);
struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
__poll_t req_events = poll_requested_events(wait);
@@ -937,13 +964,9 @@ __poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
if (req_events & (EPOLLOUT | EPOLLWRNORM | EPOLLIN | EPOLLRDNORM))
rc = v4l2_m2m_poll_for_data(file, m2m_ctx, wait);
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
- struct v4l2_fh *fh = file->private_data;
-
- poll_wait(file, &fh->wait, wait);
- if (v4l2_event_pending(fh))
- rc |= EPOLLPRI;
- }
+ poll_wait(file, &fh->wait, wait);
+ if (v4l2_event_pending(fh))
+ rc |= EPOLLPRI;
return rc;
}
@@ -971,7 +994,7 @@ unsigned long v4l2_m2m_get_unmapped_area(struct file *file, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
unsigned long offset = pgoff << PAGE_SHIFT;
struct vb2_queue *vq;
@@ -1054,11 +1077,17 @@ static int v4l2_m2m_register_entity(struct media_device *mdev,
entity->function = function;
ret = media_entity_pads_init(entity, num_pads, pads);
- if (ret)
+ if (ret) {
+ kfree(entity->name);
+ entity->name = NULL;
return ret;
+ }
ret = media_device_register_entity(mdev, entity);
- if (ret)
+ if (ret) {
+ kfree(entity->name);
+ entity->name = NULL;
return ret;
+ }
return 0;
}
@@ -1250,8 +1279,6 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
unsigned long flags;
q_ctx = get_queue_ctx(m2m_ctx, vbuf->vb2_buf.vb2_queue->type);
- if (!q_ctx)
- return;
spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
list_add_tail(&b->list, &q_ctx->rdy_queue);
@@ -1261,14 +1288,9 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb,
- struct vb2_v4l2_buffer *cap_vb,
- bool copy_frame_flags)
+ struct vb2_v4l2_buffer *cap_vb)
{
- u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-
- if (copy_frame_flags)
- mask |= V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
- V4L2_BUF_FLAG_BFRAME;
+ const u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
cap_vb->vb2_buf.timestamp = out_vb->vb2_buf.timestamp;
@@ -1332,7 +1354,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_request_queue);
int v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *rb)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_reqbufs(file, fh->m2m_ctx, rb);
}
@@ -1341,16 +1363,29 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_reqbufs);
int v4l2_m2m_ioctl_create_bufs(struct file *file, void *priv,
struct v4l2_create_buffers *create)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_create_bufs(file, fh->m2m_ctx, create);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_create_bufs);
+int v4l2_m2m_ioctl_remove_bufs(struct file *file, void *priv,
+ struct v4l2_remove_buffers *remove)
+{
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
+ struct vb2_queue *q = v4l2_m2m_get_vq(fh->m2m_ctx, remove->type);
+
+ if (q->type != remove->type)
+ return -EINVAL;
+
+ return vb2_core_remove_bufs(q, remove->index, remove->count);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_remove_bufs);
+
int v4l2_m2m_ioctl_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_querybuf(file, fh->m2m_ctx, buf);
}
@@ -1359,7 +1394,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_querybuf);
int v4l2_m2m_ioctl_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_qbuf(file, fh->m2m_ctx, buf);
}
@@ -1368,7 +1403,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_qbuf);
int v4l2_m2m_ioctl_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_dqbuf(file, fh->m2m_ctx, buf);
}
@@ -1377,7 +1412,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_dqbuf);
int v4l2_m2m_ioctl_prepare_buf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_prepare_buf(file, fh->m2m_ctx, buf);
}
@@ -1386,7 +1421,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_prepare_buf);
int v4l2_m2m_ioctl_expbuf(struct file *file, void *priv,
struct v4l2_exportbuffer *eb)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_expbuf(file, fh->m2m_ctx, eb);
}
@@ -1395,7 +1430,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_expbuf);
int v4l2_m2m_ioctl_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_streamon(file, fh->m2m_ctx, type);
}
@@ -1404,13 +1439,13 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamon);
int v4l2_m2m_ioctl_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_streamoff(file, fh->m2m_ctx, type);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamoff);
-int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh,
+int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *priv,
struct v4l2_encoder_cmd *ec)
{
if (ec->cmd != V4L2_ENC_CMD_STOP && ec->cmd != V4L2_ENC_CMD_START)
@@ -1421,7 +1456,7 @@ int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh,
}
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_encoder_cmd);
-int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh,
+int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *dc)
{
if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START)
@@ -1488,7 +1523,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_decoder_cmd);
int v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *priv,
struct v4l2_encoder_cmd *ec)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_encoder_cmd(file, fh->m2m_ctx, ec);
}
@@ -1497,13 +1532,13 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_encoder_cmd);
int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *dc)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_decoder_cmd(file, fh->m2m_ctx, dc);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_decoder_cmd);
-int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh,
+int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *dc)
{
if (dc->cmd != V4L2_DEC_CMD_FLUSH)
@@ -1518,7 +1553,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_try_decoder_cmd);
int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *dc)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
struct vb2_v4l2_buffer *out_vb, *cap_vb;
struct v4l2_m2m_dev *m2m_dev = fh->m2m_ctx->m2m_dev;
unsigned long flags;
@@ -1563,7 +1598,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_decoder_cmd);
int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
return v4l2_m2m_mmap(file, fh->m2m_ctx, vma);
}
@@ -1571,7 +1606,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_fop_mmap);
__poll_t v4l2_m2m_fop_poll(struct file *file, poll_table *wait)
{
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
struct v4l2_m2m_ctx *m2m_ctx = fh->m2m_ctx;
__poll_t ret;
diff --git a/drivers/media/v4l2-core/v4l2-spi.c b/drivers/media/v4l2-core/v4l2-spi.c
index eadecdff7349..1baf8e63f19e 100644
--- a/drivers/media/v4l2-core/v4l2-spi.c
+++ b/drivers/media/v4l2-core/v4l2-spi.c
@@ -34,7 +34,7 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);
struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
- struct spi_master *master,
+ struct spi_controller *ctlr,
struct spi_board_info *info)
{
struct v4l2_subdev *sd = NULL;
@@ -45,7 +45,7 @@ struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
if (info->modalias[0])
request_module(info->modalias);
- spi = spi_new_device(master, info);
+ spi = spi_new_device(ctlr, info);
if (!spi || !spi->dev.driver)
goto error;
@@ -59,7 +59,7 @@ struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
* Register with the v4l2_device which increases the module's
* use count as well.
*/
- if (v4l2_device_register_subdev(v4l2_dev, sd))
+ if (__v4l2_device_register_subdev(v4l2_dev, sd, sd->owner))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
diff --git a/drivers/media/v4l2-core/v4l2-subdev-priv.h b/drivers/media/v4l2-core/v4l2-subdev-priv.h
new file mode 100644
index 000000000000..52391d6d8ab7
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-subdev-priv.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * V4L2 sub-device pivate header.
+ *
+ * Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef _V4L2_SUBDEV_PRIV_H_
+#define _V4L2_SUBDEV_PRIV_H_
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd);
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd);
+
+#endif
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 5d27a27cc2f2..25e66bf18f5f 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -8,27 +8,76 @@
* Sakari Ailus <sakari.ailus@iki.fi>
*/
+#include <linux/export.h>
#include <linux/ioctl.h>
+#include <linux/leds.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/types.h>
-#include <linux/videodev2.h>
-#include <linux/export.h>
#include <linux/version.h>
+#include <linux/videodev2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+
+/**
+ * struct v4l2_subdev_stream_config - Used for storing stream configuration.
+ *
+ * @pad: pad number
+ * @stream: stream number
+ * @enabled: has the stream been enabled with v4l2_subdev_enable_streams()
+ * @fmt: &struct v4l2_mbus_framefmt
+ * @crop: &struct v4l2_rect to be used for crop
+ * @compose: &struct v4l2_rect to be used for compose
+ * @interval: frame interval
+ *
+ * This structure stores configuration for a stream.
+ */
+struct v4l2_subdev_stream_config {
+ u32 pad;
+ u32 stream;
+ bool enabled;
+
+ struct v4l2_mbus_framefmt fmt;
+ struct v4l2_rect crop;
+ struct v4l2_rect compose;
+ struct v4l2_fract interval;
+};
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+/*
+ * The Streams API is an experimental feature. To use the Streams API, set
+ * 'v4l2_subdev_enable_streams_api' to 1 below.
+ */
+
+static bool v4l2_subdev_enable_streams_api;
+#endif
+
+/*
+ * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set
+ * of streams.
+ *
+ * Note that V4L2_FRAME_DESC_ENTRY_MAX is related: V4L2_FRAME_DESC_ENTRY_MAX
+ * restricts the total number of streams in a pad, although the stream ID is
+ * not restricted.
+ */
+#define V4L2_SUBDEV_MAX_STREAM_ID 63
+
+#include "v4l2-subdev-priv.h"
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
{
struct v4l2_subdev_state *state;
+ static struct lock_class_key key;
- state = v4l2_subdev_alloc_state(sd);
+ state = __v4l2_subdev_state_alloc(sd, "fh->state->lock", &key);
if (IS_ERR(state))
return PTR_ERR(state);
@@ -39,7 +88,7 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
static void subdev_fh_free(struct v4l2_subdev_fh *fh)
{
- v4l2_subdev_free_state(fh->state);
+ __v4l2_subdev_state_free(fh->state);
fh->state = NULL;
}
@@ -61,9 +110,8 @@ static int subdev_open(struct file *file)
}
v4l2_fh_init(&subdev_fh->vfh, vdev);
- v4l2_fh_add(&subdev_fh->vfh);
- file->private_data = &subdev_fh->vfh;
-#if defined(CONFIG_MEDIA_CONTROLLER)
+ v4l2_fh_add(&subdev_fh->vfh, file);
+
if (sd->v4l2_dev->mdev && sd->entity.graph_obj.mdev->dev) {
struct module *owner;
@@ -74,7 +122,6 @@ static int subdev_open(struct file *file)
}
subdev_fh->owner = owner;
}
-#endif
if (sd->internal_ops && sd->internal_ops->open) {
ret = sd->internal_ops->open(sd, subdev_fh);
@@ -86,7 +133,7 @@ static int subdev_open(struct file *file)
err:
module_put(subdev_fh->owner);
- v4l2_fh_del(&subdev_fh->vfh);
+ v4l2_fh_del(&subdev_fh->vfh, file);
v4l2_fh_exit(&subdev_fh->vfh);
subdev_fh_free(subdev_fh);
kfree(subdev_fh);
@@ -98,17 +145,16 @@ static int subdev_close(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
- struct v4l2_fh *vfh = file->private_data;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
if (sd->internal_ops && sd->internal_ops->close)
sd->internal_ops->close(sd, subdev_fh);
module_put(subdev_fh->owner);
- v4l2_fh_del(vfh);
+ v4l2_fh_del(vfh, file);
v4l2_fh_exit(vfh);
subdev_fh_free(subdev_fh);
kfree(subdev_fh);
- file->private_data = NULL;
return 0;
}
@@ -124,6 +170,23 @@ static int subdev_close(struct file *file)
}
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
+static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led))
+ led_set_brightness(sd->privacy_led,
+ sd->privacy_led->max_brightness);
+#endif
+}
+
+static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led))
+ led_set_brightness(sd->privacy_led, 0);
+#endif
+}
+
static inline int check_which(u32 which)
{
if (which != V4L2_SUBDEV_FORMAT_TRY &&
@@ -148,8 +211,22 @@ static inline int check_pad(struct v4l2_subdev *sd, u32 pad)
return 0;
}
-static int check_state_pads(u32 which, struct v4l2_subdev_state *state)
+static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+ u32 which, u32 pad, u32 stream)
{
+ if (sd->flags & V4L2_SUBDEV_FL_STREAMS) {
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ if (!v4l2_subdev_state_get_format(state, pad, stream))
+ return -EINVAL;
+ return 0;
+#else
+ return -EINVAL;
+#endif
+ }
+
+ if (stream != 0)
+ return -EINVAL;
+
if (which == V4L2_SUBDEV_FORMAT_TRY && (!state || !state->pads))
return -EINVAL;
@@ -164,7 +241,7 @@ static inline int check_format(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(format->which) ? : check_pad(sd, format->pad) ? :
- check_state_pads(format->which, state);
+ check_state(sd, state, format->which, format->pad, format->stream);
}
static int call_get_fmt(struct v4l2_subdev *sd,
@@ -191,7 +268,7 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(code->which) ? : check_pad(sd, code->pad) ? :
- check_state_pads(code->which, state) ? :
+ check_state(sd, state, code->which, code->pad, code->stream) ? :
sd->ops->pad->enum_mbus_code(sd, state, code);
}
@@ -203,33 +280,10 @@ static int call_enum_frame_size(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(fse->which) ? : check_pad(sd, fse->pad) ? :
- check_state_pads(fse->which, state) ? :
+ check_state(sd, state, fse->which, fse->pad, fse->stream) ? :
sd->ops->pad->enum_frame_size(sd, state, fse);
}
-static inline int check_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
-{
- if (!fi)
- return -EINVAL;
-
- return check_pad(sd, fi->pad);
-}
-
-static int call_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
-{
- return check_frame_interval(sd, fi) ? :
- sd->ops->video->g_frame_interval(sd, fi);
-}
-
-static int call_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
-{
- return check_frame_interval(sd, fi) ? :
- sd->ops->video->s_frame_interval(sd, fi);
-}
-
static int call_enum_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_interval_enum *fie)
@@ -238,7 +292,7 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(fie->which) ? : check_pad(sd, fie->pad) ? :
- check_state_pads(fie->which, state) ? :
+ check_state(sd, state, fie->which, fie->pad, fie->stream) ? :
sd->ops->pad->enum_frame_interval(sd, state, fie);
}
@@ -250,7 +304,7 @@ static inline int check_selection(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(sel->which) ? : check_pad(sd, sel->pad) ? :
- check_state_pads(sel->which, state);
+ check_state(sd, state, sel->which, sel->pad, sel->stream);
}
static int call_get_selection(struct v4l2_subdev *sd,
@@ -269,6 +323,74 @@ static int call_set_selection(struct v4l2_subdev *sd,
sd->ops->pad->set_selection(sd, state, sel);
}
+static inline int check_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ if (!fi)
+ return -EINVAL;
+
+ return check_which(fi->which) ? : check_pad(sd, fi->pad) ? :
+ check_state(sd, state, fi->which, fi->pad, fi->stream);
+}
+
+static int call_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ return check_frame_interval(sd, state, fi) ? :
+ sd->ops->pad->get_frame_interval(sd, state, fi);
+}
+
+static int call_set_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ return check_frame_interval(sd, state, fi) ? :
+ sd->ops->pad->set_frame_interval(sd, state, fi);
+}
+
+static int call_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ unsigned int i;
+ int ret;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+ return -EOPNOTSUPP;
+#endif
+
+ memset(fd, 0, sizeof(*fd));
+
+ ret = sd->ops->pad->get_frame_desc(sd, pad, fd);
+ if (ret)
+ return ret;
+
+ dev_dbg(sd->dev, "Frame descriptor on pad %u, type %s\n", pad,
+ fd->type == V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL ? "parallel" :
+ fd->type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2 ? "CSI-2" :
+ "unknown");
+
+ for (i = 0; i < fd->num_entries; i++) {
+ struct v4l2_mbus_frame_desc_entry *entry = &fd->entry[i];
+ char buf[20] = "";
+
+ if (fd->type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2)
+ WARN_ON(snprintf(buf, sizeof(buf),
+ ", vc %u, dt 0x%02x",
+ entry->bus.csi2.vc,
+ entry->bus.csi2.dt) >= sizeof(buf));
+
+ dev_dbg(sd->dev,
+ "\tstream %u, code 0x%04x, length %u, flags 0x%04x%s\n",
+ entry->stream, entry->pixelcode, entry->length,
+ entry->flags, buf);
+ }
+
+ return 0;
+}
+
static inline int check_edid(struct v4l2_subdev *sd,
struct v4l2_subdev_edid *edid)
{
@@ -291,6 +413,36 @@ static int call_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
return check_edid(sd, edid) ? : sd->ops->pad->set_edid(sd, edid);
}
+static int call_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ if (!timings)
+ return -EINVAL;
+
+ return check_pad(sd, pad) ? :
+ sd->ops->pad->s_dv_timings(sd, pad, timings);
+}
+
+static int call_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ if (!timings)
+ return -EINVAL;
+
+ return check_pad(sd, pad) ? :
+ sd->ops->pad->g_dv_timings(sd, pad, timings);
+}
+
+static int call_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ if (!timings)
+ return -EINVAL;
+
+ return check_pad(sd, pad) ? :
+ sd->ops->pad->query_dv_timings(sd, pad, timings);
+}
+
static int call_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
@@ -314,36 +466,107 @@ static int call_enum_dv_timings(struct v4l2_subdev *sd,
static int call_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_config *config)
{
+ memset(config, 0, sizeof(*config));
+
return check_pad(sd, pad) ? :
sd->ops->pad->get_mbus_config(sd, pad, config);
}
-static int call_set_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
- struct v4l2_mbus_config *config)
+static int call_s_stream(struct v4l2_subdev *sd, int enable)
{
- return check_pad(sd, pad) ? :
- sd->ops->pad->get_mbus_config(sd, pad, config);
+ int ret;
+
+ /*
+ * The .s_stream() operation must never be called to start or stop an
+ * already started or stopped subdev. Catch offenders but don't return
+ * an error yet to avoid regressions.
+ */
+ if (WARN_ON(sd->s_stream_enabled == !!enable))
+ return 0;
+
+ ret = sd->ops->video->s_stream(sd, enable);
+
+ if (!enable && ret < 0) {
+ dev_warn(sd->dev, "disabling streaming failed (%d)\n", ret);
+ ret = 0;
+ }
+
+ if (!ret) {
+ sd->s_stream_enabled = enable;
+
+ if (enable)
+ v4l2_subdev_enable_privacy_led(sd);
+ else
+ v4l2_subdev_disable_privacy_led(sd);
+ }
+
+ return ret;
}
+#ifdef CONFIG_MEDIA_CONTROLLER
+/*
+ * Create state-management wrapper for pad ops dealing with subdev state. The
+ * wrapper handles the case where the caller does not provide the called
+ * subdev's state. This should be removed when all the callers are fixed.
+ */
+#define DEFINE_STATE_WRAPPER(f, arg_type) \
+ static int call_##f##_state(struct v4l2_subdev *sd, \
+ struct v4l2_subdev_state *_state, \
+ arg_type *arg) \
+ { \
+ struct v4l2_subdev_state *state = _state; \
+ int ret; \
+ if (!_state) \
+ state = v4l2_subdev_lock_and_get_active_state(sd); \
+ ret = call_##f(sd, state, arg); \
+ if (!_state && state) \
+ v4l2_subdev_unlock_state(state); \
+ return ret; \
+ }
+
+#else /* CONFIG_MEDIA_CONTROLLER */
+
+#define DEFINE_STATE_WRAPPER(f, arg_type) \
+ static int call_##f##_state(struct v4l2_subdev *sd, \
+ struct v4l2_subdev_state *state, \
+ arg_type *arg) \
+ { \
+ return call_##f(sd, state, arg); \
+ }
+
+#endif /* CONFIG_MEDIA_CONTROLLER */
+
+DEFINE_STATE_WRAPPER(get_fmt, struct v4l2_subdev_format);
+DEFINE_STATE_WRAPPER(set_fmt, struct v4l2_subdev_format);
+DEFINE_STATE_WRAPPER(enum_mbus_code, struct v4l2_subdev_mbus_code_enum);
+DEFINE_STATE_WRAPPER(enum_frame_size, struct v4l2_subdev_frame_size_enum);
+DEFINE_STATE_WRAPPER(enum_frame_interval, struct v4l2_subdev_frame_interval_enum);
+DEFINE_STATE_WRAPPER(get_selection, struct v4l2_subdev_selection);
+DEFINE_STATE_WRAPPER(set_selection, struct v4l2_subdev_selection);
+
static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = {
- .get_fmt = call_get_fmt,
- .set_fmt = call_set_fmt,
- .enum_mbus_code = call_enum_mbus_code,
- .enum_frame_size = call_enum_frame_size,
- .enum_frame_interval = call_enum_frame_interval,
- .get_selection = call_get_selection,
- .set_selection = call_set_selection,
+ .get_fmt = call_get_fmt_state,
+ .set_fmt = call_set_fmt_state,
+ .enum_mbus_code = call_enum_mbus_code_state,
+ .enum_frame_size = call_enum_frame_size_state,
+ .enum_frame_interval = call_enum_frame_interval_state,
+ .get_selection = call_get_selection_state,
+ .set_selection = call_set_selection_state,
+ .get_frame_interval = call_get_frame_interval,
+ .set_frame_interval = call_set_frame_interval,
.get_edid = call_get_edid,
.set_edid = call_set_edid,
+ .s_dv_timings = call_s_dv_timings,
+ .g_dv_timings = call_g_dv_timings,
+ .query_dv_timings = call_query_dv_timings,
.dv_timings_cap = call_dv_timings_cap,
.enum_dv_timings = call_enum_dv_timings,
+ .get_frame_desc = call_get_frame_desc,
.get_mbus_config = call_get_mbus_config,
- .set_mbus_config = call_set_mbus_config,
};
static const struct v4l2_subdev_video_ops v4l2_subdev_call_video_wrappers = {
- .g_frame_interval = call_g_frame_interval,
- .s_frame_interval = call_s_frame_interval,
+ .s_stream = call_s_stream,
};
const struct v4l2_subdev_ops v4l2_subdev_call_wrappers = {
@@ -353,22 +576,88 @@ const struct v4l2_subdev_ops v4l2_subdev_call_wrappers = {
EXPORT_SYMBOL(v4l2_subdev_call_wrappers);
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
-static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+
+static struct v4l2_subdev_state *
+subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh,
+ unsigned int cmd, void *arg)
+{
+ u32 which;
+
+ switch (cmd) {
+ default:
+ return NULL;
+ case VIDIOC_SUBDEV_G_FMT:
+ case VIDIOC_SUBDEV_S_FMT:
+ which = ((struct v4l2_subdev_format *)arg)->which;
+ break;
+ case VIDIOC_SUBDEV_G_CROP:
+ case VIDIOC_SUBDEV_S_CROP:
+ which = ((struct v4l2_subdev_crop *)arg)->which;
+ break;
+ case VIDIOC_SUBDEV_ENUM_MBUS_CODE:
+ which = ((struct v4l2_subdev_mbus_code_enum *)arg)->which;
+ break;
+ case VIDIOC_SUBDEV_ENUM_FRAME_SIZE:
+ which = ((struct v4l2_subdev_frame_size_enum *)arg)->which;
+ break;
+ case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL:
+ which = ((struct v4l2_subdev_frame_interval_enum *)arg)->which;
+ break;
+ case VIDIOC_SUBDEV_G_SELECTION:
+ case VIDIOC_SUBDEV_S_SELECTION:
+ which = ((struct v4l2_subdev_selection *)arg)->which;
+ break;
+ case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
+ case VIDIOC_SUBDEV_S_FRAME_INTERVAL: {
+ struct v4l2_subdev_frame_interval *fi = arg;
+
+ if (!(subdev_fh->client_caps &
+ V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH))
+ fi->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ which = fi->which;
+ break;
+ }
+ case VIDIOC_SUBDEV_G_ROUTING:
+ case VIDIOC_SUBDEV_S_ROUTING:
+ which = ((struct v4l2_subdev_routing *)arg)->which;
+ break;
+ }
+
+ return which == V4L2_SUBDEV_FORMAT_TRY ?
+ subdev_fh->state :
+ v4l2_subdev_get_unlocked_active_state(sd);
+}
+
+static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
+ struct v4l2_subdev_state *state)
{
struct video_device *vdev = video_devdata(file);
struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
- struct v4l2_fh *vfh = file->private_data;
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
+ bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS;
+ bool client_supports_streams = subdev_fh->client_caps &
+ V4L2_SUBDEV_CLIENT_CAP_STREAMS;
int rval;
+ /*
+ * If the streams API is not enabled, remove V4L2_SUBDEV_CAP_STREAMS.
+ * Remove this when the API is no longer experimental.
+ */
+ if (!v4l2_subdev_enable_streams_api)
+ streams_subdev = false;
+
switch (cmd) {
case VIDIOC_SUBDEV_QUERYCAP: {
struct v4l2_subdev_capability *cap = arg;
memset(cap->reserved, 0, sizeof(cap->reserved));
cap->version = LINUX_VERSION_CODE;
- cap->capabilities = ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0;
+ cap->capabilities =
+ (ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0) |
+ (streams_subdev ? V4L2_SUBDEV_CAP_STREAMS : 0);
return 0;
}
@@ -431,10 +720,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
case VIDIOC_SUBSCRIBE_EVENT:
- return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+ if (v4l2_subdev_has_op(sd, core, subscribe_event))
+ return v4l2_subdev_call(sd, core, subscribe_event,
+ vfh, arg);
+
+ if ((sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) &&
+ vfh->ctrl_handler)
+ return v4l2_ctrl_subdev_subscribe_event(sd, vfh, arg);
+
+ return -ENOIOCTLCMD;
case VIDIOC_UNSUBSCRIBE_EVENT:
- return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+ if (v4l2_subdev_has_op(sd, core, unsubscribe_event))
+ return v4l2_subdev_call(sd, core, unsubscribe_event,
+ vfh, arg);
+
+ if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)
+ return v4l2_event_subdev_unsubscribe(sd, vfh, arg);
+
+ return -ENOIOCTLCMD;
#ifdef CONFIG_VIDEO_ADV_DEBUG
case VIDIOC_DBG_G_REGISTER:
@@ -482,9 +786,12 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_G_FMT: {
struct v4l2_subdev_format *format = arg;
+ if (!client_supports_streams)
+ format->stream = 0;
+
memset(format->reserved, 0, sizeof(format->reserved));
memset(format->format.reserved, 0, sizeof(format->format.reserved));
- return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->state, format);
+ return v4l2_subdev_call(sd, pad, get_fmt, state, format);
}
case VIDIOC_SUBDEV_S_FMT: {
@@ -493,23 +800,30 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM;
+ if (!client_supports_streams)
+ format->stream = 0;
+
memset(format->reserved, 0, sizeof(format->reserved));
memset(format->format.reserved, 0, sizeof(format->format.reserved));
- return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->state, format);
+ return v4l2_subdev_call(sd, pad, set_fmt, state, format);
}
case VIDIOC_SUBDEV_G_CROP: {
struct v4l2_subdev_crop *crop = arg;
struct v4l2_subdev_selection sel;
+ if (!client_supports_streams)
+ crop->stream = 0;
+
memset(crop->reserved, 0, sizeof(crop->reserved));
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
+ sel.stream = crop->stream;
sel.target = V4L2_SEL_TGT_CROP;
rval = v4l2_subdev_call(
- sd, pad, get_selection, subdev_fh->state, &sel);
+ sd, pad, get_selection, state, &sel);
crop->rect = sel.r;
@@ -523,15 +837,19 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM;
+ if (!client_supports_streams)
+ crop->stream = 0;
+
memset(crop->reserved, 0, sizeof(crop->reserved));
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
+ sel.stream = crop->stream;
sel.target = V4L2_SEL_TGT_CROP;
sel.r = crop->rect;
rval = v4l2_subdev_call(
- sd, pad, set_selection, subdev_fh->state, &sel);
+ sd, pad, set_selection, state, &sel);
crop->rect = sel.r;
@@ -541,50 +859,68 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
struct v4l2_subdev_mbus_code_enum *code = arg;
+ if (!client_supports_streams)
+ code->stream = 0;
+
memset(code->reserved, 0, sizeof(code->reserved));
- return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->state,
+ return v4l2_subdev_call(sd, pad, enum_mbus_code, state,
code);
}
case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
struct v4l2_subdev_frame_size_enum *fse = arg;
+ if (!client_supports_streams)
+ fse->stream = 0;
+
memset(fse->reserved, 0, sizeof(fse->reserved));
- return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->state,
+ return v4l2_subdev_call(sd, pad, enum_frame_size, state,
fse);
}
case VIDIOC_SUBDEV_G_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval *fi = arg;
+ if (!client_supports_streams)
+ fi->stream = 0;
+
memset(fi->reserved, 0, sizeof(fi->reserved));
- return v4l2_subdev_call(sd, video, g_frame_interval, arg);
+ return v4l2_subdev_call(sd, pad, get_frame_interval, state, fi);
}
case VIDIOC_SUBDEV_S_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval *fi = arg;
- if (ro_subdev)
+ if (!client_supports_streams)
+ fi->stream = 0;
+
+ if (fi->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM;
memset(fi->reserved, 0, sizeof(fi->reserved));
- return v4l2_subdev_call(sd, video, s_frame_interval, arg);
+ return v4l2_subdev_call(sd, pad, set_frame_interval, state, fi);
}
case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval_enum *fie = arg;
+ if (!client_supports_streams)
+ fie->stream = 0;
+
memset(fie->reserved, 0, sizeof(fie->reserved));
- return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->state,
+ return v4l2_subdev_call(sd, pad, enum_frame_interval, state,
fie);
}
case VIDIOC_SUBDEV_G_SELECTION: {
struct v4l2_subdev_selection *sel = arg;
+ if (!client_supports_streams)
+ sel->stream = 0;
+
memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call(
- sd, pad, get_selection, subdev_fh->state, sel);
+ sd, pad, get_selection, state, sel);
}
case VIDIOC_SUBDEV_S_SELECTION: {
@@ -593,9 +929,12 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM;
+ if (!client_supports_streams)
+ sel->stream = 0;
+
memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call(
- sd, pad, set_selection, subdev_fh->state, sel);
+ sd, pad, set_selection, state, sel);
}
case VIDIOC_G_EDID: {
@@ -623,16 +962,16 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
}
case VIDIOC_SUBDEV_QUERY_DV_TIMINGS:
- return v4l2_subdev_call(sd, video, query_dv_timings, arg);
+ return v4l2_subdev_call(sd, pad, query_dv_timings, 0, arg);
case VIDIOC_SUBDEV_G_DV_TIMINGS:
- return v4l2_subdev_call(sd, video, g_dv_timings, arg);
+ return v4l2_subdev_call(sd, pad, g_dv_timings, 0, arg);
case VIDIOC_SUBDEV_S_DV_TIMINGS:
if (ro_subdev)
return -EPERM;
- return v4l2_subdev_call(sd, video, s_dv_timings, arg);
+ return v4l2_subdev_call(sd, pad, s_dv_timings, 0, arg);
case VIDIOC_SUBDEV_G_STD:
return v4l2_subdev_call(sd, video, g_std, arg);
@@ -659,6 +998,147 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_QUERYSTD:
return v4l2_subdev_call(sd, video, querystd, arg);
+ case VIDIOC_SUBDEV_G_ROUTING: {
+ struct v4l2_subdev_routing *routing = arg;
+ struct v4l2_subdev_krouting *krouting;
+
+ if (!v4l2_subdev_enable_streams_api)
+ return -ENOIOCTLCMD;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ return -ENOIOCTLCMD;
+
+ memset(routing->reserved, 0, sizeof(routing->reserved));
+
+ krouting = &state->routing;
+
+ memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
+ krouting->routes,
+ min(krouting->num_routes, routing->len_routes) *
+ sizeof(*krouting->routes));
+ routing->num_routes = krouting->num_routes;
+
+ return 0;
+ }
+
+ case VIDIOC_SUBDEV_S_ROUTING: {
+ struct v4l2_subdev_routing *routing = arg;
+ struct v4l2_subdev_route *routes =
+ (struct v4l2_subdev_route *)(uintptr_t)routing->routes;
+ struct v4l2_subdev_krouting krouting = {};
+ unsigned int num_active_routes = 0;
+ unsigned int i;
+
+ if (!v4l2_subdev_enable_streams_api)
+ return -ENOIOCTLCMD;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ return -ENOIOCTLCMD;
+
+ if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
+ return -EPERM;
+
+ if (routing->num_routes > routing->len_routes)
+ return -EINVAL;
+
+ memset(routing->reserved, 0, sizeof(routing->reserved));
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routes[i];
+ const struct media_pad *pads = sd->entity.pads;
+
+ if (route->sink_stream > V4L2_SUBDEV_MAX_STREAM_ID ||
+ route->source_stream > V4L2_SUBDEV_MAX_STREAM_ID)
+ return -EINVAL;
+
+ if (route->sink_pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!(pads[route->sink_pad].flags &
+ MEDIA_PAD_FL_SINK))
+ return -EINVAL;
+
+ if (route->source_pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!(pads[route->source_pad].flags &
+ MEDIA_PAD_FL_SOURCE))
+ return -EINVAL;
+
+ if (route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)
+ num_active_routes++;
+ }
+
+ /*
+ * Drivers that implement routing need to report a frame
+ * descriptor accordingly, with up to one entry per route. Until
+ * the frame descriptors entries get allocated dynamically,
+ * limit the number of active routes to
+ * V4L2_FRAME_DESC_ENTRY_MAX.
+ */
+ if (num_active_routes > V4L2_FRAME_DESC_ENTRY_MAX)
+ return -E2BIG;
+
+ /*
+ * If the driver doesn't support setting routing, just return
+ * the routing table.
+ */
+ if (!v4l2_subdev_has_op(sd, pad, set_routing)) {
+ memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
+ state->routing.routes,
+ min(state->routing.num_routes, routing->len_routes) *
+ sizeof(*state->routing.routes));
+ routing->num_routes = state->routing.num_routes;
+
+ return 0;
+ }
+
+ krouting.num_routes = routing->num_routes;
+ krouting.len_routes = routing->len_routes;
+ krouting.routes = routes;
+
+ rval = v4l2_subdev_call(sd, pad, set_routing, state,
+ routing->which, &krouting);
+ if (rval < 0)
+ return rval;
+
+ memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
+ state->routing.routes,
+ min(state->routing.num_routes, routing->len_routes) *
+ sizeof(*state->routing.routes));
+ routing->num_routes = state->routing.num_routes;
+
+ return 0;
+ }
+
+ case VIDIOC_SUBDEV_G_CLIENT_CAP: {
+ struct v4l2_subdev_client_capability *client_cap = arg;
+
+ client_cap->capabilities = subdev_fh->client_caps;
+
+ return 0;
+ }
+
+ case VIDIOC_SUBDEV_S_CLIENT_CAP: {
+ struct v4l2_subdev_client_capability *client_cap = arg;
+
+ /*
+ * Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not
+ * enabled. Remove this when streams API is no longer
+ * experimental.
+ */
+ if (!v4l2_subdev_enable_streams_api)
+ client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS;
+
+ /* Filter out unsupported capabilities */
+ client_cap->capabilities &= (V4L2_SUBDEV_CLIENT_CAP_STREAMS |
+ V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH);
+
+ subdev_fh->client_caps = client_cap->capabilities;
+
+ return 0;
+ }
+
default:
return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
}
@@ -674,8 +1154,24 @@ static long subdev_do_ioctl_lock(struct file *file, unsigned int cmd, void *arg)
if (lock && mutex_lock_interruptible(lock))
return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = subdev_do_ioctl(file, cmd, arg);
+
+ if (video_is_registered(vdev)) {
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file_to_v4l2_fh(file);
+ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+ struct v4l2_subdev_state *state;
+
+ state = subdev_ioctl_get_state(sd, subdev_fh, cmd, arg);
+
+ if (state)
+ v4l2_subdev_lock_state(state);
+
+ ret = subdev_do_ioctl(file, cmd, arg, state);
+
+ if (state)
+ v4l2_subdev_unlock_state(state);
+ }
+
if (lock)
mutex_unlock(lock);
return ret;
@@ -718,7 +1214,7 @@ static __poll_t subdev_poll(struct file *file, poll_table *wait)
{
struct video_device *vdev = video_devdata(file);
struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
- struct v4l2_fh *fh = file->private_data;
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
return EPOLLERR;
@@ -758,7 +1254,7 @@ int v4l2_subdev_get_fwnode_pad_1_to_1(struct media_entity *entity,
fwnode = fwnode_graph_get_port_parent(endpoint->local_fwnode);
fwnode_handle_put(fwnode);
- if (dev_fwnode(sd->dev) == fwnode)
+ if (device_match_fwnode(sd->dev, fwnode))
return endpoint->port;
return -ENXIO;
@@ -823,54 +1319,289 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
static int
-v4l2_subdev_link_validate_get_format(struct media_pad *pad,
- struct v4l2_subdev_format *fmt)
+v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream,
+ struct v4l2_subdev_format *fmt,
+ bool states_locked)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt->pad = pad->index;
+ fmt->stream = stream;
+
+ if (states_locked)
+ state = v4l2_subdev_get_locked_active_state(sd);
+ else
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, state, fmt);
+
+ if (!states_locked && state)
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+
+static void __v4l2_link_validate_get_streams(struct media_pad *pad,
+ u64 *streams_mask,
+ bool states_locked)
{
- if (is_media_entity_v4l2_subdev(pad->entity)) {
- struct v4l2_subdev *sd =
- media_entity_to_v4l2_subdev(pad->entity);
+ struct v4l2_subdev_route *route;
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev *subdev;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+ *streams_mask = 0;
+
+ if (states_locked)
+ state = v4l2_subdev_get_locked_active_state(subdev);
+ else
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
+ if (WARN_ON(!state))
+ return;
+
+ for_each_active_route(&state->routing, route) {
+ u32 route_pad;
+ u32 route_stream;
+
+ if (pad->flags & MEDIA_PAD_FL_SOURCE) {
+ route_pad = route->source_pad;
+ route_stream = route->source_stream;
+ } else {
+ route_pad = route->sink_pad;
+ route_stream = route->sink_stream;
+ }
+
+ if (route_pad != pad->index)
+ continue;
- fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt->pad = pad->index;
- return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+ *streams_mask |= BIT_ULL(route_stream);
}
- WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L,
- "Driver bug! Wrong media entity type 0x%08x, entity %s\n",
- pad->entity->function, pad->entity->name);
+ if (!states_locked)
+ v4l2_subdev_unlock_state(state);
+}
- return -EINVAL;
+#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
+
+static void v4l2_link_validate_get_streams(struct media_pad *pad,
+ u64 *streams_mask,
+ bool states_locked)
+{
+ struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+ if (!(subdev->flags & V4L2_SUBDEV_FL_STREAMS)) {
+ /* Non-streams subdevs have an implicit stream 0 */
+ *streams_mask = BIT_ULL(0);
+ return;
+ }
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ __v4l2_link_validate_get_streams(pad, streams_mask, states_locked);
+#else
+ /* This shouldn't happen */
+ *streams_mask = 0;
+#endif
+}
+
+static int v4l2_subdev_link_validate_locked(struct media_link *link, bool states_locked)
+{
+ struct v4l2_subdev *sink_subdev =
+ media_entity_to_v4l2_subdev(link->sink->entity);
+ struct device *dev = sink_subdev->entity.graph_obj.mdev->dev;
+ u64 source_streams_mask;
+ u64 sink_streams_mask;
+ u64 dangling_sink_streams;
+ u32 stream;
+ int ret;
+
+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\":%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index);
+
+ v4l2_link_validate_get_streams(link->source, &source_streams_mask, states_locked);
+ v4l2_link_validate_get_streams(link->sink, &sink_streams_mask, states_locked);
+
+ /*
+ * It is ok to have more source streams than sink streams as extra
+ * source streams can just be ignored by the receiver, but having extra
+ * sink streams is an error as streams must have a source.
+ */
+ dangling_sink_streams = (source_streams_mask ^ sink_streams_mask) &
+ sink_streams_mask;
+ if (dangling_sink_streams) {
+ dev_err(dev, "Dangling sink streams: mask %#llx\n",
+ dangling_sink_streams);
+ return -EINVAL;
+ }
+
+ /* Validate source and sink stream formats */
+
+ for (stream = 0; stream < sizeof(sink_streams_mask) * 8; ++stream) {
+ struct v4l2_subdev_format sink_fmt, source_fmt;
+
+ if (!(sink_streams_mask & BIT_ULL(stream)))
+ continue;
+
+ dev_dbg(dev, "validating stream \"%s\":%u:%u -> \"%s\":%u:%u\n",
+ link->source->entity->name, link->source->index, stream,
+ link->sink->entity->name, link->sink->index, stream);
+
+ ret = v4l2_subdev_link_validate_get_format(link->source, stream,
+ &source_fmt, states_locked);
+ if (ret < 0) {
+ dev_dbg(dev,
+ "Failed to get format for \"%s\":%u:%u (but that's ok)\n",
+ link->source->entity->name, link->source->index,
+ stream);
+ continue;
+ }
+
+ ret = v4l2_subdev_link_validate_get_format(link->sink, stream,
+ &sink_fmt, states_locked);
+ if (ret < 0) {
+ dev_dbg(dev,
+ "Failed to get format for \"%s\":%u:%u (but that's ok)\n",
+ link->sink->entity->name, link->sink->index,
+ stream);
+ continue;
+ }
+
+ /* TODO: add stream number to link_validate() */
+ ret = v4l2_subdev_call(sink_subdev, pad, link_validate, link,
+ &source_fmt, &sink_fmt);
+ if (!ret)
+ continue;
+
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ ret = v4l2_subdev_link_validate_default(sink_subdev, link,
+ &source_fmt, &sink_fmt);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
int v4l2_subdev_link_validate(struct media_link *link)
{
- struct v4l2_subdev *sink;
- struct v4l2_subdev_format sink_fmt, source_fmt;
- int rval;
+ struct v4l2_subdev *source_sd, *sink_sd;
+ struct v4l2_subdev_state *source_state, *sink_state;
+ bool states_locked;
+ int ret;
- rval = v4l2_subdev_link_validate_get_format(
- link->source, &source_fmt);
- if (rval < 0)
- return 0;
+ /*
+ * Links are validated in the context of the sink entity. Usage of this
+ * helper on a sink that is not a subdev is a clear driver bug.
+ */
+ if (WARN_ON_ONCE(!is_media_entity_v4l2_subdev(link->sink->entity)))
+ return -EINVAL;
- rval = v4l2_subdev_link_validate_get_format(
- link->sink, &sink_fmt);
- if (rval < 0)
- return 0;
+ /*
+ * If the source is a video device, delegate link validation to it. This
+ * allows usage of this helper for subdev connected to a video output
+ * device, provided that the driver implement the video output device's
+ * .link_validate() operation.
+ */
+ if (is_media_entity_v4l2_video_device(link->source->entity)) {
+ struct media_entity *source = link->source->entity;
+
+ if (!source->ops || !source->ops->link_validate) {
+ /*
+ * Many existing drivers do not implement the required
+ * .link_validate() operation for their video devices.
+ * Print a warning to get the drivers fixed, and return
+ * 0 to avoid breaking userspace. This should
+ * eventually be turned into a WARN_ON() when all
+ * drivers will have been fixed.
+ */
+ pr_warn_once("video device '%s' does not implement .link_validate(), driver bug!\n",
+ source->name);
+ return 0;
+ }
- sink = media_entity_to_v4l2_subdev(link->sink->entity);
+ /*
+ * Avoid infinite loops in case a video device incorrectly uses
+ * this helper function as its .link_validate() handler.
+ */
+ if (WARN_ON(source->ops->link_validate == v4l2_subdev_link_validate))
+ return -EINVAL;
- rval = v4l2_subdev_call(sink, pad, link_validate, link,
- &source_fmt, &sink_fmt);
- if (rval != -ENOIOCTLCMD)
- return rval;
+ return source->ops->link_validate(link);
+ }
- return v4l2_subdev_link_validate_default(
- sink, link, &source_fmt, &sink_fmt);
+ /*
+ * If the source is still not a subdev, usage of this helper is a clear
+ * driver bug.
+ */
+ if (WARN_ON(!is_media_entity_v4l2_subdev(link->source->entity)))
+ return -EINVAL;
+
+ sink_sd = media_entity_to_v4l2_subdev(link->sink->entity);
+ source_sd = media_entity_to_v4l2_subdev(link->source->entity);
+
+ sink_state = v4l2_subdev_get_unlocked_active_state(sink_sd);
+ source_state = v4l2_subdev_get_unlocked_active_state(source_sd);
+
+ states_locked = sink_state && source_state;
+
+ if (states_locked)
+ v4l2_subdev_lock_states(sink_state, source_state);
+
+ ret = v4l2_subdev_link_validate_locked(link, states_locked);
+
+ if (states_locked)
+ v4l2_subdev_unlock_states(sink_state, source_state);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
-struct v4l2_subdev_state *v4l2_subdev_alloc_state(struct v4l2_subdev *sd)
+bool v4l2_subdev_has_pad_interdep(struct media_entity *entity,
+ unsigned int pad0, unsigned int pad1)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct v4l2_subdev_krouting *routing;
+ struct v4l2_subdev_state *state;
+ unsigned int i;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ routing = &state->routing;
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ if ((route->sink_pad == pad0 && route->source_pad == pad1) ||
+ (route->source_pad == pad0 && route->sink_pad == pad1)) {
+ v4l2_subdev_unlock_state(state);
+ return true;
+ }
+ }
+
+ v4l2_subdev_unlock_state(state);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_has_pad_interdep);
+
+struct v4l2_subdev_state *
+__v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name,
+ struct lock_class_key *lock_key)
{
struct v4l2_subdev_state *state;
int ret;
@@ -879,19 +1610,36 @@ struct v4l2_subdev_state *v4l2_subdev_alloc_state(struct v4l2_subdev *sd)
if (!state)
return ERR_PTR(-ENOMEM);
- if (sd->entity.num_pads) {
- state->pads = kvmalloc_array(sd->entity.num_pads,
- sizeof(*state->pads),
- GFP_KERNEL | __GFP_ZERO);
+ __mutex_init(&state->_lock, lock_name, lock_key);
+ if (sd->state_lock)
+ state->lock = sd->state_lock;
+ else
+ state->lock = &state->_lock;
+
+ state->sd = sd;
+
+ /* Drivers that support streams do not need the legacy pad config */
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS) && sd->entity.num_pads) {
+ state->pads = kvcalloc(sd->entity.num_pads,
+ sizeof(*state->pads), GFP_KERNEL);
if (!state->pads) {
ret = -ENOMEM;
goto err;
}
}
- ret = v4l2_subdev_call(sd, pad, init_cfg, state);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- goto err;
+ if (sd->internal_ops && sd->internal_ops->init_state) {
+ /*
+ * There can be no race at this point, but we lock the state
+ * anyway to satisfy lockdep checks.
+ */
+ v4l2_subdev_lock_state(state);
+ ret = sd->internal_ops->init_state(sd, state);
+ v4l2_subdev_unlock_state(state);
+
+ if (ret)
+ goto err;
+ }
return state;
@@ -903,17 +1651,903 @@ err:
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(v4l2_subdev_alloc_state);
+EXPORT_SYMBOL_GPL(__v4l2_subdev_state_alloc);
-void v4l2_subdev_free_state(struct v4l2_subdev_state *state)
+void __v4l2_subdev_state_free(struct v4l2_subdev_state *state)
{
if (!state)
return;
+ mutex_destroy(&state->_lock);
+
+ kfree(state->routing.routes);
+ kvfree(state->stream_configs.configs);
kvfree(state->pads);
kfree(state);
}
-EXPORT_SYMBOL_GPL(v4l2_subdev_free_state);
+EXPORT_SYMBOL_GPL(__v4l2_subdev_state_free);
+
+int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name,
+ struct lock_class_key *key)
+{
+ struct v4l2_subdev_state *state;
+ struct device *dev = sd->dev;
+ bool has_disable_streams;
+ bool has_enable_streams;
+ bool has_s_stream;
+
+ /* Check that the subdevice implements the required features */
+
+ has_s_stream = v4l2_subdev_has_op(sd, video, s_stream);
+ has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams);
+ has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams);
+
+ if (has_enable_streams != has_disable_streams) {
+ dev_err(dev,
+ "subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n",
+ sd->name);
+ return -EINVAL;
+ }
+
+ if (sd->flags & V4L2_SUBDEV_FL_STREAMS) {
+ if (has_s_stream && !has_enable_streams) {
+ dev_err(dev,
+ "subdev '%s' must implement .enable/disable_streams()\n",
+ sd->name);
+
+ return -EINVAL;
+ }
+ }
+
+ if (sd->ctrl_handler)
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+
+ state = __v4l2_subdev_state_alloc(sd, name, key);
+ if (IS_ERR(state))
+ return PTR_ERR(state);
+
+ sd->active_state = state;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_init_finalize);
+
+void v4l2_subdev_cleanup(struct v4l2_subdev *sd)
+{
+ struct v4l2_async_subdev_endpoint *ase, *ase_tmp;
+
+ __v4l2_subdev_state_free(sd->active_state);
+ sd->active_state = NULL;
+
+ /* Uninitialised sub-device, bail out here. */
+ if (!sd->async_subdev_endpoint_list.next)
+ return;
+
+ list_for_each_entry_safe(ase, ase_tmp, &sd->async_subdev_endpoint_list,
+ async_subdev_endpoint_entry) {
+ list_del(&ase->async_subdev_endpoint_entry);
+
+ kfree(ase);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup);
+
+struct v4l2_mbus_framefmt *
+__v4l2_subdev_state_get_format(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ if (WARN_ON_ONCE(!state))
+ return NULL;
+
+ if (state->pads) {
+ if (stream)
+ return NULL;
+
+ if (pad >= state->sd->entity.num_pads)
+ return NULL;
+
+ return &state->pads[pad].format;
+ }
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return &stream_configs->configs[i].fmt;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_format);
+
+struct v4l2_rect *
+__v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad,
+ u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ if (WARN_ON_ONCE(!state))
+ return NULL;
+
+ if (state->pads) {
+ if (stream)
+ return NULL;
+
+ if (pad >= state->sd->entity.num_pads)
+ return NULL;
+
+ return &state->pads[pad].crop;
+ }
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return &stream_configs->configs[i].crop;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_crop);
+
+struct v4l2_rect *
+__v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ if (WARN_ON_ONCE(!state))
+ return NULL;
+
+ if (state->pads) {
+ if (stream)
+ return NULL;
+
+ if (pad >= state->sd->entity.num_pads)
+ return NULL;
+
+ return &state->pads[pad].compose;
+ }
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return &stream_configs->configs[i].compose;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_compose);
+
+struct v4l2_fract *
+__v4l2_subdev_state_get_interval(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ if (WARN_ON(!state))
+ return NULL;
+
+ lockdep_assert_held(state->lock);
+
+ if (state->pads) {
+ if (stream)
+ return NULL;
+
+ if (pad >= state->sd->entity.num_pads)
+ return NULL;
+
+ return &state->pads[pad].interval;
+ }
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return &stream_configs->configs[i].interval;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_state_get_interval);
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+
+static int
+v4l2_subdev_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs,
+ const struct v4l2_subdev_krouting *routing)
+{
+ struct v4l2_subdev_stream_configs new_configs = { 0 };
+ struct v4l2_subdev_route *route;
+ u32 idx;
+
+ /* Count number of formats needed */
+ for_each_active_route(routing, route) {
+ /*
+ * Each route needs a format on both ends of the route.
+ */
+ new_configs.num_configs += 2;
+ }
+
+ if (new_configs.num_configs) {
+ new_configs.configs = kvcalloc(new_configs.num_configs,
+ sizeof(*new_configs.configs),
+ GFP_KERNEL);
+
+ if (!new_configs.configs)
+ return -ENOMEM;
+ }
+
+ /*
+ * Fill in the 'pad' and stream' value for each item in the array from
+ * the routing table
+ */
+ idx = 0;
+
+ for_each_active_route(routing, route) {
+ new_configs.configs[idx].pad = route->sink_pad;
+ new_configs.configs[idx].stream = route->sink_stream;
+
+ idx++;
+
+ new_configs.configs[idx].pad = route->source_pad;
+ new_configs.configs[idx].stream = route->source_stream;
+
+ idx++;
+ }
+
+ kvfree(stream_configs->configs);
+ *stream_configs = new_configs;
+
+ return 0;
+}
+
+int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ format->format = *fmt;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt);
+
+int v4l2_subdev_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct v4l2_fract *interval;
+
+ interval = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream);
+ if (!interval)
+ return -EINVAL;
+
+ fi->interval = *interval;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_frame_interval);
+
+int v4l2_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ const struct v4l2_subdev_krouting *routing)
+{
+ struct v4l2_subdev_krouting *dst = &state->routing;
+ const struct v4l2_subdev_krouting *src = routing;
+ struct v4l2_subdev_krouting new_routing = { 0 };
+ size_t bytes;
+ int r;
+
+ if (unlikely(check_mul_overflow((size_t)src->num_routes,
+ sizeof(*src->routes), &bytes)))
+ return -EOVERFLOW;
+
+ lockdep_assert_held(state->lock);
+
+ if (src->num_routes > 0) {
+ new_routing.routes = kmemdup(src->routes, bytes, GFP_KERNEL);
+ if (!new_routing.routes)
+ return -ENOMEM;
+ }
+
+ new_routing.num_routes = src->num_routes;
+
+ r = v4l2_subdev_init_stream_configs(&state->stream_configs,
+ &new_routing);
+ if (r) {
+ kfree(new_routing.routes);
+ return r;
+ }
+
+ kfree(dst->routes);
+ *dst = new_routing;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing);
+
+struct v4l2_subdev_route *
+__v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing,
+ struct v4l2_subdev_route *route)
+{
+ if (route)
+ ++route;
+ else
+ route = &routing->routes[0];
+
+ for (; route < routing->routes + routing->num_routes; ++route) {
+ if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ return route;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route);
+
+int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ const struct v4l2_subdev_krouting *routing,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_subdev_set_routing(sd, state, routing);
+ if (ret)
+ return ret;
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i)
+ stream_configs->configs[i].fmt = *fmt;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt);
+
+int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing,
+ u32 pad, u32 stream, u32 *other_pad,
+ u32 *other_stream)
+{
+ unsigned int i;
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (route->source_pad == pad &&
+ route->source_stream == stream) {
+ if (other_pad)
+ *other_pad = route->sink_pad;
+ if (other_stream)
+ *other_stream = route->sink_stream;
+ return 0;
+ }
+
+ if (route->sink_pad == pad && route->sink_stream == stream) {
+ if (other_pad)
+ *other_pad = route->source_pad;
+ if (other_stream)
+ *other_stream = route->source_stream;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_routing_find_opposite_end);
+
+struct v4l2_mbus_framefmt *
+v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state,
+ u32 pad, u32 stream)
+{
+ u32 other_pad, other_stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ pad, stream,
+ &other_pad, &other_stream);
+ if (ret)
+ return NULL;
+
+ return v4l2_subdev_state_get_format(state, other_pad, other_stream);
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format);
+
+u64 v4l2_subdev_state_xlate_streams(const struct v4l2_subdev_state *state,
+ u32 pad0, u32 pad1, u64 *streams)
+{
+ const struct v4l2_subdev_krouting *routing = &state->routing;
+ struct v4l2_subdev_route *route;
+ u64 streams0 = 0;
+ u64 streams1 = 0;
+
+ for_each_active_route(routing, route) {
+ if (route->sink_pad == pad0 && route->source_pad == pad1 &&
+ (*streams & BIT_ULL(route->sink_stream))) {
+ streams0 |= BIT_ULL(route->sink_stream);
+ streams1 |= BIT_ULL(route->source_stream);
+ }
+ if (route->source_pad == pad0 && route->sink_pad == pad1 &&
+ (*streams & BIT_ULL(route->source_stream))) {
+ streams0 |= BIT_ULL(route->source_stream);
+ streams1 |= BIT_ULL(route->sink_stream);
+ }
+ }
+
+ *streams = streams0;
+ return streams1;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_xlate_streams);
+
+int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_krouting *routing,
+ enum v4l2_subdev_routing_restriction disallow)
+{
+ u32 *remote_pads = NULL;
+ unsigned int i, j;
+ int ret = -EINVAL;
+
+ if (disallow & (V4L2_SUBDEV_ROUTING_NO_STREAM_MIX |
+ V4L2_SUBDEV_ROUTING_NO_MULTIPLEXING)) {
+ remote_pads = kcalloc(sd->entity.num_pads, sizeof(*remote_pads),
+ GFP_KERNEL);
+ if (!remote_pads)
+ return -ENOMEM;
+
+ for (i = 0; i < sd->entity.num_pads; ++i)
+ remote_pads[i] = U32_MAX;
+ }
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+
+ /* Validate the sink and source pad numbers. */
+ if (route->sink_pad >= sd->entity.num_pads ||
+ !(sd->entity.pads[route->sink_pad].flags & MEDIA_PAD_FL_SINK)) {
+ dev_dbg(sd->dev, "route %u sink (%u) is not a sink pad\n",
+ i, route->sink_pad);
+ goto out;
+ }
+
+ if (route->source_pad >= sd->entity.num_pads ||
+ !(sd->entity.pads[route->source_pad].flags & MEDIA_PAD_FL_SOURCE)) {
+ dev_dbg(sd->dev, "route %u source (%u) is not a source pad\n",
+ i, route->source_pad);
+ goto out;
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX: all streams from a
+ * sink pad must be routed to a single source pad.
+ */
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX) {
+ if (remote_pads[route->sink_pad] != U32_MAX &&
+ remote_pads[route->sink_pad] != route->source_pad) {
+ dev_dbg(sd->dev,
+ "route %u attempts to mix %s streams\n",
+ i, "sink");
+ goto out;
+ }
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_SOURCE_STREAM_MIX: all streams on a
+ * source pad must originate from a single sink pad.
+ */
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_SOURCE_STREAM_MIX) {
+ if (remote_pads[route->source_pad] != U32_MAX &&
+ remote_pads[route->source_pad] != route->sink_pad) {
+ dev_dbg(sd->dev,
+ "route %u attempts to mix %s streams\n",
+ i, "source");
+ goto out;
+ }
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_SINK_MULTIPLEXING: Pads on the sink
+ * side can not do stream multiplexing, i.e. there can be only
+ * a single stream in a sink pad.
+ */
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_SINK_MULTIPLEXING) {
+ if (remote_pads[route->sink_pad] != U32_MAX) {
+ dev_dbg(sd->dev,
+ "route %u attempts to multiplex on %s pad %u\n",
+ i, "sink", route->sink_pad);
+ goto out;
+ }
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING: Pads on the
+ * source side can not do stream multiplexing, i.e. there can
+ * be only a single stream in a source pad.
+ */
+ if (disallow & V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING) {
+ if (remote_pads[route->source_pad] != U32_MAX) {
+ dev_dbg(sd->dev,
+ "route %u attempts to multiplex on %s pad %u\n",
+ i, "source", route->source_pad);
+ goto out;
+ }
+ }
+
+ if (remote_pads) {
+ remote_pads[route->sink_pad] = route->source_pad;
+ remote_pads[route->source_pad] = route->sink_pad;
+ }
+
+ for (j = i + 1; j < routing->num_routes; ++j) {
+ const struct v4l2_subdev_route *r = &routing->routes[j];
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_1_TO_N: No two routes can
+ * originate from the same (sink) stream.
+ */
+ if ((disallow & V4L2_SUBDEV_ROUTING_NO_1_TO_N) &&
+ route->sink_pad == r->sink_pad &&
+ route->sink_stream == r->sink_stream) {
+ dev_dbg(sd->dev,
+ "routes %u and %u originate from same sink (%u/%u)\n",
+ i, j, route->sink_pad,
+ route->sink_stream);
+ goto out;
+ }
+
+ /*
+ * V4L2_SUBDEV_ROUTING_NO_N_TO_1: No two routes can end
+ * at the same (source) stream.
+ */
+ if ((disallow & V4L2_SUBDEV_ROUTING_NO_N_TO_1) &&
+ route->source_pad == r->source_pad &&
+ route->source_stream == r->source_stream) {
+ dev_dbg(sd->dev,
+ "routes %u and %u end at same source (%u/%u)\n",
+ i, j, route->source_pad,
+ route->source_stream);
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+
+out:
+ kfree(remote_pads);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate);
+
+static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask,
+ u64 *found_streams,
+ u64 *enabled_streams)
+{
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) {
+ *found_streams = BIT_ULL(0);
+ *enabled_streams =
+ (sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0;
+ dev_dbg(sd->dev,
+ "collect_streams: sub-device \"%s\" does not support streams\n",
+ sd->entity.name);
+ return;
+ }
+
+ *found_streams = 0;
+ *enabled_streams = 0;
+
+ for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) {
+ const struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream)))
+ continue;
+
+ *found_streams |= BIT_ULL(cfg->stream);
+ if (cfg->enabled)
+ *enabled_streams |= BIT_ULL(cfg->stream);
+ }
+
+ dev_dbg(sd->dev,
+ "collect_streams: \"%s\":%u: found %#llx enabled %#llx\n",
+ sd->entity.name, pad, *found_streams, *enabled_streams);
+}
+
+static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask,
+ bool enabled)
+{
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) {
+ if (enabled)
+ sd->enabled_pads |= BIT_ULL(pad);
+ else
+ sd->enabled_pads &= ~BIT_ULL(pad);
+ return;
+ }
+
+ for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream)))
+ cfg->enabled = enabled;
+ }
+}
+
+int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ struct v4l2_subdev_state *state;
+ bool already_streaming;
+ u64 enabled_streams;
+ u64 found_streams;
+ bool use_s_stream;
+ int ret;
+
+ dev_dbg(dev, "enable streams \"%s\":%u/%#llx\n", sd->entity.name, pad,
+ streams_mask);
+
+ /* A few basic sanity checks first. */
+ if (pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+ return -EOPNOTSUPP;
+
+ /*
+ * We use a 64-bit bitmask for tracking enabled pads, so only subdevices
+ * with 64 pads or less can be supported.
+ */
+ if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE)
+ return -EOPNOTSUPP;
+
+ if (!streams_mask)
+ return 0;
+
+ /* Fallback on .s_stream() if .enable_streams() isn't available. */
+ use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams);
+
+ if (!use_s_stream)
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ else
+ state = NULL;
+
+ /*
+ * Verify that the requested streams exist and that they are not
+ * already enabled.
+ */
+
+ v4l2_subdev_collect_streams(sd, state, pad, streams_mask,
+ &found_streams, &enabled_streams);
+
+ if (found_streams != streams_mask) {
+ dev_dbg(dev, "streams 0x%llx not found on %s:%u\n",
+ streams_mask & ~found_streams, sd->entity.name, pad);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (enabled_streams) {
+ dev_dbg(dev, "streams 0x%llx already enabled on %s:%u\n",
+ enabled_streams, sd->entity.name, pad);
+ ret = -EALREADY;
+ goto done;
+ }
+
+ already_streaming = v4l2_subdev_is_streaming(sd);
+
+ if (!use_s_stream) {
+ /* Call the .enable_streams() operation. */
+ ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
+ streams_mask);
+ } else {
+ /* Start streaming when the first pad is enabled. */
+ if (!already_streaming)
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ else
+ ret = 0;
+ }
+
+ if (ret) {
+ dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad,
+ streams_mask, ret);
+ goto done;
+ }
+
+ /* Mark the streams as enabled. */
+ v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true);
+
+ /*
+ * TODO: When all the drivers have been changed to use
+ * v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams(),
+ * instead of calling .s_stream() operation directly, we can remove
+ * the privacy LED handling from call_s_stream() and do it here
+ * for all cases.
+ */
+ if (!use_s_stream && !already_streaming)
+ v4l2_subdev_enable_privacy_led(sd);
+
+done:
+ if (!use_s_stream)
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams);
+
+int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ struct v4l2_subdev_state *state;
+ u64 enabled_streams;
+ u64 found_streams;
+ bool use_s_stream;
+ int ret;
+
+ dev_dbg(dev, "disable streams \"%s\":%u/%#llx\n", sd->entity.name, pad,
+ streams_mask);
+
+ /* A few basic sanity checks first. */
+ if (pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+ return -EOPNOTSUPP;
+
+ /*
+ * We use a 64-bit bitmask for tracking enabled pads, so only subdevices
+ * with 64 pads or less can be supported.
+ */
+ if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE)
+ return -EOPNOTSUPP;
+
+ if (!streams_mask)
+ return 0;
+
+ /* Fallback on .s_stream() if .disable_streams() isn't available. */
+ use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams);
+
+ if (!use_s_stream)
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ else
+ state = NULL;
+
+ /*
+ * Verify that the requested streams exist and that they are not
+ * already disabled.
+ */
+
+ v4l2_subdev_collect_streams(sd, state, pad, streams_mask,
+ &found_streams, &enabled_streams);
+
+ if (found_streams != streams_mask) {
+ dev_dbg(dev, "streams 0x%llx not found on %s:%u\n",
+ streams_mask & ~found_streams, sd->entity.name, pad);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (enabled_streams != streams_mask) {
+ dev_dbg(dev, "streams 0x%llx already disabled on %s:%u\n",
+ streams_mask & ~enabled_streams, sd->entity.name, pad);
+ ret = -EALREADY;
+ goto done;
+ }
+
+ if (!use_s_stream) {
+ /* Call the .disable_streams() operation. */
+ ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad,
+ streams_mask);
+ } else {
+ /* Stop streaming when the last streams are disabled. */
+
+ if (!(sd->enabled_pads & ~BIT_ULL(pad)))
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ else
+ ret = 0;
+ }
+
+ if (ret) {
+ dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad,
+ streams_mask, ret);
+ goto done;
+ }
+
+ v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false);
+
+done:
+ if (!use_s_stream) {
+ if (!v4l2_subdev_is_streaming(sd))
+ v4l2_subdev_disable_privacy_led(sd);
+
+ v4l2_subdev_unlock_state(state);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_disable_streams);
+
+int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *route;
+ struct media_pad *pad;
+ u64 source_mask = 0;
+ int pad_index = -1;
+
+ /*
+ * Find the source pad. This helper is meant for subdevs that have a
+ * single source pad, so failures shouldn't happen, but catch them
+ * loudly nonetheless as they indicate a driver bug.
+ */
+ media_entity_for_each_pad(&sd->entity, pad) {
+ if (pad->flags & MEDIA_PAD_FL_SOURCE) {
+ pad_index = pad->index;
+ break;
+ }
+ }
+
+ if (WARN_ON(pad_index == -1))
+ return -EINVAL;
+
+ if (sd->flags & V4L2_SUBDEV_FL_STREAMS) {
+ /*
+ * As there's a single source pad, just collect all the source
+ * streams.
+ */
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ for_each_active_route(&state->routing, route)
+ source_mask |= BIT_ULL(route->source_stream);
+
+ v4l2_subdev_unlock_state(state);
+ } else {
+ /*
+ * For non-streams subdevices, there's a single implicit stream
+ * per pad.
+ */
+ source_mask = BIT_ULL(0);
+ }
+
+ if (enable)
+ return v4l2_subdev_enable_streams(sd, pad_index, source_mask);
+ else
+ return v4l2_subdev_disable_streams(sd, pad_index, source_mask);
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_s_stream_helper);
+
+#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */
@@ -928,6 +2562,8 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
sd->grp_id = 0;
sd->dev_priv = NULL;
sd->host_priv = NULL;
+ sd->privacy_led = NULL;
+ INIT_LIST_HEAD(&sd->async_subdev_endpoint_list);
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.name = sd->name;
sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV;
@@ -943,3 +2579,61 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd,
v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev);
}
EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event);
+
+bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd)
+{
+ struct v4l2_subdev_state *state;
+
+ if (!v4l2_subdev_has_op(sd, pad, enable_streams))
+ return sd->s_stream_enabled;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
+ return !!sd->enabled_pads;
+
+ state = v4l2_subdev_get_locked_active_state(sd);
+
+ for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) {
+ const struct v4l2_subdev_stream_config *cfg;
+
+ cfg = &state->stream_configs.configs[i];
+
+ if (cfg->enabled)
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_is_streaming);
+
+int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ sd->privacy_led = led_get(sd->dev, "privacy");
+ if (IS_ERR(sd->privacy_led) && PTR_ERR(sd->privacy_led) != -ENOENT)
+ return dev_err_probe(sd->dev, PTR_ERR(sd->privacy_led),
+ "getting privacy LED\n");
+
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ mutex_lock(&sd->privacy_led->led_access);
+ led_sysfs_disable(sd->privacy_led);
+ led_trigger_remove(sd->privacy_led);
+ led_set_brightness(sd->privacy_led, 0);
+ mutex_unlock(&sd->privacy_led->led_access);
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_privacy_led);
+
+void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd)
+{
+#if IS_REACHABLE(CONFIG_LEDS_CLASS)
+ if (!IS_ERR_OR_NULL(sd->privacy_led)) {
+ mutex_lock(&sd->privacy_led->led_access);
+ led_sysfs_enable(sd->privacy_led);
+ mutex_unlock(&sd->privacy_led->led_access);
+ led_put(sd->privacy_led);
+ }
+#endif
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_put_privacy_led);
diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c
deleted file mode 100644
index 606a271bdd2d..000000000000
--- a/drivers/media/v4l2-core/videobuf-core.c
+++ /dev/null
@@ -1,1198 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * generic helper functions for handling video4linux capture buffers
- *
- * (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
- *
- * Highly based on video-buf written originally by:
- * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
- * (c) 2006 Mauro Carvalho Chehab, <mchehab@kernel.org>
- * (c) 2006 Ted Walther and John Sokol
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-
-#include <media/videobuf-core.h>
-#include <media/v4l2-common.h>
-
-#define MAGIC_BUFFER 0x20070728
-#define MAGIC_CHECK(is, should) \
- do { \
- if (unlikely((is) != (should))) { \
- printk(KERN_ERR \
- "magic mismatch: %x (expected %x)\n", \
- is, should); \
- BUG(); \
- } \
- } while (0)
-
-static int debug;
-module_param(debug, int, 0644);
-
-MODULE_DESCRIPTION("helper module to manage video4linux buffers");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
-MODULE_LICENSE("GPL");
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "vbuf: " fmt, ## arg); \
- } while (0)
-
-/* --------------------------------------------------------------------- */
-
-#define CALL(q, f, arg...) \
- ((q->int_ops->f) ? q->int_ops->f(arg) : 0)
-#define CALLPTR(q, f, arg...) \
- ((q->int_ops->f) ? q->int_ops->f(arg) : NULL)
-
-struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q)
-{
- struct videobuf_buffer *vb;
-
- BUG_ON(q->msize < sizeof(*vb));
-
- if (!q->int_ops || !q->int_ops->alloc_vb) {
- printk(KERN_ERR "No specific ops defined!\n");
- BUG();
- }
-
- vb = q->int_ops->alloc_vb(q->msize);
- if (NULL != vb) {
- init_waitqueue_head(&vb->done);
- vb->magic = MAGIC_BUFFER;
- }
-
- return vb;
-}
-EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
-
-static int state_neither_active_nor_queued(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- unsigned long flags;
- bool rc;
-
- spin_lock_irqsave(q->irqlock, flags);
- rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED;
- spin_unlock_irqrestore(q->irqlock, flags);
- return rc;
-};
-
-int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb,
- int non_blocking, int intr)
-{
- bool is_ext_locked;
- int ret = 0;
-
- MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
-
- if (non_blocking) {
- if (state_neither_active_nor_queued(q, vb))
- return 0;
- return -EAGAIN;
- }
-
- is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock);
-
- /* Release vdev lock to prevent this wait from blocking outside access to
- the device. */
- if (is_ext_locked)
- mutex_unlock(q->ext_lock);
- if (intr)
- ret = wait_event_interruptible(vb->done,
- state_neither_active_nor_queued(q, vb));
- else
- wait_event(vb->done, state_neither_active_nor_queued(q, vb));
- /* Relock */
- if (is_ext_locked)
- mutex_lock(q->ext_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_waiton);
-
-int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
- struct v4l2_framebuffer *fbuf)
-{
- MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- return CALL(q, iolock, q, vb, fbuf);
-}
-EXPORT_SYMBOL_GPL(videobuf_iolock);
-
-void *videobuf_queue_to_vaddr(struct videobuf_queue *q,
- struct videobuf_buffer *buf)
-{
- if (q->int_ops->vaddr)
- return q->int_ops->vaddr(buf);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr);
-
-/* --------------------------------------------------------------------- */
-
-
-void videobuf_queue_core_init(struct videobuf_queue *q,
- const struct videobuf_queue_ops *ops,
- struct device *dev,
- spinlock_t *irqlock,
- enum v4l2_buf_type type,
- enum v4l2_field field,
- unsigned int msize,
- void *priv,
- struct videobuf_qtype_ops *int_ops,
- struct mutex *ext_lock)
-{
- BUG_ON(!q);
- memset(q, 0, sizeof(*q));
- q->irqlock = irqlock;
- q->ext_lock = ext_lock;
- q->dev = dev;
- q->type = type;
- q->field = field;
- q->msize = msize;
- q->ops = ops;
- q->priv_data = priv;
- q->int_ops = int_ops;
-
- /* All buffer operations are mandatory */
- BUG_ON(!q->ops->buf_setup);
- BUG_ON(!q->ops->buf_prepare);
- BUG_ON(!q->ops->buf_queue);
- BUG_ON(!q->ops->buf_release);
-
- /* Lock is mandatory for queue_cancel to work */
- BUG_ON(!irqlock);
-
- /* Having implementations for abstract methods are mandatory */
- BUG_ON(!q->int_ops);
-
- mutex_init(&q->vb_lock);
- init_waitqueue_head(&q->wait);
- INIT_LIST_HEAD(&q->stream);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
-
-/* Locking: Only usage in bttv unsafe find way to remove */
-int videobuf_queue_is_busy(struct videobuf_queue *q)
-{
- int i;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- if (q->streaming) {
- dprintk(1, "busy: streaming active\n");
- return 1;
- }
- if (q->reading) {
- dprintk(1, "busy: pending read #1\n");
- return 1;
- }
- if (q->read_buf) {
- dprintk(1, "busy: pending read #2\n");
- return 1;
- }
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
- if (q->bufs[i]->map) {
- dprintk(1, "busy: buffer #%d mapped\n", i);
- return 1;
- }
- if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
- dprintk(1, "busy: buffer #%d queued\n", i);
- return 1;
- }
- if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {
- dprintk(1, "busy: buffer #%d active\n", i);
- return 1;
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);
-
-/*
- * __videobuf_free() - free all the buffers and their control structures
- *
- * This function can only be called if streaming/reading is off, i.e. no buffers
- * are under control of the driver.
- */
-/* Locking: Caller holds q->vb_lock */
-static int __videobuf_free(struct videobuf_queue *q)
-{
- int i;
-
- dprintk(1, "%s\n", __func__);
- if (!q)
- return 0;
-
- if (q->streaming || q->reading) {
- dprintk(1, "Cannot free buffers when streaming or reading\n");
- return -EBUSY;
- }
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- for (i = 0; i < VIDEO_MAX_FRAME; i++)
- if (q->bufs[i] && q->bufs[i]->map) {
- dprintk(1, "Cannot free mmapped buffers\n");
- return -EBUSY;
- }
-
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
- q->ops->buf_release(q, q->bufs[i]);
- kfree(q->bufs[i]);
- q->bufs[i] = NULL;
- }
-
- return 0;
-}
-
-/* Locking: Caller holds q->vb_lock */
-void videobuf_queue_cancel(struct videobuf_queue *q)
-{
- unsigned long flags = 0;
- int i;
-
- q->streaming = 0;
- q->reading = 0;
- wake_up_interruptible_sync(&q->wait);
-
- /* remove queued buffers from list */
- spin_lock_irqsave(q->irqlock, flags);
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
- if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
- list_del(&q->bufs[i]->queue);
- q->bufs[i]->state = VIDEOBUF_ERROR;
- wake_up_all(&q->bufs[i]->done);
- }
- }
- spin_unlock_irqrestore(q->irqlock, flags);
-
- /* free all buffers + clear queue */
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
- q->ops->buf_release(q, q->bufs[i]);
- }
- INIT_LIST_HEAD(&q->stream);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
-
-/* --------------------------------------------------------------------- */
-
-/* Locking: Caller holds q->vb_lock */
-enum v4l2_field videobuf_next_field(struct videobuf_queue *q)
-{
- enum v4l2_field field = q->field;
-
- BUG_ON(V4L2_FIELD_ANY == field);
-
- if (V4L2_FIELD_ALTERNATE == field) {
- if (V4L2_FIELD_TOP == q->last) {
- field = V4L2_FIELD_BOTTOM;
- q->last = V4L2_FIELD_BOTTOM;
- } else {
- field = V4L2_FIELD_TOP;
- q->last = V4L2_FIELD_TOP;
- }
- }
- return field;
-}
-EXPORT_SYMBOL_GPL(videobuf_next_field);
-
-/* Locking: Caller holds q->vb_lock */
-static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
- struct videobuf_buffer *vb, enum v4l2_buf_type type)
-{
- MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- b->index = vb->i;
- b->type = type;
-
- b->memory = vb->memory;
- switch (b->memory) {
- case V4L2_MEMORY_MMAP:
- b->m.offset = vb->boff;
- b->length = vb->bsize;
- break;
- case V4L2_MEMORY_USERPTR:
- b->m.userptr = vb->baddr;
- b->length = vb->bsize;
- break;
- case V4L2_MEMORY_OVERLAY:
- b->m.offset = vb->boff;
- break;
- case V4L2_MEMORY_DMABUF:
- /* DMABUF is not handled in videobuf framework */
- break;
- }
-
- b->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- if (vb->map)
- b->flags |= V4L2_BUF_FLAG_MAPPED;
-
- switch (vb->state) {
- case VIDEOBUF_PREPARED:
- case VIDEOBUF_QUEUED:
- case VIDEOBUF_ACTIVE:
- b->flags |= V4L2_BUF_FLAG_QUEUED;
- break;
- case VIDEOBUF_ERROR:
- b->flags |= V4L2_BUF_FLAG_ERROR;
- fallthrough;
- case VIDEOBUF_DONE:
- b->flags |= V4L2_BUF_FLAG_DONE;
- break;
- case VIDEOBUF_NEEDS_INIT:
- case VIDEOBUF_IDLE:
- /* nothing */
- break;
- }
-
- b->field = vb->field;
- v4l2_buffer_set_timestamp(b, vb->ts);
- b->bytesused = vb->size;
- b->sequence = vb->field_count >> 1;
-}
-
-int videobuf_mmap_free(struct videobuf_queue *q)
-{
- int ret;
- videobuf_queue_lock(q);
- ret = __videobuf_free(q);
- videobuf_queue_unlock(q);
- return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_mmap_free);
-
-/* Locking: Caller holds q->vb_lock */
-int __videobuf_mmap_setup(struct videobuf_queue *q,
- unsigned int bcount, unsigned int bsize,
- enum v4l2_memory memory)
-{
- unsigned int i;
- int err;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- err = __videobuf_free(q);
- if (0 != err)
- return err;
-
- /* Allocate and initialize buffers */
- for (i = 0; i < bcount; i++) {
- q->bufs[i] = videobuf_alloc_vb(q);
-
- if (NULL == q->bufs[i])
- break;
-
- q->bufs[i]->i = i;
- q->bufs[i]->memory = memory;
- q->bufs[i]->bsize = bsize;
- switch (memory) {
- case V4L2_MEMORY_MMAP:
- q->bufs[i]->boff = PAGE_ALIGN(bsize) * i;
- break;
- case V4L2_MEMORY_USERPTR:
- case V4L2_MEMORY_OVERLAY:
- case V4L2_MEMORY_DMABUF:
- /* nothing */
- break;
- }
- }
-
- if (!i)
- return -ENOMEM;
-
- dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize);
-
- return i;
-}
-EXPORT_SYMBOL_GPL(__videobuf_mmap_setup);
-
-int videobuf_mmap_setup(struct videobuf_queue *q,
- unsigned int bcount, unsigned int bsize,
- enum v4l2_memory memory)
-{
- int ret;
- videobuf_queue_lock(q);
- ret = __videobuf_mmap_setup(q, bcount, bsize, memory);
- videobuf_queue_unlock(q);
- return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
-
-int videobuf_reqbufs(struct videobuf_queue *q,
- struct v4l2_requestbuffers *req)
-{
- unsigned int size, count;
- int retval;
-
- if (req->memory != V4L2_MEMORY_MMAP &&
- req->memory != V4L2_MEMORY_USERPTR &&
- req->memory != V4L2_MEMORY_OVERLAY) {
- dprintk(1, "reqbufs: memory type invalid\n");
- return -EINVAL;
- }
-
- videobuf_queue_lock(q);
- if (req->type != q->type) {
- dprintk(1, "reqbufs: queue type invalid\n");
- retval = -EINVAL;
- goto done;
- }
-
- if (q->streaming) {
- dprintk(1, "reqbufs: streaming already exists\n");
- retval = -EBUSY;
- goto done;
- }
- if (!list_empty(&q->stream)) {
- dprintk(1, "reqbufs: stream running\n");
- retval = -EBUSY;
- goto done;
- }
-
- if (req->count == 0) {
- dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
- retval = __videobuf_free(q);
- goto done;
- }
-
- count = req->count;
- if (count > VIDEO_MAX_FRAME)
- count = VIDEO_MAX_FRAME;
- size = 0;
- q->ops->buf_setup(q, &count, &size);
- dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n",
- count, size,
- (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT));
-
- retval = __videobuf_mmap_setup(q, count, size, req->memory);
- if (retval < 0) {
- dprintk(1, "reqbufs: mmap setup returned %d\n", retval);
- goto done;
- }
-
- req->count = retval;
- retval = 0;
-
- done:
- videobuf_queue_unlock(q);
- return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_reqbufs);
-
-int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
-{
- int ret = -EINVAL;
-
- videobuf_queue_lock(q);
- if (unlikely(b->type != q->type)) {
- dprintk(1, "querybuf: Wrong type.\n");
- goto done;
- }
- if (unlikely(b->index >= VIDEO_MAX_FRAME)) {
- dprintk(1, "querybuf: index out of range.\n");
- goto done;
- }
- if (unlikely(NULL == q->bufs[b->index])) {
- dprintk(1, "querybuf: buffer is null.\n");
- goto done;
- }
-
- videobuf_status(q, b, q->bufs[b->index], q->type);
-
- ret = 0;
-done:
- videobuf_queue_unlock(q);
- return ret;
-}
-EXPORT_SYMBOL_GPL(videobuf_querybuf);
-
-int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
-{
- struct videobuf_buffer *buf;
- enum v4l2_field field;
- unsigned long flags = 0;
- int retval;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- if (b->memory == V4L2_MEMORY_MMAP)
- mmap_read_lock(current->mm);
-
- videobuf_queue_lock(q);
- retval = -EBUSY;
- if (q->reading) {
- dprintk(1, "qbuf: Reading running...\n");
- goto done;
- }
- retval = -EINVAL;
- if (b->type != q->type) {
- dprintk(1, "qbuf: Wrong type.\n");
- goto done;
- }
- if (b->index >= VIDEO_MAX_FRAME) {
- dprintk(1, "qbuf: index out of range.\n");
- goto done;
- }
- buf = q->bufs[b->index];
- if (NULL == buf) {
- dprintk(1, "qbuf: buffer is null.\n");
- goto done;
- }
- MAGIC_CHECK(buf->magic, MAGIC_BUFFER);
- if (buf->memory != b->memory) {
- dprintk(1, "qbuf: memory type is wrong.\n");
- goto done;
- }
- if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) {
- dprintk(1, "qbuf: buffer is already queued or active.\n");
- goto done;
- }
-
- switch (b->memory) {
- case V4L2_MEMORY_MMAP:
- if (0 == buf->baddr) {
- dprintk(1, "qbuf: mmap requested but buffer addr is zero!\n");
- goto done;
- }
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT
- || q->type == V4L2_BUF_TYPE_VBI_OUTPUT
- || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT
- || q->type == V4L2_BUF_TYPE_SDR_OUTPUT) {
- buf->size = b->bytesused;
- buf->field = b->field;
- buf->ts = v4l2_buffer_get_timestamp(b);
- }
- break;
- case V4L2_MEMORY_USERPTR:
- if (b->length < buf->bsize) {
- dprintk(1, "qbuf: buffer length is not enough\n");
- goto done;
- }
- if (VIDEOBUF_NEEDS_INIT != buf->state &&
- buf->baddr != b->m.userptr)
- q->ops->buf_release(q, buf);
- buf->baddr = b->m.userptr;
- break;
- case V4L2_MEMORY_OVERLAY:
- buf->boff = b->m.offset;
- break;
- default:
- dprintk(1, "qbuf: wrong memory type\n");
- goto done;
- }
-
- dprintk(1, "qbuf: requesting next field\n");
- field = videobuf_next_field(q);
- retval = q->ops->buf_prepare(q, buf, field);
- if (0 != retval) {
- dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);
- goto done;
- }
-
- list_add_tail(&buf->stream, &q->stream);
- if (q->streaming) {
- spin_lock_irqsave(q->irqlock, flags);
- q->ops->buf_queue(q, buf);
- spin_unlock_irqrestore(q->irqlock, flags);
- }
- dprintk(1, "qbuf: succeeded\n");
- retval = 0;
- wake_up_interruptible_sync(&q->wait);
-
-done:
- videobuf_queue_unlock(q);
-
- if (b->memory == V4L2_MEMORY_MMAP)
- mmap_read_unlock(current->mm);
-
- return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_qbuf);
-
-/* Locking: Caller holds q->vb_lock */
-static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
-{
- int retval;
-
-checks:
- if (!q->streaming) {
- dprintk(1, "next_buffer: Not streaming\n");
- retval = -EINVAL;
- goto done;
- }
-
- if (list_empty(&q->stream)) {
- if (noblock) {
- retval = -EAGAIN;
- dprintk(2, "next_buffer: no buffers to dequeue\n");
- goto done;
- } else {
- dprintk(2, "next_buffer: waiting on buffer\n");
-
- /* Drop lock to avoid deadlock with qbuf */
- videobuf_queue_unlock(q);
-
- /* Checking list_empty and streaming is safe without
- * locks because we goto checks to validate while
- * holding locks before proceeding */
- retval = wait_event_interruptible(q->wait,
- !list_empty(&q->stream) || !q->streaming);
- videobuf_queue_lock(q);
-
- if (retval)
- goto done;
-
- goto checks;
- }
- }
-
- retval = 0;
-
-done:
- return retval;
-}
-
-/* Locking: Caller holds q->vb_lock */
-static int stream_next_buffer(struct videobuf_queue *q,
- struct videobuf_buffer **vb, int nonblocking)
-{
- int retval;
- struct videobuf_buffer *buf = NULL;
-
- retval = stream_next_buffer_check_queue(q, nonblocking);
- if (retval)
- goto done;
-
- buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
- retval = videobuf_waiton(q, buf, nonblocking, 1);
- if (retval < 0)
- goto done;
-
- *vb = buf;
-done:
- return retval;
-}
-
-int videobuf_dqbuf(struct videobuf_queue *q,
- struct v4l2_buffer *b, int nonblocking)
-{
- struct videobuf_buffer *buf = NULL;
- int retval;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- memset(b, 0, sizeof(*b));
- videobuf_queue_lock(q);
-
- retval = stream_next_buffer(q, &buf, nonblocking);
- if (retval < 0) {
- dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
- goto done;
- }
-
- switch (buf->state) {
- case VIDEOBUF_ERROR:
- dprintk(1, "dqbuf: state is error\n");
- break;
- case VIDEOBUF_DONE:
- dprintk(1, "dqbuf: state is done\n");
- break;
- default:
- dprintk(1, "dqbuf: state invalid\n");
- retval = -EINVAL;
- goto done;
- }
- CALL(q, sync, q, buf);
- videobuf_status(q, b, buf, q->type);
- list_del(&buf->stream);
- buf->state = VIDEOBUF_IDLE;
- b->flags &= ~V4L2_BUF_FLAG_DONE;
-done:
- videobuf_queue_unlock(q);
- return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_dqbuf);
-
-int videobuf_streamon(struct videobuf_queue *q)
-{
- struct videobuf_buffer *buf;
- unsigned long flags = 0;
- int retval;
-
- videobuf_queue_lock(q);
- retval = -EBUSY;
- if (q->reading)
- goto done;
- retval = 0;
- if (q->streaming)
- goto done;
- q->streaming = 1;
- spin_lock_irqsave(q->irqlock, flags);
- list_for_each_entry(buf, &q->stream, stream)
- if (buf->state == VIDEOBUF_PREPARED)
- q->ops->buf_queue(q, buf);
- spin_unlock_irqrestore(q->irqlock, flags);
-
- wake_up_interruptible_sync(&q->wait);
-done:
- videobuf_queue_unlock(q);
- return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_streamon);
-
-/* Locking: Caller holds q->vb_lock */
-static int __videobuf_streamoff(struct videobuf_queue *q)
-{
- if (!q->streaming)
- return -EINVAL;
-
- videobuf_queue_cancel(q);
-
- return 0;
-}
-
-int videobuf_streamoff(struct videobuf_queue *q)
-{
- int retval;
-
- videobuf_queue_lock(q);
- retval = __videobuf_streamoff(q);
- videobuf_queue_unlock(q);
-
- return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_streamoff);
-
-/* Locking: Caller holds q->vb_lock */
-static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
- char __user *data,
- size_t count, loff_t *ppos)
-{
- enum v4l2_field field;
- unsigned long flags = 0;
- int retval;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- /* setup stuff */
- q->read_buf = videobuf_alloc_vb(q);
- if (NULL == q->read_buf)
- return -ENOMEM;
-
- q->read_buf->memory = V4L2_MEMORY_USERPTR;
- q->read_buf->baddr = (unsigned long)data;
- q->read_buf->bsize = count;
-
- field = videobuf_next_field(q);
- retval = q->ops->buf_prepare(q, q->read_buf, field);
- if (0 != retval)
- goto done;
-
- /* start capture & wait */
- spin_lock_irqsave(q->irqlock, flags);
- q->ops->buf_queue(q, q->read_buf);
- spin_unlock_irqrestore(q->irqlock, flags);
- retval = videobuf_waiton(q, q->read_buf, 0, 0);
- if (0 == retval) {
- CALL(q, sync, q, q->read_buf);
- if (VIDEOBUF_ERROR == q->read_buf->state)
- retval = -EIO;
- else
- retval = q->read_buf->size;
- }
-
-done:
- /* cleanup */
- q->ops->buf_release(q, q->read_buf);
- kfree(q->read_buf);
- q->read_buf = NULL;
- return retval;
-}
-
-static int __videobuf_copy_to_user(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- char __user *data, size_t count,
- int nonblocking)
-{
- void *vaddr = CALLPTR(q, vaddr, buf);
-
- /* copy to userspace */
- if (count > buf->size - q->read_off)
- count = buf->size - q->read_off;
-
- if (copy_to_user(data, vaddr + q->read_off, count))
- return -EFAULT;
-
- return count;
-}
-
-static int __videobuf_copy_stream(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- char __user *data, size_t count, size_t pos,
- int vbihack, int nonblocking)
-{
- unsigned int *fc = CALLPTR(q, vaddr, buf);
-
- if (vbihack) {
- /* dirty, undocumented hack -- pass the frame counter
- * within the last four bytes of each vbi data block.
- * We need that one to maintain backward compatibility
- * to all vbi decoding software out there ... */
- fc += (buf->size >> 2) - 1;
- *fc = buf->field_count >> 1;
- dprintk(1, "vbihack: %d\n", *fc);
- }
-
- /* copy stuff using the common method */
- count = __videobuf_copy_to_user(q, buf, data, count, nonblocking);
-
- if ((count == -EFAULT) && (pos == 0))
- return -EFAULT;
-
- return count;
-}
-
-ssize_t videobuf_read_one(struct videobuf_queue *q,
- char __user *data, size_t count, loff_t *ppos,
- int nonblocking)
-{
- enum v4l2_field field;
- unsigned long flags = 0;
- unsigned size = 0, nbufs = 1;
- int retval;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- videobuf_queue_lock(q);
-
- q->ops->buf_setup(q, &nbufs, &size);
-
- if (NULL == q->read_buf &&
- count >= size &&
- !nonblocking) {
- retval = videobuf_read_zerocopy(q, data, count, ppos);
- if (retval >= 0 || retval == -EIO)
- /* ok, all done */
- goto done;
- /* fallback to kernel bounce buffer on failures */
- }
-
- if (NULL == q->read_buf) {
- /* need to capture a new frame */
- retval = -ENOMEM;
- q->read_buf = videobuf_alloc_vb(q);
-
- dprintk(1, "video alloc=0x%p\n", q->read_buf);
- if (NULL == q->read_buf)
- goto done;
- q->read_buf->memory = V4L2_MEMORY_USERPTR;
- q->read_buf->bsize = count; /* preferred size */
- field = videobuf_next_field(q);
- retval = q->ops->buf_prepare(q, q->read_buf, field);
-
- if (0 != retval) {
- kfree(q->read_buf);
- q->read_buf = NULL;
- goto done;
- }
-
- spin_lock_irqsave(q->irqlock, flags);
- q->ops->buf_queue(q, q->read_buf);
- spin_unlock_irqrestore(q->irqlock, flags);
-
- q->read_off = 0;
- }
-
- /* wait until capture is done */
- retval = videobuf_waiton(q, q->read_buf, nonblocking, 1);
- if (0 != retval)
- goto done;
-
- CALL(q, sync, q, q->read_buf);
-
- if (VIDEOBUF_ERROR == q->read_buf->state) {
- /* catch I/O errors */
- q->ops->buf_release(q, q->read_buf);
- kfree(q->read_buf);
- q->read_buf = NULL;
- retval = -EIO;
- goto done;
- }
-
- /* Copy to userspace */
- retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking);
- if (retval < 0)
- goto done;
-
- q->read_off += retval;
- if (q->read_off == q->read_buf->size) {
- /* all data copied, cleanup */
- q->ops->buf_release(q, q->read_buf);
- kfree(q->read_buf);
- q->read_buf = NULL;
- }
-
-done:
- videobuf_queue_unlock(q);
- return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_read_one);
-
-/* Locking: Caller holds q->vb_lock */
-static int __videobuf_read_start(struct videobuf_queue *q)
-{
- enum v4l2_field field;
- unsigned long flags = 0;
- unsigned int count = 0, size = 0;
- int err, i;
-
- q->ops->buf_setup(q, &count, &size);
- if (count < 2)
- count = 2;
- if (count > VIDEO_MAX_FRAME)
- count = VIDEO_MAX_FRAME;
- size = PAGE_ALIGN(size);
-
- err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR);
- if (err < 0)
- return err;
-
- count = err;
-
- for (i = 0; i < count; i++) {
- field = videobuf_next_field(q);
- err = q->ops->buf_prepare(q, q->bufs[i], field);
- if (err)
- return err;
- list_add_tail(&q->bufs[i]->stream, &q->stream);
- }
- spin_lock_irqsave(q->irqlock, flags);
- for (i = 0; i < count; i++)
- q->ops->buf_queue(q, q->bufs[i]);
- spin_unlock_irqrestore(q->irqlock, flags);
- q->reading = 1;
- return 0;
-}
-
-static void __videobuf_read_stop(struct videobuf_queue *q)
-{
- int i;
-
- videobuf_queue_cancel(q);
- __videobuf_free(q);
- INIT_LIST_HEAD(&q->stream);
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
- kfree(q->bufs[i]);
- q->bufs[i] = NULL;
- }
- q->read_buf = NULL;
-}
-
-int videobuf_read_start(struct videobuf_queue *q)
-{
- int rc;
-
- videobuf_queue_lock(q);
- rc = __videobuf_read_start(q);
- videobuf_queue_unlock(q);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(videobuf_read_start);
-
-void videobuf_read_stop(struct videobuf_queue *q)
-{
- videobuf_queue_lock(q);
- __videobuf_read_stop(q);
- videobuf_queue_unlock(q);
-}
-EXPORT_SYMBOL_GPL(videobuf_read_stop);
-
-void videobuf_stop(struct videobuf_queue *q)
-{
- videobuf_queue_lock(q);
-
- if (q->streaming)
- __videobuf_streamoff(q);
-
- if (q->reading)
- __videobuf_read_stop(q);
-
- videobuf_queue_unlock(q);
-}
-EXPORT_SYMBOL_GPL(videobuf_stop);
-
-ssize_t videobuf_read_stream(struct videobuf_queue *q,
- char __user *data, size_t count, loff_t *ppos,
- int vbihack, int nonblocking)
-{
- int rc, retval;
- unsigned long flags = 0;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- dprintk(2, "%s\n", __func__);
- videobuf_queue_lock(q);
- retval = -EBUSY;
- if (q->streaming)
- goto done;
- if (!q->reading) {
- retval = __videobuf_read_start(q);
- if (retval < 0)
- goto done;
- }
-
- retval = 0;
- while (count > 0) {
- /* get / wait for data */
- if (NULL == q->read_buf) {
- q->read_buf = list_entry(q->stream.next,
- struct videobuf_buffer,
- stream);
- list_del(&q->read_buf->stream);
- q->read_off = 0;
- }
- rc = videobuf_waiton(q, q->read_buf, nonblocking, 1);
- if (rc < 0) {
- if (0 == retval)
- retval = rc;
- break;
- }
-
- if (q->read_buf->state == VIDEOBUF_DONE) {
- rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count,
- retval, vbihack, nonblocking);
- if (rc < 0) {
- retval = rc;
- break;
- }
- retval += rc;
- count -= rc;
- q->read_off += rc;
- } else {
- /* some error */
- q->read_off = q->read_buf->size;
- if (0 == retval)
- retval = -EIO;
- }
-
- /* requeue buffer when done with copying */
- if (q->read_off == q->read_buf->size) {
- list_add_tail(&q->read_buf->stream,
- &q->stream);
- spin_lock_irqsave(q->irqlock, flags);
- q->ops->buf_queue(q, q->read_buf);
- spin_unlock_irqrestore(q->irqlock, flags);
- q->read_buf = NULL;
- }
- if (retval < 0)
- break;
- }
-
-done:
- videobuf_queue_unlock(q);
- return retval;
-}
-EXPORT_SYMBOL_GPL(videobuf_read_stream);
-
-__poll_t videobuf_poll_stream(struct file *file,
- struct videobuf_queue *q,
- poll_table *wait)
-{
- __poll_t req_events = poll_requested_events(wait);
- struct videobuf_buffer *buf = NULL;
- __poll_t rc = 0;
-
- videobuf_queue_lock(q);
- if (q->streaming) {
- if (!list_empty(&q->stream))
- buf = list_entry(q->stream.next,
- struct videobuf_buffer, stream);
- } else if (req_events & (EPOLLIN | EPOLLRDNORM)) {
- if (!q->reading)
- __videobuf_read_start(q);
- if (!q->reading) {
- rc = EPOLLERR;
- } else if (NULL == q->read_buf) {
- q->read_buf = list_entry(q->stream.next,
- struct videobuf_buffer,
- stream);
- list_del(&q->read_buf->stream);
- q->read_off = 0;
- }
- buf = q->read_buf;
- }
- if (buf)
- poll_wait(file, &buf->done, wait);
- else
- rc = EPOLLERR;
-
- if (0 == rc) {
- if (buf->state == VIDEOBUF_DONE ||
- buf->state == VIDEOBUF_ERROR) {
- switch (q->type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- case V4L2_BUF_TYPE_SDR_OUTPUT:
- rc = EPOLLOUT | EPOLLWRNORM;
- break;
- default:
- rc = EPOLLIN | EPOLLRDNORM;
- break;
- }
- }
- }
- videobuf_queue_unlock(q);
- return rc;
-}
-EXPORT_SYMBOL_GPL(videobuf_poll_stream);
-
-int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma)
-{
- int rc = -EINVAL;
- int i;
-
- MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
- if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
- dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n");
- return -EINVAL;
- }
-
- videobuf_queue_lock(q);
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- struct videobuf_buffer *buf = q->bufs[i];
-
- if (buf && buf->memory == V4L2_MEMORY_MMAP &&
- buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) {
- rc = CALL(q, mmap_mapper, q, buf, vma);
- break;
- }
- }
- videobuf_queue_unlock(q);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
deleted file mode 100644
index 52312ce2ba05..000000000000
--- a/drivers/media/v4l2-core/videobuf-dma-contig.c
+++ /dev/null
@@ -1,408 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * helper functions for physically contiguous capture buffers
- *
- * The functions support hardware lacking scatter gather support
- * (i.e. the buffers must be linear in physical memory)
- *
- * Copyright (c) 2008 Magnus Damm
- *
- * Based on videobuf-vmalloc.c,
- * (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/dma-mapping.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <media/videobuf-dma-contig.h>
-
-struct videobuf_dma_contig_memory {
- u32 magic;
- void *vaddr;
- dma_addr_t dma_handle;
- unsigned long size;
-};
-
-#define MAGIC_DC_MEM 0x0733ac61
-#define MAGIC_CHECK(is, should) \
- if (unlikely((is) != (should))) { \
- pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
- BUG(); \
- }
-
-static int __videobuf_dc_alloc(struct device *dev,
- struct videobuf_dma_contig_memory *mem,
- unsigned long size, gfp_t flags)
-{
- mem->size = size;
- mem->vaddr = dma_alloc_coherent(dev, mem->size,
- &mem->dma_handle, flags);
-
- if (!mem->vaddr) {
- dev_err(dev, "memory alloc size %ld failed\n", mem->size);
- return -ENOMEM;
- }
-
- dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
-
- return 0;
-}
-
-static void __videobuf_dc_free(struct device *dev,
- struct videobuf_dma_contig_memory *mem)
-{
- dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
-
- mem->vaddr = NULL;
-}
-
-static void videobuf_vm_open(struct vm_area_struct *vma)
-{
- struct videobuf_mapping *map = vma->vm_private_data;
-
- dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
- map, map->count, vma->vm_start, vma->vm_end);
-
- map->count++;
-}
-
-static void videobuf_vm_close(struct vm_area_struct *vma)
-{
- struct videobuf_mapping *map = vma->vm_private_data;
- struct videobuf_queue *q = map->q;
- int i;
-
- dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
- map, map->count, vma->vm_start, vma->vm_end);
-
- map->count--;
- if (0 == map->count) {
- struct videobuf_dma_contig_memory *mem;
-
- dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
-
- /* We need first to cancel streams, before unmapping */
- if (q->streaming)
- videobuf_queue_cancel(q);
-
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
-
- if (q->bufs[i]->map != map)
- continue;
-
- mem = q->bufs[i]->priv;
- if (mem) {
- /* This callback is called only if kernel has
- allocated memory and this memory is mmapped.
- In this case, memory should be freed,
- in order to do memory unmap.
- */
-
- MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
- /* vfree is not atomic - can't be
- called with IRQ's disabled
- */
- dev_dbg(q->dev, "buf[%d] freeing %p\n",
- i, mem->vaddr);
-
- __videobuf_dc_free(q->dev, mem);
- mem->vaddr = NULL;
- }
-
- q->bufs[i]->map = NULL;
- q->bufs[i]->baddr = 0;
- }
-
- kfree(map);
-
- videobuf_queue_unlock(q);
- }
-}
-
-static const struct vm_operations_struct videobuf_vm_ops = {
- .open = videobuf_vm_open,
- .close = videobuf_vm_close,
-};
-
-/**
- * videobuf_dma_contig_user_put() - reset pointer to user space buffer
- * @mem: per-buffer private videobuf-dma-contig data
- *
- * This function resets the user space pointer
- */
-static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
-{
- mem->dma_handle = 0;
- mem->size = 0;
-}
-
-/**
- * videobuf_dma_contig_user_get() - setup user space memory pointer
- * @mem: per-buffer private videobuf-dma-contig data
- * @vb: video buffer to map
- *
- * This function validates and sets up a pointer to user space memory.
- * Only physically contiguous pfn-mapped memory is accepted.
- *
- * Returns 0 if successful.
- */
-static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
- struct videobuf_buffer *vb)
-{
- unsigned long untagged_baddr = untagged_addr(vb->baddr);
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- unsigned long prev_pfn, this_pfn;
- unsigned long pages_done, user_address;
- unsigned int offset;
- int ret;
-
- offset = untagged_baddr & ~PAGE_MASK;
- mem->size = PAGE_ALIGN(vb->size + offset);
- ret = -EINVAL;
-
- mmap_read_lock(mm);
-
- vma = find_vma(mm, untagged_baddr);
- if (!vma)
- goto out_up;
-
- if ((untagged_baddr + mem->size) > vma->vm_end)
- goto out_up;
-
- pages_done = 0;
- prev_pfn = 0; /* kill warning */
- user_address = untagged_baddr;
-
- while (pages_done < (mem->size >> PAGE_SHIFT)) {
- ret = follow_pfn(vma, user_address, &this_pfn);
- if (ret)
- break;
-
- if (pages_done == 0)
- mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
- else if (this_pfn != (prev_pfn + 1))
- ret = -EFAULT;
-
- if (ret)
- break;
-
- prev_pfn = this_pfn;
- user_address += PAGE_SIZE;
- pages_done++;
- }
-
-out_up:
- mmap_read_unlock(current->mm);
-
- return ret;
-}
-
-static struct videobuf_buffer *__videobuf_alloc(size_t size)
-{
- struct videobuf_dma_contig_memory *mem;
- struct videobuf_buffer *vb;
-
- vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
- if (vb) {
- vb->priv = ((char *)vb) + size;
- mem = vb->priv;
- mem->magic = MAGIC_DC_MEM;
- }
-
- return vb;
-}
-
-static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
-{
- struct videobuf_dma_contig_memory *mem = buf->priv;
-
- BUG_ON(!mem);
- MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
- return mem->vaddr;
-}
-
-static int __videobuf_iolock(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- struct v4l2_framebuffer *fbuf)
-{
- struct videobuf_dma_contig_memory *mem = vb->priv;
-
- BUG_ON(!mem);
- MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
- switch (vb->memory) {
- case V4L2_MEMORY_MMAP:
- dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
-
- /* All handling should be done by __videobuf_mmap_mapper() */
- if (!mem->vaddr) {
- dev_err(q->dev, "memory is not allocated/mmapped.\n");
- return -EINVAL;
- }
- break;
- case V4L2_MEMORY_USERPTR:
- dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
-
- /* handle pointer from user space */
- if (vb->baddr)
- return videobuf_dma_contig_user_get(mem, vb);
-
- /* allocate memory for the read() method */
- if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size),
- GFP_KERNEL))
- return -ENOMEM;
- break;
- case V4L2_MEMORY_OVERLAY:
- default:
- dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int __videobuf_mmap_mapper(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- struct vm_area_struct *vma)
-{
- struct videobuf_dma_contig_memory *mem;
- struct videobuf_mapping *map;
- int retval;
-
- dev_dbg(q->dev, "%s\n", __func__);
-
- /* create mapping + update buffer list */
- map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
- if (!map)
- return -ENOMEM;
-
- buf->map = map;
- map->q = q;
-
- buf->baddr = vma->vm_start;
-
- mem = buf->priv;
- BUG_ON(!mem);
- MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
- if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize),
- GFP_KERNEL | __GFP_COMP))
- goto error;
-
- /* Try to remap memory */
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- /* the "vm_pgoff" is just used in v4l2 to find the
- * corresponding buffer data structure which is allocated
- * earlier and it does not mean the offset from the physical
- * buffer start address as usual. So set it to 0 to pass
- * the sanity check in vm_iomap_memory().
- */
- vma->vm_pgoff = 0;
-
- retval = vm_iomap_memory(vma, mem->dma_handle, mem->size);
- if (retval) {
- dev_err(q->dev, "mmap: remap failed with error %d. ",
- retval);
- dma_free_coherent(q->dev, mem->size,
- mem->vaddr, mem->dma_handle);
- goto error;
- }
-
- vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND;
- vma->vm_private_data = map;
-
- dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
- map, q, vma->vm_start, vma->vm_end,
- (long int)buf->bsize, vma->vm_pgoff, buf->i);
-
- videobuf_vm_open(vma);
-
- return 0;
-
-error:
- kfree(map);
- return -ENOMEM;
-}
-
-static struct videobuf_qtype_ops qops = {
- .magic = MAGIC_QTYPE_OPS,
- .alloc_vb = __videobuf_alloc,
- .iolock = __videobuf_iolock,
- .mmap_mapper = __videobuf_mmap_mapper,
- .vaddr = __videobuf_to_vaddr,
-};
-
-void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
- const struct videobuf_queue_ops *ops,
- struct device *dev,
- spinlock_t *irqlock,
- enum v4l2_buf_type type,
- enum v4l2_field field,
- unsigned int msize,
- void *priv,
- struct mutex *ext_lock)
-{
- videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
- priv, &qops, ext_lock);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
-
-dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
-{
- struct videobuf_dma_contig_memory *mem = buf->priv;
-
- BUG_ON(!mem);
- MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
- return mem->dma_handle;
-}
-EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
-
-void videobuf_dma_contig_free(struct videobuf_queue *q,
- struct videobuf_buffer *buf)
-{
- struct videobuf_dma_contig_memory *mem = buf->priv;
-
- /* mmapped memory can't be freed here, otherwise mmapped region
- would be released, while still needed. In this case, the memory
- release should happen inside videobuf_vm_close().
- So, it should free memory only if the memory were allocated for
- read() operation.
- */
- if (buf->memory != V4L2_MEMORY_USERPTR)
- return;
-
- if (!mem)
- return;
-
- MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
- /* handle user space pointer case */
- if (buf->baddr) {
- videobuf_dma_contig_user_put(mem);
- return;
- }
-
- /* read() method */
- if (mem->vaddr) {
- __videobuf_dc_free(q->dev, mem);
- mem->vaddr = NULL;
- }
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
-
-MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
-MODULE_AUTHOR("Magnus Damm");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
deleted file mode 100644
index f75e5eedeee0..000000000000
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ /dev/null
@@ -1,685 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * helper functions for SG DMA video4linux capture buffers
- *
- * The functions expect the hardware being able to scatter gather
- * (i.e. the buffers are not linear in physical memory, but fragmented
- * into PAGE_SIZE chunks). They also assume the driver does not need
- * to touch the video data.
- *
- * (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
- *
- * Highly based on video-buf written originally by:
- * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
- * (c) 2006 Mauro Carvalho Chehab, <mchehab@kernel.org>
- * (c) 2006 Ted Walther and John Sokol
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/sched/mm.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pgtable.h>
-
-#include <linux/dma-mapping.h>
-#include <linux/vmalloc.h>
-#include <linux/pagemap.h>
-#include <linux/scatterlist.h>
-#include <asm/page.h>
-
-#include <media/videobuf-dma-sg.h>
-
-#define MAGIC_DMABUF 0x19721112
-#define MAGIC_SG_MEM 0x17890714
-
-#define MAGIC_CHECK(is, should) \
- if (unlikely((is) != (should))) { \
- printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \
- is, should); \
- BUG(); \
- }
-
-static int debug;
-module_param(debug, int, 0644);
-
-MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
-MODULE_LICENSE("GPL");
-
-#define dprintk(level, fmt, arg...) \
- if (debug >= level) \
- printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg)
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Return a scatterlist for some page-aligned vmalloc()'ed memory
- * block (NULL on errors). Memory for the scatterlist is allocated
- * using kmalloc. The caller must free the memory.
- */
-static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt,
- int nr_pages)
-{
- struct scatterlist *sglist;
- struct page *pg;
- int i;
-
- sglist = vzalloc(array_size(nr_pages, sizeof(*sglist)));
- if (NULL == sglist)
- return NULL;
- sg_init_table(sglist, nr_pages);
- for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
- pg = vmalloc_to_page(virt);
- if (NULL == pg)
- goto err;
- BUG_ON(PageHighMem(pg));
- sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
- }
- return sglist;
-
-err:
- vfree(sglist);
- return NULL;
-}
-
-/*
- * Return a scatterlist for a an array of userpages (NULL on errors).
- * Memory for the scatterlist is allocated using kmalloc. The caller
- * must free the memory.
- */
-static struct scatterlist *videobuf_pages_to_sg(struct page **pages,
- int nr_pages, int offset, size_t size)
-{
- struct scatterlist *sglist;
- int i;
-
- if (NULL == pages[0])
- return NULL;
- sglist = vmalloc(array_size(nr_pages, sizeof(*sglist)));
- if (NULL == sglist)
- return NULL;
- sg_init_table(sglist, nr_pages);
-
- if (PageHighMem(pages[0]))
- /* DMA to highmem pages might not work */
- goto highmem;
- sg_set_page(&sglist[0], pages[0],
- min_t(size_t, PAGE_SIZE - offset, size), offset);
- size -= min_t(size_t, PAGE_SIZE - offset, size);
- for (i = 1; i < nr_pages; i++) {
- if (NULL == pages[i])
- goto nopage;
- if (PageHighMem(pages[i]))
- goto highmem;
- sg_set_page(&sglist[i], pages[i], min_t(size_t, PAGE_SIZE, size), 0);
- size -= min_t(size_t, PAGE_SIZE, size);
- }
- return sglist;
-
-nopage:
- dprintk(2, "sgl: oops - no page\n");
- vfree(sglist);
- return NULL;
-
-highmem:
- dprintk(2, "sgl: oops - highmem page\n");
- vfree(sglist);
- return NULL;
-}
-
-/* --------------------------------------------------------------------- */
-
-struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf)
-{
- struct videobuf_dma_sg_memory *mem = buf->priv;
- BUG_ON(!mem);
-
- MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
- return &mem->dma;
-}
-EXPORT_SYMBOL_GPL(videobuf_to_dma);
-
-static void videobuf_dma_init(struct videobuf_dmabuf *dma)
-{
- memset(dma, 0, sizeof(*dma));
- dma->magic = MAGIC_DMABUF;
-}
-
-static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
- int direction, unsigned long data, unsigned long size)
-{
- unsigned long first, last;
- int err, rw = 0;
- unsigned int flags = FOLL_FORCE;
-
- dma->direction = direction;
- switch (dma->direction) {
- case DMA_FROM_DEVICE:
- rw = READ;
- break;
- case DMA_TO_DEVICE:
- rw = WRITE;
- break;
- default:
- BUG();
- }
-
- first = (data & PAGE_MASK) >> PAGE_SHIFT;
- last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;
- dma->offset = data & ~PAGE_MASK;
- dma->size = size;
- dma->nr_pages = last-first+1;
- dma->pages = kmalloc_array(dma->nr_pages, sizeof(struct page *),
- GFP_KERNEL);
- if (NULL == dma->pages)
- return -ENOMEM;
-
- if (rw == READ)
- flags |= FOLL_WRITE;
-
- dprintk(1, "init user [0x%lx+0x%lx => %lu pages]\n",
- data, size, dma->nr_pages);
-
- err = pin_user_pages(data & PAGE_MASK, dma->nr_pages,
- flags | FOLL_LONGTERM, dma->pages, NULL);
-
- if (err != dma->nr_pages) {
- dma->nr_pages = (err >= 0) ? err : 0;
- dprintk(1, "pin_user_pages: err=%d [%lu]\n", err,
- dma->nr_pages);
- return err < 0 ? err : -EINVAL;
- }
- return 0;
-}
-
-static int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
- unsigned long data, unsigned long size)
-{
- int ret;
-
- mmap_read_lock(current->mm);
- ret = videobuf_dma_init_user_locked(dma, direction, data, size);
- mmap_read_unlock(current->mm);
-
- return ret;
-}
-
-static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
- unsigned long nr_pages)
-{
- int i;
-
- dprintk(1, "init kernel [%lu pages]\n", nr_pages);
-
- dma->direction = direction;
- dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages),
- GFP_KERNEL);
- if (!dma->vaddr_pages)
- return -ENOMEM;
-
- dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL);
- if (!dma->dma_addr) {
- kfree(dma->vaddr_pages);
- return -ENOMEM;
- }
- for (i = 0; i < nr_pages; i++) {
- void *addr;
-
- addr = dma_alloc_coherent(dma->dev, PAGE_SIZE,
- &(dma->dma_addr[i]), GFP_KERNEL);
- if (addr == NULL)
- goto out_free_pages;
-
- dma->vaddr_pages[i] = virt_to_page(addr);
- }
- dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP,
- PAGE_KERNEL);
- if (NULL == dma->vaddr) {
- dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages);
- goto out_free_pages;
- }
-
- dprintk(1, "vmalloc is at addr %p, size=%lu\n",
- dma->vaddr, nr_pages << PAGE_SHIFT);
-
- memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
- dma->nr_pages = nr_pages;
-
- return 0;
-out_free_pages:
- while (i > 0) {
- void *addr;
-
- i--;
- addr = page_address(dma->vaddr_pages[i]);
- dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]);
- }
- kfree(dma->dma_addr);
- dma->dma_addr = NULL;
- kfree(dma->vaddr_pages);
- dma->vaddr_pages = NULL;
-
- return -ENOMEM;
-
-}
-
-static int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
- dma_addr_t addr, unsigned long nr_pages)
-{
- dprintk(1, "init overlay [%lu pages @ bus 0x%lx]\n",
- nr_pages, (unsigned long)addr);
- dma->direction = direction;
-
- if (0 == addr)
- return -EINVAL;
-
- dma->bus_addr = addr;
- dma->nr_pages = nr_pages;
-
- return 0;
-}
-
-static int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
-{
- MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
- BUG_ON(0 == dma->nr_pages);
-
- if (dma->pages) {
- dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,
- dma->offset, dma->size);
- }
- if (dma->vaddr) {
- dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr,
- dma->nr_pages);
- }
- if (dma->bus_addr) {
- dma->sglist = vmalloc(sizeof(*dma->sglist));
- if (NULL != dma->sglist) {
- dma->sglen = 1;
- sg_dma_address(&dma->sglist[0]) = dma->bus_addr
- & PAGE_MASK;
- dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK;
- sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE;
- }
- }
- if (NULL == dma->sglist) {
- dprintk(1, "scatterlist is NULL\n");
- return -ENOMEM;
- }
- if (!dma->bus_addr) {
- dma->sglen = dma_map_sg(dev, dma->sglist,
- dma->nr_pages, dma->direction);
- if (0 == dma->sglen) {
- printk(KERN_WARNING
- "%s: videobuf_map_sg failed\n", __func__);
- vfree(dma->sglist);
- dma->sglist = NULL;
- dma->sglen = 0;
- return -ENOMEM;
- }
- }
-
- return 0;
-}
-
-int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
-{
- MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
-
- if (!dma->sglen)
- return 0;
-
- dma_unmap_sg(dev, dma->sglist, dma->nr_pages, dma->direction);
-
- vfree(dma->sglist);
- dma->sglist = NULL;
- dma->sglen = 0;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_unmap);
-
-int videobuf_dma_free(struct videobuf_dmabuf *dma)
-{
- int i;
- MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
- BUG_ON(dma->sglen);
-
- if (dma->pages) {
- unpin_user_pages_dirty_lock(dma->pages, dma->nr_pages,
- dma->direction == DMA_FROM_DEVICE);
- kfree(dma->pages);
- dma->pages = NULL;
- }
-
- if (dma->dma_addr) {
- for (i = 0; i < dma->nr_pages; i++) {
- void *addr;
-
- addr = page_address(dma->vaddr_pages[i]);
- dma_free_coherent(dma->dev, PAGE_SIZE, addr,
- dma->dma_addr[i]);
- }
- kfree(dma->dma_addr);
- dma->dma_addr = NULL;
- kfree(dma->vaddr_pages);
- dma->vaddr_pages = NULL;
- vunmap(dma->vaddr);
- dma->vaddr = NULL;
- }
-
- if (dma->bus_addr)
- dma->bus_addr = 0;
- dma->direction = DMA_NONE;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(videobuf_dma_free);
-
-/* --------------------------------------------------------------------- */
-
-static void videobuf_vm_open(struct vm_area_struct *vma)
-{
- struct videobuf_mapping *map = vma->vm_private_data;
-
- dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map,
- map->count, vma->vm_start, vma->vm_end);
-
- map->count++;
-}
-
-static void videobuf_vm_close(struct vm_area_struct *vma)
-{
- struct videobuf_mapping *map = vma->vm_private_data;
- struct videobuf_queue *q = map->q;
- struct videobuf_dma_sg_memory *mem;
- int i;
-
- dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map,
- map->count, vma->vm_start, vma->vm_end);
-
- map->count--;
- if (0 == map->count) {
- dprintk(1, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
- mem = q->bufs[i]->priv;
- if (!mem)
- continue;
-
- MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
- if (q->bufs[i]->map != map)
- continue;
- q->bufs[i]->map = NULL;
- q->bufs[i]->baddr = 0;
- q->ops->buf_release(q, q->bufs[i]);
- }
- videobuf_queue_unlock(q);
- kfree(map);
- }
-}
-
-/*
- * Get a anonymous page for the mapping. Make sure we can DMA to that
- * memory location with 32bit PCI devices (i.e. don't use highmem for
- * now ...). Bounce buffers don't work very well for the data rates
- * video capture has.
- */
-static vm_fault_t videobuf_vm_fault(struct vm_fault *vmf)
-{
- struct vm_area_struct *vma = vmf->vma;
- struct page *page;
-
- dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n",
- vmf->address, vma->vm_start, vma->vm_end);
-
- page = alloc_page(GFP_USER | __GFP_DMA32);
- if (!page)
- return VM_FAULT_OOM;
- clear_user_highpage(page, vmf->address);
- vmf->page = page;
-
- return 0;
-}
-
-static const struct vm_operations_struct videobuf_vm_ops = {
- .open = videobuf_vm_open,
- .close = videobuf_vm_close,
- .fault = videobuf_vm_fault,
-};
-
-/* ---------------------------------------------------------------------
- * SG handlers for the generic methods
- */
-
-/* Allocated area consists on 3 parts:
- struct video_buffer
- struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
- struct videobuf_dma_sg_memory
- */
-
-static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
-{
- struct videobuf_dma_sg_memory *mem;
- struct videobuf_buffer *vb;
-
- vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
- if (!vb)
- return vb;
-
- mem = vb->priv = ((char *)vb) + size;
- mem->magic = MAGIC_SG_MEM;
-
- videobuf_dma_init(&mem->dma);
-
- dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
- __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
- mem, (long)sizeof(*mem));
-
- return vb;
-}
-
-static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
-{
- struct videobuf_dma_sg_memory *mem = buf->priv;
- BUG_ON(!mem);
-
- MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
- return mem->dma.vaddr;
-}
-
-static int __videobuf_iolock(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- struct v4l2_framebuffer *fbuf)
-{
- struct videobuf_dma_sg_memory *mem = vb->priv;
- unsigned long pages;
- dma_addr_t bus;
- int err;
-
- BUG_ON(!mem);
-
- MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
- if (!mem->dma.dev)
- mem->dma.dev = q->dev;
- else
- WARN_ON(mem->dma.dev != q->dev);
-
- switch (vb->memory) {
- case V4L2_MEMORY_MMAP:
- case V4L2_MEMORY_USERPTR:
- if (0 == vb->baddr) {
- /* no userspace addr -- kernel bounce buffer */
- pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
- err = videobuf_dma_init_kernel(&mem->dma,
- DMA_FROM_DEVICE,
- pages);
- if (0 != err)
- return err;
- } else if (vb->memory == V4L2_MEMORY_USERPTR) {
- /* dma directly to userspace */
- err = videobuf_dma_init_user(&mem->dma,
- DMA_FROM_DEVICE,
- vb->baddr, vb->bsize);
- if (0 != err)
- return err;
- } else {
- /* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP
- buffers can only be called from videobuf_qbuf
- we take current->mm->mmap_lock there, to prevent
- locking inversion, so don't take it here */
-
- err = videobuf_dma_init_user_locked(&mem->dma,
- DMA_FROM_DEVICE,
- vb->baddr, vb->bsize);
- if (0 != err)
- return err;
- }
- break;
- case V4L2_MEMORY_OVERLAY:
- if (NULL == fbuf)
- return -EINVAL;
- /* FIXME: need sanity checks for vb->boff */
- /*
- * Using a double cast to avoid compiler warnings when
- * building for PAE. Compiler doesn't like direct casting
- * of a 32 bit ptr to 64 bit integer.
- */
- bus = (dma_addr_t)(unsigned long)fbuf->base + vb->boff;
- pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
- err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE,
- bus, pages);
- if (0 != err)
- return err;
- break;
- default:
- BUG();
- }
- err = videobuf_dma_map(q->dev, &mem->dma);
- if (0 != err)
- return err;
-
- return 0;
-}
-
-static int __videobuf_sync(struct videobuf_queue *q,
- struct videobuf_buffer *buf)
-{
- struct videobuf_dma_sg_memory *mem = buf->priv;
- BUG_ON(!mem || !mem->dma.sglen);
-
- MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
- MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF);
-
- dma_sync_sg_for_cpu(q->dev, mem->dma.sglist,
- mem->dma.nr_pages, mem->dma.direction);
-
- return 0;
-}
-
-static int __videobuf_mmap_mapper(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- struct vm_area_struct *vma)
-{
- struct videobuf_dma_sg_memory *mem = buf->priv;
- struct videobuf_mapping *map;
- unsigned int first, last, size = 0, i;
- int retval;
-
- retval = -EINVAL;
-
- BUG_ON(!mem);
- MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
-
- /* look for first buffer to map */
- for (first = 0; first < VIDEO_MAX_FRAME; first++) {
- if (buf == q->bufs[first]) {
- size = PAGE_ALIGN(q->bufs[first]->bsize);
- break;
- }
- }
-
- /* paranoia, should never happen since buf is always valid. */
- if (!size) {
- dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n",
- (vma->vm_pgoff << PAGE_SHIFT));
- goto done;
- }
-
- last = first;
-
- /* create mapping + update buffer list */
- retval = -ENOMEM;
- map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
- if (NULL == map)
- goto done;
-
- size = 0;
- for (i = first; i <= last; i++) {
- if (NULL == q->bufs[i])
- continue;
- q->bufs[i]->map = map;
- q->bufs[i]->baddr = vma->vm_start + size;
- size += PAGE_ALIGN(q->bufs[i]->bsize);
- }
-
- map->count = 1;
- map->q = q;
- vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
- vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
- vma->vm_private_data = map;
- dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n",
- map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last);
- retval = 0;
-
-done:
- return retval;
-}
-
-static struct videobuf_qtype_ops sg_ops = {
- .magic = MAGIC_QTYPE_OPS,
-
- .alloc_vb = __videobuf_alloc_vb,
- .iolock = __videobuf_iolock,
- .sync = __videobuf_sync,
- .mmap_mapper = __videobuf_mmap_mapper,
- .vaddr = __videobuf_to_vaddr,
-};
-
-void *videobuf_sg_alloc(size_t size)
-{
- struct videobuf_queue q;
-
- /* Required to make generic handler to call __videobuf_alloc */
- q.int_ops = &sg_ops;
-
- q.msize = size;
-
- return videobuf_alloc_vb(&q);
-}
-EXPORT_SYMBOL_GPL(videobuf_sg_alloc);
-
-void videobuf_queue_sg_init(struct videobuf_queue *q,
- const struct videobuf_queue_ops *ops,
- struct device *dev,
- spinlock_t *irqlock,
- enum v4l2_buf_type type,
- enum v4l2_field field,
- unsigned int msize,
- void *priv,
- struct mutex *ext_lock)
-{
- videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
- priv, &sg_ops, ext_lock);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);
-
diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c
deleted file mode 100644
index 9b2443720ab0..000000000000
--- a/drivers/media/v4l2-core/videobuf-vmalloc.c
+++ /dev/null
@@ -1,326 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * helper functions for vmalloc video4linux capture buffers
- *
- * The functions expect the hardware being able to scatter gather
- * (i.e. the buffers are not linear in physical memory, but fragmented
- * into PAGE_SIZE chunks). They also assume the driver does not need
- * to touch the video data.
- *
- * (c) 2007 Mauro Carvalho Chehab <mchehab@kernel.org>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pgtable.h>
-
-#include <linux/pci.h>
-#include <linux/vmalloc.h>
-#include <linux/pagemap.h>
-#include <asm/page.h>
-
-#include <media/videobuf-vmalloc.h>
-
-#define MAGIC_DMABUF 0x17760309
-#define MAGIC_VMAL_MEM 0x18221223
-
-#define MAGIC_CHECK(is, should) \
- if (unlikely((is) != (should))) { \
- printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \
- is, should); \
- BUG(); \
- }
-
-static int debug;
-module_param(debug, int, 0644);
-
-MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
-MODULE_LICENSE("GPL");
-
-#define dprintk(level, fmt, arg...) \
- if (debug >= level) \
- printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
-
-
-/***************************************************************************/
-
-static void videobuf_vm_open(struct vm_area_struct *vma)
-{
- struct videobuf_mapping *map = vma->vm_private_data;
-
- dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
- map->count, vma->vm_start, vma->vm_end);
-
- map->count++;
-}
-
-static void videobuf_vm_close(struct vm_area_struct *vma)
-{
- struct videobuf_mapping *map = vma->vm_private_data;
- struct videobuf_queue *q = map->q;
- int i;
-
- dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
- map->count, vma->vm_start, vma->vm_end);
-
- map->count--;
- if (0 == map->count) {
- struct videobuf_vmalloc_memory *mem;
-
- dprintk(1, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
-
- /* We need first to cancel streams, before unmapping */
- if (q->streaming)
- videobuf_queue_cancel(q);
-
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
-
- if (q->bufs[i]->map != map)
- continue;
-
- mem = q->bufs[i]->priv;
- if (mem) {
- /* This callback is called only if kernel has
- allocated memory and this memory is mmapped.
- In this case, memory should be freed,
- in order to do memory unmap.
- */
-
- MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
- /* vfree is not atomic - can't be
- called with IRQ's disabled
- */
- dprintk(1, "%s: buf[%d] freeing (%p)\n",
- __func__, i, mem->vaddr);
-
- vfree(mem->vaddr);
- mem->vaddr = NULL;
- }
-
- q->bufs[i]->map = NULL;
- q->bufs[i]->baddr = 0;
- }
-
- kfree(map);
-
- videobuf_queue_unlock(q);
- }
-
- return;
-}
-
-static const struct vm_operations_struct videobuf_vm_ops = {
- .open = videobuf_vm_open,
- .close = videobuf_vm_close,
-};
-
-/* ---------------------------------------------------------------------
- * vmalloc handlers for the generic methods
- */
-
-/* Allocated area consists on 3 parts:
- struct video_buffer
- struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
- struct videobuf_dma_sg_memory
- */
-
-static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
-{
- struct videobuf_vmalloc_memory *mem;
- struct videobuf_buffer *vb;
-
- vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
- if (!vb)
- return vb;
-
- mem = vb->priv = ((char *)vb) + size;
- mem->magic = MAGIC_VMAL_MEM;
-
- dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
- __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
- mem, (long)sizeof(*mem));
-
- return vb;
-}
-
-static int __videobuf_iolock(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- struct v4l2_framebuffer *fbuf)
-{
- struct videobuf_vmalloc_memory *mem = vb->priv;
- int pages;
-
- BUG_ON(!mem);
-
- MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
- switch (vb->memory) {
- case V4L2_MEMORY_MMAP:
- dprintk(1, "%s memory method MMAP\n", __func__);
-
- /* All handling should be done by __videobuf_mmap_mapper() */
- if (!mem->vaddr) {
- printk(KERN_ERR "memory is not allocated/mmapped.\n");
- return -EINVAL;
- }
- break;
- case V4L2_MEMORY_USERPTR:
- pages = PAGE_ALIGN(vb->size);
-
- dprintk(1, "%s memory method USERPTR\n", __func__);
-
- if (vb->baddr) {
- printk(KERN_ERR "USERPTR is currently not supported\n");
- return -EINVAL;
- }
-
- /* The only USERPTR currently supported is the one needed for
- * read() method.
- */
-
- mem->vaddr = vmalloc_user(pages);
- if (!mem->vaddr) {
- printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
- return -ENOMEM;
- }
- dprintk(1, "vmalloc is at addr %p (%d pages)\n",
- mem->vaddr, pages);
- break;
- case V4L2_MEMORY_OVERLAY:
- default:
- dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
-
- /* Currently, doesn't support V4L2_MEMORY_OVERLAY */
- printk(KERN_ERR "Memory method currently unsupported.\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int __videobuf_mmap_mapper(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- struct vm_area_struct *vma)
-{
- struct videobuf_vmalloc_memory *mem;
- struct videobuf_mapping *map;
- int retval, pages;
-
- dprintk(1, "%s\n", __func__);
-
- /* create mapping + update buffer list */
- map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
- if (NULL == map)
- return -ENOMEM;
-
- buf->map = map;
- map->q = q;
-
- buf->baddr = vma->vm_start;
-
- mem = buf->priv;
- BUG_ON(!mem);
- MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
- pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
- mem->vaddr = vmalloc_user(pages);
- if (!mem->vaddr) {
- printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
- goto error;
- }
- dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages);
-
- /* Try to remap memory */
- retval = remap_vmalloc_range(vma, mem->vaddr, 0);
- if (retval < 0) {
- printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
- vfree(mem->vaddr);
- goto error;
- }
-
- vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
- vma->vm_private_data = map;
-
- dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
- map, q, vma->vm_start, vma->vm_end,
- (long int)buf->bsize,
- vma->vm_pgoff, buf->i);
-
- videobuf_vm_open(vma);
-
- return 0;
-
-error:
- mem = NULL;
- kfree(map);
- return -ENOMEM;
-}
-
-static struct videobuf_qtype_ops qops = {
- .magic = MAGIC_QTYPE_OPS,
-
- .alloc_vb = __videobuf_alloc_vb,
- .iolock = __videobuf_iolock,
- .mmap_mapper = __videobuf_mmap_mapper,
- .vaddr = videobuf_to_vmalloc,
-};
-
-void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
- const struct videobuf_queue_ops *ops,
- struct device *dev,
- spinlock_t *irqlock,
- enum v4l2_buf_type type,
- enum v4l2_field field,
- unsigned int msize,
- void *priv,
- struct mutex *ext_lock)
-{
- videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
- priv, &qops, ext_lock);
-}
-EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
-
-void *videobuf_to_vmalloc(struct videobuf_buffer *buf)
-{
- struct videobuf_vmalloc_memory *mem = buf->priv;
- BUG_ON(!mem);
- MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
- return mem->vaddr;
-}
-EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
-
-void videobuf_vmalloc_free(struct videobuf_buffer *buf)
-{
- struct videobuf_vmalloc_memory *mem = buf->priv;
-
- /* mmapped memory can't be freed here, otherwise mmapped region
- would be released, while still needed. In this case, the memory
- release should happen inside videobuf_vm_close().
- So, it should free memory only if the memory were allocated for
- read() operation.
- */
- if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
- return;
-
- if (!mem)
- return;
-
- MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
-
- vfree(mem->vaddr);
- mem->vaddr = NULL;
-
- return;
-}
-EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);
-