diff options
Diffstat (limited to 'drivers/media/v4l2-core')
29 files changed, 3040 insertions, 4088 deletions
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 348559bc2468..d50ccac9733c 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -74,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 41d91bd10cf2..329f0eadce99 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -25,17 +25,14 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o # (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_DMA_CONTIG) += videobuf-dma-contig.o -obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o -obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o -obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o - obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEO_DEV) += v4l2-dv-timings.o videodev.o diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index b16b5f4cb91e..ee884a8221fb 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -28,22 +28,22 @@ 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) @@ -55,131 +55,142 @@ static int v4l2_async_nf_call_complete(struct v4l2_async_notifier *n) } static void v4l2_async_nf_call_destroy(struct v4l2_async_notifier *n, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asc) { if (!n->ops || !n->ops->destroy) return; - n->ops->destroy(asd); + 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 struct device *notifier_dev(struct v4l2_async_notifier *notifier) +{ + if (notifier->sd) + return notifier->sd->dev; + + if (notifier->v4l2_dev) + return notifier->v4l2_dev->dev; + + return NULL; +} + static bool match_fwnode_one(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct fwnode_handle *sd_fwnode, - struct v4l2_async_subdev *asd) + struct v4l2_async_match_desc *match) { - struct fwnode_handle *other_fwnode; - struct fwnode_handle *dev_fwnode; - bool asd_fwnode_is_ep; - bool sd_fwnode_is_ep; - struct device *dev; + struct fwnode_handle *asd_dev_fwnode; + bool ret; - /* - * 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; + dev_dbg(notifier_dev(notifier), + "v4l2-async: fwnode match: need %pfw, trying %pfw\n", + sd_fwnode, match->fwnode); - /* - * 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); + 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; } - fwnode_handle_put(dev_fwnode); + asd_dev_fwnode = fwnode_graph_get_port_parent(match->fwnode); - if (dev_fwnode != other_fwnode) - return false; + ret = sd_fwnode == asd_dev_fwnode; - /* - * 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); - } + fwnode_handle_put(asd_dev_fwnode); - return true; + 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_subdev *asd) + struct v4l2_subdev *sd, + struct v4l2_async_match_desc *match) { - if (match_fwnode_one(notifier, sd, sd->fwnode, asd)) + 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; + } + + if (match_fwnode_one(notifier, sd, sd->fwnode, match)) return true; /* Also check the secondary fwnode. */ if (IS_ERR_OR_NULL(sd->fwnode->secondary)) return false; - return match_fwnode_one(notifier, sd, sd->fwnode->secondary, asd); + dev_dbg(notifier_dev(notifier), + "v4l2-async: trying secondary fwnode match\n"); + + 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, ¬ifier->waiting, list) { + list_for_each_entry(asc, ¬ifier->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: @@ -189,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; } @@ -224,7 +233,7 @@ v4l2_async_find_subdev_notifier(struct v4l2_subdev *sd) { struct v4l2_async_notifier *n; - list_for_each_entry(n, ¬ifier_list, list) + list_for_each_entry(n, ¬ifier_list, notifier_entry) if (n->sd == sd) return n; @@ -247,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(¬ifier->waiting)) + if (!list_empty(¬ifier->waiting_list)) return false; - list_for_each_entry(sd, ¬ifier->done, async_list) { + list_for_each_entry(asc, ¬ifier->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)) @@ -271,22 +280,33 @@ 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(¬ifier->waiting)) + if (!list_empty(¬ifier->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); } @@ -296,59 +316,78 @@ 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) { - struct media_link *link = NULL; - #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; - link = media_create_ancillary_link(&n->sd->entity, &sd->entity); + 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; + } -#endif + 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; } - /* - * 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) { - v4l2_async_nf_call_unbind(notifier, sd, asd); - v4l2_device_unregister_subdev(sd); - return ret; + 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; + } } - /* Remove from the waiting list */ - list_del(&asd->list); - sd->asd = asd; - sd->notifier = notifier; + list_add(&asc->asc_subdev_entry, &sd->asc_list); + asc->sd = sd; - /* Move from the global subdevice list to notifier's done */ - list_move(&sd->async_list, ¬ifier->done); + /* Move from the waiting list to notifier's done */ + list_move(&asc->asc_entry, ¬ifier->done_list); + + 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. @@ -365,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. */ @@ -378,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; @@ -403,37 +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, ¬ifier->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, - bool readd) +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, ¬ifier->done, async_list) { + list_for_each_entry_safe(asc, asc_tmp, ¬ifier->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, true); - - v4l2_async_nf_call_unbind(notifier, sd, sd->asd); - if (readd) - list_add_tail(&sd->asd->list, ¬ifier->waiting); - v4l2_async_cleanup(sd); + v4l2_async_nf_unbind_all_subdevs(subdev_notifier); - list_move(&sd->async_list, &subdev_list); + v4l2_async_unbind_subdev_one(notifier, asc); } notifier->parent = NULL; @@ -441,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, ¬ifier->waiting, list) - if (asd_equal(asd, asd_y)) + list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry) + if (v4l2_async_match_equal(&asc->match, match)) return true; - list_for_each_entry(sd, ¬ifier->done, async_list) { - if (WARN_ON(!sd->asd)) - continue; - - if (asd_equal(asd, sd->asd)) + list_for_each_entry(asc, ¬ifier->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[] = { + ¬ifier->waiting_list, + ¬ifier->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, ¬ifier->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, ¬ifier_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, ¬ifier_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; - - if (!asd) - return -EINVAL; + struct device *dev = notifier_dev(notifier); - 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(¬ifier->asd_list); + INIT_LIST_HEAD(¬ifier->waiting_list); + INIT_LIST_HEAD(¬ifier->done_list); + INIT_LIST_HEAD(¬ifier->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(¬ifier->waiting_list); + INIT_LIST_HEAD(¬ifier->done_list); + INIT_LIST_HEAD(¬ifier->notifier_entry); + notifier->sd = sd; +} +EXPORT_SYMBOL_GPL(v4l2_async_subdev_nf_init); - INIT_LIST_HEAD(¬ifier->waiting); - INIT_LIST_HEAD(¬ifier->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, ¬ifier->asd_list, asd_list) { - ret = v4l2_async_nf_asd_valid(notifier, asd, i++); + list_for_each_entry(asc, ¬ifier->waiting_list, asc_entry) { + ret = v4l2_async_nf_match_valid(notifier, &asc->match); if (ret) goto err_unlock; - - list_add_tail(&asd->list, ¬ifier->waiting); } ret = v4l2_async_nf_try_all_subdevs(notifier); @@ -552,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(¬ifier->list, ¬ifier_list); + list_add(¬ifier->notifier_entry, ¬ifier_list); mutex_unlock(&list_lock); @@ -562,7 +617,7 @@ err_unbind: /* * On failure, unbind all sub-devices registered through this notifier. */ - v4l2_async_nf_unbind_all_subdevs(notifier, false); + v4l2_async_nf_unbind_all_subdevs(notifier); err_unlock: mutex_unlock(&list_lock); @@ -570,54 +625,24 @@ 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) { if (!notifier || (!notifier->v4l2_dev && !notifier->sd)) return; - v4l2_async_nf_unbind_all_subdevs(notifier, false); + v4l2_async_nf_unbind_all_subdevs(notifier); - notifier->sd = NULL; - notifier->v4l2_dev = NULL; - - list_del(¬ifier->list); + list_del_init(¬ifier->notifier_entry); } void v4l2_async_nf_unregister(struct v4l2_async_notifier *notifier) @@ -632,24 +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, ¬ifier->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(¬ifier->done_list)); + + list_for_each_entry_safe(asc, tmp, ¬ifier->waiting_list, asc_entry) { + list_del(&asc->asc_entry); + v4l2_async_nf_call_destroy(notifier, asc); + + if (asc->match.type == V4L2_ASYNC_MATCH_TYPE_FWNODE) + fwnode_handle_put(asc->match.fwnode); - list_del(&asd->asd_list); - v4l2_async_nf_call_destroy(notifier, asd); - kfree(asd); + kfree(asc); } + + notifier->sd = NULL; + notifier->v4l2_dev = NULL; } void v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier) @@ -662,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, ¬ifier->waiting_list); - list_add_tail(&asd->asd_list, ¬ifier->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_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, ¬ifier_list, list) { + list_for_each_entry(notifier, ¬ifier_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; @@ -810,20 +851,23 @@ err_unbind: */ subdev_notifier = v4l2_async_find_subdev_notifier(sd); if (subdev_notifier) - v4l2_async_nf_unbind_all_subdevs(subdev_notifier, false); + 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; @@ -836,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, ¬ifier->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) : @@ -889,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, ¬ifier_list, list) { + list_for_each_entry(notif, ¬ifier_list, notifier_entry) { seq_printf(s, "%s:\n", v4l2_async_nf_name(notif)); - list_for_each_entry(asd, ¬if->waiting, list) - print_waiting_subdev(s, asd); + list_for_each_entry(asc, ¬if->waiting_list, asc_entry) + print_waiting_match(s, &asc->match); } mutex_unlock(&list_lock); @@ -928,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 bee1535b04d3..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; @@ -250,24 +261,38 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .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 }, .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 }, .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 }, @@ -301,6 +326,12 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .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 }, .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 }, @@ -310,6 +341,10 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .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 }, @@ -322,6 +357,28 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .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; @@ -346,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) { @@ -381,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] / info->bpp_div[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) / info->bpp_div[i]; - } + 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) / info->bpp_div[i]; - 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; @@ -435,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] / info->bpp_div[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) / info->bpp_div[i]; - } + 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; @@ -483,16 +541,69 @@ 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 @@ -578,3 +689,120 @@ u32 v4l2_fraction_to_interval(u32 numerator, u32 denominator) 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 f3bed37859a2..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. @@ -116,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 { @@ -125,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, @@ -175,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); } @@ -221,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); @@ -664,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; @@ -686,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); } diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index 002ea6588edf..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 @@ -94,6 +94,22 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) 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 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) { @@ -229,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", @@ -259,6 +275,15 @@ 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) @@ -368,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; } @@ -389,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); @@ -432,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++) @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, 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 @@ -564,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; } @@ -753,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; @@ -770,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); @@ -811,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) + ret = set_ctrl(fh, ctrl, 0); if (!ret) - cur_to_user(c, ctrl); + ret = cur_to_user(c, ctrl); v4l2_ctrl_unlock(ctrl); return ret; } @@ -1052,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); @@ -1113,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; + to->id = from->id; + to->type = from->type; + to->flags = from->flags; + strscpy(to->name, from->name, sizeof(to->name)); - rc = v4l2_query_ext_ctrl(hdl, &qec); - if (rc) - return rc; - - 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); @@ -1179,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) { @@ -1197,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); @@ -1295,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 a662fb60f73f..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> @@ -160,7 +160,13 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, 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; @@ -182,29 +188,66 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, } } -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_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; - if (from_idx >= tot_elems) + 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: + 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, ' ', ctrl->minimum); - ptr.p_char[offset + ctrl->minimum] = '\0'; + memset(ptr.p_char + offset, ' ', value); + ptr.p_char[offset + value] = '\0'; } break; case V4L2_CTRL_TYPE_INTEGER64: - if (ctrl->default_value) { + if (value) { for (i = from_idx; i < tot_elems; i++) - ptr.p_s64[i] = ctrl->default_value; + ptr.p_s64[i] = value; } else { memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64)); } @@ -214,9 +257,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BOOLEAN: - if (ctrl->default_value) { + if (value) { for (i = from_idx; i < tot_elems; i++) - ptr.p_s32[i] = ctrl->default_value; + ptr.p_s32[i] = value; } else { memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32)); } @@ -226,32 +269,61 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32)); break; case V4L2_CTRL_TYPE_U8: - memset(ptr.p_u8 + from_idx, ctrl->default_value, elems); + memset(ptr.p_u8 + from_idx, value, elems); break; case V4L2_CTRL_TYPE_U16: - if (ctrl->default_value) { + if (value) { for (i = from_idx; i < tot_elems; i++) - ptr.p_u16[i] = ctrl->default_value; + ptr.p_u16[i] = value; } else { memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16)); } break; case V4L2_CTRL_TYPE_U32: - if (ctrl->default_value) { + if (value) { for (i = from_idx; i < tot_elems; i++) - ptr.p_u32[i] = ctrl->default_value; + ptr.p_u32[i] = value; } else { memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32)); } break; default: - for (i = from_idx; i < tot_elems; i++) - std_init_compound(ctrl, i, 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; } } + +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; @@ -295,6 +367,9 @@ void v4l2_ctrl_type_op_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; @@ -367,7 +442,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl) 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; @@ -754,39 +833,114 @@ static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f) 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) { - 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; + 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; - if (s->seq_profile == 1 && s->flags & V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME) + /* 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; - /* reserved */ + /* 2. Profile range */ if (s->seq_profile > 2) return -EINVAL; - /* TODO: PROFILES */ + /* 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; } @@ -812,6 +966,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, 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; @@ -894,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; @@ -1169,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; } @@ -1282,6 +1443,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate); static const struct v4l2_ctrl_type_ops std_type_ops = { .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, }; @@ -1504,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: @@ -1548,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); @@ -1578,9 +1745,10 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) 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); @@ -1753,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; @@ -1868,12 +2039,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, 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; } + 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 || id >= V4L2_CID_PRIVATE_BASE || @@ -1882,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); @@ -1923,6 +2104,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, 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) { @@ -1988,6 +2173,22 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, memcpy(ctrl->p_def.p, p_def.p_const, elem_size); } + 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); @@ -2038,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; @@ -2063,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); @@ -2096,7 +2299,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, mask, def, NULL, 0, - flags, qmenu, qmenu_int, ptr_null, NULL); + flags, qmenu, qmenu_int, ptr_null, ptr_null, + ptr_null, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -2128,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); @@ -2136,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; @@ -2150,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); @@ -2174,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); @@ -2555,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 8696eb1cdd61..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> @@ -970,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"; @@ -1507,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; diff --git a/drivers/media/v4l2-core/v4l2-ctrls-priv.h b/drivers/media/v4l2-core/v4l2-ctrls-priv.h index aba6176fab6c..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_ diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c index c637049d7a2b..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 diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index f81279492682..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, }; /** @@ -557,6 +567,7 @@ static void determine_valid_ioctls(struct video_device *vdev) 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); @@ -570,13 +581,13 @@ static void determine_valid_ioctls(struct video_device *vdev) 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) + if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl) __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) + 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_ctrl || ops->vidioc_s_ext_ctrls) + 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); @@ -642,11 +653,13 @@ 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) { + 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) + 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); @@ -720,6 +733,9 @@ 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) { @@ -779,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); @@ -901,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); @@ -1033,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; @@ -1090,12 +1126,21 @@ 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, @@ -1200,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 942d0005c55e..346d1b0e10ce 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -481,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; @@ -625,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); @@ -699,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; @@ -755,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); @@ -765,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); @@ -783,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); @@ -1006,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 @@ -1154,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-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 049c2f2001ea..cb153ce42c45 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -127,7 +127,7 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, { 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_MBUS_CSI2_MAX_DATA_LANES]; u32 clock_lane = 0; @@ -197,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); @@ -250,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; @@ -568,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); @@ -798,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 @@ -918,10 +872,10 @@ static int v4l2_fwnode_reference_parse(struct device *dev, !(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 */ @@ -1223,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); @@ -1266,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; @@ -1302,7 +1258,7 @@ 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) @@ -1312,7 +1268,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) 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; @@ -1336,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-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 01ba27f2ef87..98512ea4cc5b 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -310,8 +310,8 @@ 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; - pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, chromakey=0x%08x, 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->global_alpha); break; @@ -343,8 +343,9 @@ static void v4l_print_format(const void *arg, bool write_only) case V4L2_BUF_TYPE_META_OUTPUT: meta = &p->fmt.meta; pixelformat = meta->dataformat; - pr_cont(", dataformat=%p4cc, buffersize=%u\n", - &pixelformat, meta->buffersize); + pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n", + &pixelformat, meta->buffersize, meta->width, + meta->height, meta->bytesperline); break; } } @@ -483,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; @@ -580,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); } @@ -593,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) @@ -884,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; @@ -1078,8 +1089,8 @@ static void v4l_sanitize_format(struct v4l2_format *fmt) } } -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); @@ -1092,7 +1103,7 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, media_set_bus_info(cap->bus_info, sizeof(cap->bus_info), vfd->dev_parent); - ret = ops->vidioc_querycap(file, fh, cap); + ret = ops->vidioc_querycap(file, NULL, cap); /* * Drivers must not change device_caps, so check for this and @@ -1112,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); @@ -1122,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); @@ -1135,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; @@ -1151,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; @@ -1176,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; @@ -1213,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; @@ -1239,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) @@ -1298,6 +1307,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) 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; @@ -1312,8 +1323,11 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) 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; @@ -1347,8 +1361,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) 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; @@ -1395,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; @@ -1403,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; @@ -1411,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; @@ -1441,10 +1461,16 @@ 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; @@ -1452,6 +1478,16 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) 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 */ @@ -1508,6 +1544,19 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) 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; @@ -1519,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; @@ -1555,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: @@ -1572,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) @@ -1615,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); @@ -1632,50 +1697,50 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, 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); @@ -1695,7 +1760,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_s_fmt_vid_cap)) break; memset_after(p, 0, fmt.pix); - ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg); + 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) @@ -1708,7 +1773,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, for (i = 0; i < p->fmt.pix_mp.num_planes; i++) memset_after(&p->fmt.pix_mp.plane_fmt[i], 0, bytesperline); - return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg); + 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; @@ -1716,22 +1781,22 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, p->fmt.win.clips = NULL; p->fmt.win.clipcount = 0; p->fmt.win.bitmap = NULL; - return ops->vidioc_s_fmt_vid_overlay(file, fh, arg); + 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; memset_after(p, 0, fmt.vbi.flags); - return ops->vidioc_s_fmt_vbi_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.sliced.io_size); - return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.pix); - ret = ops->vidioc_s_fmt_vid_out(file, fh, arg); + 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; @@ -1742,7 +1807,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, for (i = 0; i < p->fmt.pix_mp.num_planes; i++) memset_after(&p->fmt.pix_mp.plane_fmt[i], 0, bytesperline); - return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg); + 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; @@ -1750,43 +1815,43 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, p->fmt.win.clips = NULL; p->fmt.win.clipcount = 0; p->fmt.win.bitmap = NULL; - return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg); + 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; memset_after(p, 0, fmt.vbi.flags); - return ops->vidioc_s_fmt_vbi_out(file, fh, arg); + 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; memset_after(p, 0, fmt.sliced.io_size); - return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg); + 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; memset_after(p, 0, fmt.sdr.buffersize); - return ops->vidioc_s_fmt_sdr_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.sdr.buffersize); - return ops->vidioc_s_fmt_sdr_out(file, fh, arg); + 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; memset_after(p, 0, fmt.meta); - return ops->vidioc_s_fmt_meta_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.meta); - return ops->vidioc_s_fmt_meta_out(file, fh, arg); + 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); @@ -1803,7 +1868,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!ops->vidioc_try_fmt_vid_cap)) break; memset_after(p, 0, fmt.pix); - ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg); + 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) @@ -1816,7 +1881,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, for (i = 0; i < p->fmt.pix_mp.num_planes; i++) memset_after(&p->fmt.pix_mp.plane_fmt[i], 0, bytesperline); - return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg); + 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; @@ -1824,22 +1889,22 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, p->fmt.win.clips = NULL; p->fmt.win.clipcount = 0; p->fmt.win.bitmap = NULL; - return ops->vidioc_try_fmt_vid_overlay(file, fh, arg); + 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; memset_after(p, 0, fmt.vbi.flags); - return ops->vidioc_try_fmt_vbi_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.sliced.io_size); - return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.pix); - ret = ops->vidioc_try_fmt_vid_out(file, fh, arg); + 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; @@ -1850,7 +1915,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, for (i = 0; i < p->fmt.pix_mp.num_planes; i++) memset_after(&p->fmt.pix_mp.plane_fmt[i], 0, bytesperline); - return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg); + 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; @@ -1858,55 +1923,55 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, p->fmt.win.clips = NULL; p->fmt.win.clipcount = 0; p->fmt.win.bitmap = NULL; - return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg); + 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; memset_after(p, 0, fmt.vbi.flags); - return ops->vidioc_try_fmt_vbi_out(file, fh, arg); + 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; memset_after(p, 0, fmt.sliced.io_size); - return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg); + 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; memset_after(p, 0, fmt.sdr.buffersize); - return ops->vidioc_try_fmt_sdr_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.sdr.buffersize); - return ops->vidioc_try_fmt_sdr_out(file, fh, arg); + 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; memset_after(p, 0, fmt.meta); - return ops->vidioc_try_fmt_meta_cap(file, fh, arg); + 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; memset_after(p, 0, fmt.meta); - return ops->vidioc_try_fmt_meta_out(file, fh, arg); + 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; @@ -1914,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; @@ -1932,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; @@ -1945,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; @@ -1960,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; @@ -1974,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; @@ -1997,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; @@ -2009,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; @@ -2024,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; @@ -2046,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; @@ -2068,27 +2133,28 @@ 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 *fh, void *arg) +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, fh, p); + 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); @@ -2097,39 +2163,44 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, memset_after(p, 0, flags); - return ops->vidioc_reqbufs(file, fh, p); + p->capabilities = 0; + if (is_valid_ioctl(vfd, VIDIOC_REMOVE_BUFS)) + p->capabilities = V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS; + + 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); @@ -2140,7 +2211,11 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, 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) @@ -2150,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; @@ -2169,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); @@ -2202,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; @@ -2270,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; @@ -2281,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; @@ -2290,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; @@ -2305,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; @@ -2317,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) @@ -2340,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) @@ -2361,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) @@ -2382,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; } /* @@ -2395,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; @@ -2405,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; @@ -2421,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; @@ -2448,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) @@ -2456,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; @@ -2478,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; @@ -2506,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); /* @@ -2528,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; @@ -2539,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; @@ -2547,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; @@ -2556,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); @@ -2564,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; @@ -2584,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; @@ -2592,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; @@ -2612,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; @@ -2620,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); @@ -2636,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; @@ -2662,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); @@ -2692,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; @@ -2714,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; } @@ -2726,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; @@ -2745,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; @@ -2763,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); }; @@ -2784,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) \ @@ -2907,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) @@ -2948,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; } @@ -2981,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; @@ -2992,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 @@ -3025,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; @@ -3043,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); } @@ -3144,13 +3229,13 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, case VIDIOC_SUBDEV_S_ROUTING: { struct v4l2_subdev_routing *routing = parg; - if (routing->num_routes > 256) + 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->num_routes; + * routing->len_routes; ret = 1; break; } @@ -3159,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) { @@ -3180,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, @@ -3340,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 */ @@ -3404,11 +3490,14 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, * 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_G_ROUTING needs to return results to applications even - * in case of failure, but it is not defined here as part of the + * 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 (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING) + 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) { 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 94435a7b6816..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 @@ -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 52d349e72b8c..937d358697e1 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -329,7 +329,7 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, if (!(sink->flags & MEDIA_PAD_FL_SINK)) return -EINVAL; - 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; @@ -337,12 +337,18 @@ 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, @@ -353,8 +359,12 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, 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, @@ -367,8 +377,13 @@ 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(src_sd->dev, "creating link %s:%d -> %s:%d\n", src_sd->entity.name, src_idx, diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 0cc30397fbad..fec93c1a9231 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -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; } @@ -948,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); @@ -967,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; } @@ -1001,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; @@ -1084,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; } @@ -1280,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); @@ -1291,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; @@ -1362,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); } @@ -1371,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); } @@ -1389,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); } @@ -1398,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); } @@ -1407,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); } @@ -1416,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); } @@ -1425,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); } @@ -1434,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) @@ -1451,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) @@ -1518,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); } @@ -1527,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) @@ -1548,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; @@ -1593,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); } @@ -1601,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.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2ec179cd1264..25e66bf18f5f 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/overflow.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/types.h> #include <linux/version.h> #include <linux/videodev2.h> @@ -25,6 +26,30 @@ #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 @@ -85,8 +110,7 @@ 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; + v4l2_fh_add(&subdev_fh->vfh, file); if (sd->v4l2_dev->mdev && sd->entity.graph_obj.mdev->dev) { struct module *owner; @@ -109,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); @@ -121,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; } @@ -147,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 && @@ -176,7 +216,7 @@ static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - if (!v4l2_subdev_state_get_stream_format(state, pad, stream)) + if (!v4l2_subdev_state_get_format(state, pad, stream)) return -EINVAL; return 0; #else @@ -200,9 +240,6 @@ static inline int check_format(struct v4l2_subdev *sd, if (!format) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - format->stream = 0; - return check_which(format->which) ? : check_pad(sd, format->pad) ? : check_state(sd, state, format->which, format->pad, format->stream); } @@ -230,9 +267,6 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd, if (!code) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - code->stream = 0; - return check_which(code->which) ? : check_pad(sd, code->pad) ? : check_state(sd, state, code->which, code->pad, code->stream) ? : sd->ops->pad->enum_mbus_code(sd, state, code); @@ -245,37 +279,11 @@ static int call_enum_frame_size(struct v4l2_subdev *sd, if (!fse) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - fse->stream = 0; - return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : 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) @@ -283,9 +291,6 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd, if (!fie) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - fie->stream = 0; - return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : check_state(sd, state, fie->which, fie->pad, fie->stream) ? : sd->ops->pad->enum_frame_interval(sd, state, fie); @@ -298,9 +303,6 @@ static inline int check_selection(struct v4l2_subdev *sd, if (!sel) return -EINVAL; - if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) - sel->stream = 0; - return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : check_state(sd, state, sel->which, sel->pad, sel->stream); } @@ -321,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) { @@ -343,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) { @@ -366,6 +466,8 @@ 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); } @@ -374,20 +476,28 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) { int ret; -#if IS_REACHABLE(CONFIG_LEDS_CLASS) - if (!IS_ERR_OR_NULL(sd->privacy_led)) { - if (enable) - led_set_brightness(sd->privacy_led, - sd->privacy_led->max_brightness); - else - led_set_brightness(sd->privacy_led, 0); - } -#endif + /* + * 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); - return 0; + 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; @@ -442,16 +552,20 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .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, }; 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, }; @@ -493,6 +607,17 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh, 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; @@ -509,7 +634,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, { 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; @@ -517,6 +642,13 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, 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; @@ -588,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: @@ -672,6 +819,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, 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( @@ -696,6 +844,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, 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; @@ -736,20 +885,20 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, 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) - return -EPERM; - 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: { @@ -813,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); @@ -863,14 +1012,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, krouting = &state->routing; - if (routing->num_routes < krouting->num_routes) { - routing->num_routes = krouting->num_routes; - return -ENOSPC; - } - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, krouting->routes, - krouting->num_routes * sizeof(*krouting->routes)); + min(krouting->num_routes, routing->len_routes) * + sizeof(*krouting->routes)); routing->num_routes = krouting->num_routes; return 0; @@ -881,6 +1026,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *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) @@ -892,6 +1038,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, 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) { @@ -915,13 +1064,51 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, 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; - return v4l2_subdev_call(sd, pad, set_routing, state, + 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: { @@ -944,7 +1131,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS; /* Filter out unsupported capabilities */ - client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS; + client_cap->capabilities &= (V4L2_SUBDEV_CLIENT_CAP_STREAMS | + V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH); subdev_fh->client_caps = client_cap->capabilities; @@ -969,7 +1157,7 @@ static long subdev_do_ioctl_lock(struct file *file, unsigned int cmd, void *arg) if (video_is_registered(vdev)) { 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); struct v4l2_subdev_state *state; @@ -1026,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; @@ -1139,14 +1327,6 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream, struct v4l2_subdev *sd; int ret; - if (!is_media_entity_v4l2_subdev(pad->entity)) { - 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); - - return -EINVAL; - } - sd = media_entity_to_v4l2_subdev(pad->entity); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -1321,16 +1501,53 @@ int v4l2_subdev_link_validate(struct media_link *link) bool states_locked; int ret; - if (!is_media_entity_v4l2_subdev(link->sink->entity) || - !is_media_entity_v4l2_subdev(link->source->entity)) { - pr_warn_once("%s of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", - !is_media_entity_v4l2_subdev(link->sink->entity) ? - "sink" : "source", - link->source->entity->name, link->source->index, - link->sink->entity->name, link->sink->index); - 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; + + /* + * 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; + } + + /* + * 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; + + return source->ops->link_validate(link); } + /* + * 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); @@ -1339,17 +1556,13 @@ int v4l2_subdev_link_validate(struct media_link *link) states_locked = sink_state && source_state; - if (states_locked) { - v4l2_subdev_lock_state(sink_state); - v4l2_subdev_lock_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_state(sink_state); - v4l2_subdev_unlock_state(source_state); - } + if (states_locked) + v4l2_subdev_unlock_states(sink_state, source_state); return ret; } @@ -1403,6 +1616,8 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, 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, @@ -1413,16 +1628,18 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, } } - /* - * There can be no race at this point, but we lock the state anyway to - * satisfy lockdep checks. - */ - v4l2_subdev_lock_state(state); - ret = v4l2_subdev_call(sd, pad, init_cfg, state); - v4l2_subdev_unlock_state(state); + 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 < 0 && ret != -ENOIOCTLCMD) - goto err; + if (ret) + goto err; + } return state; @@ -1454,6 +1671,36 @@ 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)) @@ -1467,11 +1714,162 @@ 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 @@ -1528,14 +1926,7 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { struct v4l2_mbus_framefmt *fmt; - if (sd->flags & V4L2_SUBDEV_FL_STREAMS) - fmt = v4l2_subdev_state_get_stream_format(state, format->pad, - format->stream); - else if (format->pad < sd->entity.num_pads && format->stream == 0) - fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); - else - fmt = NULL; - + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); if (!fmt) return -EINVAL; @@ -1545,6 +1936,22 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, } 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) @@ -1605,7 +2012,7 @@ EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route); int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, - struct v4l2_subdev_krouting *routing, + const struct v4l2_subdev_krouting *routing, const struct v4l2_mbus_framefmt *fmt) { struct v4l2_subdev_stream_configs *stream_configs; @@ -1625,69 +2032,6 @@ int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); -struct v4l2_mbus_framefmt * -v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - 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_stream_format); - -struct v4l2_rect * -v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - 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_stream_crop); - -struct v4l2_rect * -v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, - unsigned int pad, u32 stream) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - 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_stream_compose); - int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, u32 pad, u32 stream, u32 *other_pad, u32 *other_stream) @@ -1732,8 +2076,7 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, if (ret) return NULL; - return v4l2_subdev_state_get_stream_format(state, other_pad, - other_stream); + return v4l2_subdev_state_get_format(state, other_pad, other_stream); } EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format); @@ -1902,43 +2245,62 @@ out: } EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); -static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; - int ret; +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; + } - /* - * The subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; + *found_streams = 0; + *enabled_streams = 0; - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } + 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; - if (sd->enabled_streams & streams_mask) { - dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n", - streams_mask, sd->entity.name, pad); - return -EALREADY; + *found_streams |= BIT_ULL(cfg->stream); + if (cfg->enabled) + *enabled_streams |= BIT_ULL(cfg->stream); } - /* Start streaming when the first streams are enabled. */ - if (!sd->enabled_streams) { - ret = v4l2_subdev_call(sd, video, s_stream, 1); - if (ret) - return ret; + 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; } - sd->enabled_streams |= streams_mask; + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; - return 0; + if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) + cfg->enabled = enabled; + } } int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, @@ -1946,44 +2308,47 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; - u64 found_streams = 0; - unsigned int i; + 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. */ - if (!sd->ops->pad || !sd->ops->pad->enable_streams) - return v4l2_subdev_enable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + 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. */ - for (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))) - continue; - - found_streams |= BIT_ULL(cfg->stream); - - if (cfg->enabled) { - dev_dbg(dev, "stream %u already enabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + 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", @@ -1992,110 +2357,99 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } - /* Call the .enable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, - streams_mask); - if (ret) + 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; + } - /* Mark the streams as enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; + already_streaming = v4l2_subdev_is_streaming(sd); - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = true; + 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; } -done: - v4l2_subdev_unlock_state(state); - - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams); + if (ret) { + dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad, + streams_mask, ret); + goto done; + } -static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; - int ret; + /* Mark the streams as enabled. */ + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); /* - * If the subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. + * 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 (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; - - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } - - if ((sd->enabled_streams & streams_mask) != streams_mask) { - dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n", - streams_mask, sd->entity.name, pad); - return -EALREADY; - } - - /* Stop streaming when the last streams are disabled. */ - if (!(sd->enabled_streams & ~streams_mask)) { - ret = v4l2_subdev_call(sd, video, s_stream, 0); - if (ret) - return ret; - } + if (!use_s_stream && !already_streaming) + v4l2_subdev_enable_privacy_led(sd); - sd->enabled_streams &= ~streams_mask; +done: + if (!use_s_stream) + v4l2_subdev_unlock_state(state); - return 0; + 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 found_streams = 0; - unsigned int i; + 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. */ - if (!sd->ops->pad || !sd->ops->pad->disable_streams) - return v4l2_subdev_disable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + 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. */ - for (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))) - continue; - - found_streams |= BIT_ULL(cfg->stream); - - if (!cfg->enabled) { - dev_dbg(dev, "stream %u already disabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + 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", @@ -2104,23 +2458,41 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } - /* Call the .disable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, - streams_mask); - if (ret) + 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; + } - /* Mark the streams as disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; + 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 (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = false; + 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: - v4l2_subdev_unlock_state(state); + if (!use_s_stream) { + if (!v4l2_subdev_is_streaming(sd)) + v4l2_subdev_disable_privacy_led(sd); + + v4l2_subdev_unlock_state(state); + } return ret; } @@ -2149,15 +2521,24 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) if (WARN_ON(pad_index == -1)) return -EINVAL; - /* - * As there's a single source pad, just collect all the source streams. - */ - state = v4l2_subdev_lock_and_get_active_state(sd); + 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); + for_each_active_route(&state->routing, route) + source_mask |= BIT_ULL(route->source_stream); - v4l2_subdev_unlock_state(state); + 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); @@ -2182,6 +2563,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) 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; @@ -2198,10 +2580,35 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, } 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-led"); + 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"); 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 4c2ec7a0d804..000000000000 --- a/drivers/media/v4l2-core/videobuf-dma-contig.c +++ /dev/null @@ -1,402 +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) -{ - mem->size = size; - mem->vaddr = dma_alloc_coherent(dev, mem->size, &mem->dma_handle, - GFP_KERNEL); - 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))) - 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))) - goto error; - - /* 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 dma_mmap_coherent(). - */ - vma->vm_pgoff = 0; - retval = dma_mmap_coherent(q->dev, vma, mem->vaddr, 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; - vm_flags_set(vma, 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 405b89ea1054..000000000000 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ /dev/null @@ -1,681 +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 int gup_flags = FOLL_LONGTERM; - unsigned long first, last; - int err; - - dma->direction = direction; - switch (dma->direction) { - case DMA_FROM_DEVICE: - gup_flags |= FOLL_WRITE; - break; - case DMA_TO_DEVICE: - 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; - - 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, gup_flags, - dma->pages); - - 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; - /* using shared anonymous pages */ - vm_flags_mod(vma, VM_DONTEXPAND | VM_DONTDUMP, VM_IO); - 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 85c7090606d6..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; - vm_flags_set(vma, 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); - |
