From cde9070d9970eef1f7ec2360586c802a16230ad8 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 07:43:50 +0000 Subject: [PATCH] rtl88x2CE_WiFi_linux driver --- kernel/drivers/media/v4l2-core/v4l2-subdev.c | 461 +++++++++++++++++++++++++++++++++++++++++---------------- 1 files changed, 330 insertions(+), 131 deletions(-) diff --git a/kernel/drivers/media/v4l2-core/v4l2-subdev.c b/kernel/drivers/media/v4l2-core/v4l2-subdev.c index 8abea17..4a95991 100644 --- a/kernel/drivers/media/v4l2-core/v4l2-subdev.c +++ b/kernel/drivers/media/v4l2-core/v4l2-subdev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * V4L2 sub-device * @@ -5,48 +6,42 @@ * * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> * Sakari Ailus <sakari.ailus@iki.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/ioctl.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/videodev2.h> #include <linux/export.h> +#include <linux/version.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-fh.h> #include <media/v4l2-event.h> +#ifndef __GENKSYMS__ +#include <trace/hooks/v4l2core.h> +#endif +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) if (sd->entity.num_pads) { fh->pad = v4l2_subdev_alloc_pad_config(sd); if (fh->pad == NULL) return -ENOMEM; } -#endif + return 0; } static void subdev_fh_free(struct v4l2_subdev_fh *fh) { -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) v4l2_subdev_free_pad_config(fh->pad); fh->pad = NULL; -#endif } static int subdev_open(struct file *file) @@ -54,9 +49,6 @@ struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct v4l2_subdev_fh *subdev_fh; -#if defined(CONFIG_MEDIA_CONTROLLER) - struct media_entity *entity = NULL; -#endif int ret; subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL); @@ -73,12 +65,15 @@ v4l2_fh_add(&subdev_fh->vfh); file->private_data = &subdev_fh->vfh; #if defined(CONFIG_MEDIA_CONTROLLER) - if (sd->v4l2_dev->mdev) { - entity = media_entity_get(&sd->entity); - if (!entity) { + if (sd->v4l2_dev->mdev && sd->entity.graph_obj.mdev->dev) { + struct module *owner; + + owner = sd->entity.graph_obj.mdev->dev->driver->owner; + if (!try_module_get(owner)) { ret = -EBUSY; goto err; } + subdev_fh->owner = owner; } #endif @@ -91,9 +86,7 @@ return 0; err: -#if defined(CONFIG_MEDIA_CONTROLLER) - media_entity_put(entity); -#endif + module_put(subdev_fh->owner); v4l2_fh_del(&subdev_fh->vfh); v4l2_fh_exit(&subdev_fh->vfh); subdev_fh_free(subdev_fh); @@ -111,10 +104,7 @@ if (sd->internal_ops && sd->internal_ops->close) sd->internal_ops->close(sd, subdev_fh); -#if defined(CONFIG_MEDIA_CONTROLLER) - if (sd->v4l2_dev->mdev) - media_entity_put(&sd->entity); -#endif + module_put(subdev_fh->owner); v4l2_fh_del(vfh); v4l2_fh_exit(vfh); subdev_fh_free(subdev_fh); @@ -123,69 +113,267 @@ return 0; } +#else /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static int subdev_open(struct file *file) +{ + return -ENODEV; +} -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) -static int check_format(struct v4l2_subdev *sd, +static int subdev_close(struct file *file) +{ + return -ENODEV; +} +#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ + +static inline int check_which(u32 which) +{ + if (which != V4L2_SUBDEV_FORMAT_TRY && + which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + return 0; +} + +static inline int check_pad(struct v4l2_subdev *sd, u32 pad) +{ +#if defined(CONFIG_MEDIA_CONTROLLER) + if (sd->entity.num_pads) { + if (pad >= sd->entity.num_pads) + return -EINVAL; + return 0; + } +#endif + /* allow pad 0 on subdevices not registered as media entities */ + if (pad > 0) + return -EINVAL; + return 0; +} + +static int check_cfg(u32 which, struct v4l2_subdev_pad_config *cfg) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY && !cfg) + return -EINVAL; + + return 0; +} + +static inline int check_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + if (!format) + return -EINVAL; + + return check_which(format->which) ? : check_pad(sd, format->pad) ? : + check_cfg(format->which, cfg); +} + +static int call_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { - if (format->which != V4L2_SUBDEV_FORMAT_TRY && - format->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (format->pad >= sd->entity.num_pads) - return -EINVAL; - - return 0; + return check_format(sd, cfg, format) ? : + sd->ops->pad->get_fmt(sd, cfg, format); } -static int check_crop(struct v4l2_subdev *sd, struct v4l2_subdev_crop *crop) +static int call_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { - if (crop->which != V4L2_SUBDEV_FORMAT_TRY && - crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (crop->pad >= sd->entity.num_pads) - return -EINVAL; - - return 0; + return check_format(sd, cfg, format) ? : + sd->ops->pad->set_fmt(sd, cfg, format); } -static int check_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_selection *sel) +static int call_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) { - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + if (!code) return -EINVAL; - if (sel->pad >= sd->entity.num_pads) - return -EINVAL; - - return 0; + return check_which(code->which) ? : check_pad(sd, code->pad) ? : + check_cfg(code->which, cfg) ? : + sd->ops->pad->enum_mbus_code(sd, cfg, code); } -static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +static int call_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) { - if (edid->pad >= sd->entity.num_pads) + if (!fse) + return -EINVAL; + + return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : + check_cfg(fse->which, cfg) ? : + sd->ops->pad->enum_frame_size(sd, cfg, 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_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + if (!fie) + return -EINVAL; + + return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : + check_cfg(fie->which, cfg) ? : + sd->ops->pad->enum_frame_interval(sd, cfg, fie); +} + +static inline int check_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (!sel) + return -EINVAL; + + return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : + check_cfg(sel->which, cfg); +} + +static int call_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + return check_selection(sd, cfg, sel) ? : + sd->ops->pad->get_selection(sd, cfg, sel); +} + +static int call_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + return check_selection(sd, cfg, sel) ? : + sd->ops->pad->set_selection(sd, cfg, sel); +} + +static inline int check_edid(struct v4l2_subdev *sd, + struct v4l2_subdev_edid *edid) +{ + if (!edid) return -EINVAL; if (edid->blocks && edid->edid == NULL) return -EINVAL; - return 0; + return check_pad(sd, edid->pad); } -#endif +static int call_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +{ + return check_edid(sd, edid) ? : sd->ops->pad->get_edid(sd, edid); +} + +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_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + if (!cap) + return -EINVAL; + + return check_pad(sd, cap->pad) ? : + sd->ops->pad->dv_timings_cap(sd, cap); +} + +static int call_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *dvt) +{ + if (!dvt) + return -EINVAL; + + return check_pad(sd, dvt->pad) ? : + sd->ops->pad->enum_dv_timings(sd, dvt); +} + +static int call_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + return check_pad(sd, pad) ? : + sd->ops->pad->get_mbus_config(sd, pad, config); +} + +static int call_set_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + return check_pad(sd, pad) ? : + sd->ops->pad->get_mbus_config(sd, pad, config); +} + +static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { + .get_fmt = call_get_fmt, + .set_fmt = call_set_fmt, + .enum_mbus_code = call_enum_mbus_code, + .enum_frame_size = call_enum_frame_size, + .enum_frame_interval = call_enum_frame_interval, + .get_selection = call_get_selection, + .set_selection = call_set_selection, + .get_edid = call_get_edid, + .set_edid = call_set_edid, + .dv_timings_cap = call_dv_timings_cap, + .enum_dv_timings = call_enum_dv_timings, + .get_mbus_config = call_get_mbus_config, + .set_mbus_config = call_set_mbus_config, +}; + +static const struct v4l2_subdev_video_ops v4l2_subdev_call_video_wrappers = { + .g_frame_interval = call_g_frame_interval, + .s_frame_interval = call_s_frame_interval, +}; + +const struct v4l2_subdev_ops v4l2_subdev_call_wrappers = { + .pad = &v4l2_subdev_call_pad_wrappers, + .video = &v4l2_subdev_call_video_wrappers, +}; +EXPORT_SYMBOL(v4l2_subdev_call_wrappers); + +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct v4l2_fh *vfh = file->private_data; -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); + bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); int rval; -#endif switch (cmd) { + case VIDIOC_SUBDEV_QUERYCAP: { + struct v4l2_subdev_capability *cap = arg; + + memset(cap->reserved, 0, sizeof(cap->reserved)); + cap->version = LINUX_VERSION_CODE; + cap->capabilities = ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0; + + return 0; + } + case VIDIOC_QUERYCTRL: /* * TODO: this really should be folded into v4l2_queryctrl (this @@ -222,17 +410,20 @@ case VIDIOC_G_EXT_CTRLS: if (!vfh->ctrl_handler) return -ENOTTY; - return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg); + return v4l2_g_ext_ctrls(vfh->ctrl_handler, + vdev, sd->v4l2_dev->mdev, arg); case VIDIOC_S_EXT_CTRLS: if (!vfh->ctrl_handler) return -ENOTTY; - return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg); + return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, + vdev, sd->v4l2_dev->mdev, arg); case VIDIOC_TRY_EXT_CTRLS: if (!vfh->ctrl_handler) return -ENOTTY; - return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg); + return v4l2_try_ext_ctrls(vfh->ctrl_handler, + vdev, sd->v4l2_dev->mdev, arg); case VIDIOC_DQEVENT: if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) @@ -273,7 +464,7 @@ p->flags |= V4L2_CHIP_FL_WRITABLE; if (sd->ops->core && sd->ops->core->g_register) p->flags |= V4L2_CHIP_FL_READABLE; - strlcpy(p->name, sd->name, sizeof(p->name)); + strscpy(p->name, sd->name, sizeof(p->name)); return 0; } #endif @@ -289,13 +480,8 @@ return ret; } -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) case VIDIOC_SUBDEV_G_FMT: { struct v4l2_subdev_format *format = arg; - - rval = check_format(sd, format); - if (rval) - return rval; memset(format->reserved, 0, sizeof(format->reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved)); @@ -304,10 +490,17 @@ case VIDIOC_SUBDEV_S_FMT: { struct v4l2_subdev_format *format = arg; + int ret = 0; - rval = check_format(sd, format); - if (rval) - return rval; + if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + + trace_android_vh_v4l2subdev_set_fmt(sd, subdev_fh->pad, + format, &ret); + trace_android_rvh_v4l2subdev_set_fmt(sd, subdev_fh->pad, + format, &ret); + if (ret) + return ret; memset(format->reserved, 0, sizeof(format->reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved)); @@ -317,10 +510,6 @@ case VIDIOC_SUBDEV_G_CROP: { struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; - - rval = check_crop(sd, crop); - if (rval) - return rval; memset(crop->reserved, 0, sizeof(crop->reserved)); memset(&sel, 0, sizeof(sel)); @@ -340,11 +529,10 @@ struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; - memset(crop->reserved, 0, sizeof(crop->reserved)); - rval = check_crop(sd, crop); - if (rval) - return rval; + if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + memset(crop->reserved, 0, sizeof(crop->reserved)); memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; @@ -362,13 +550,6 @@ case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { struct v4l2_subdev_mbus_code_enum *code = arg; - if (code->which != V4L2_SUBDEV_FORMAT_TRY && - code->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (code->pad >= sd->entity.num_pads) - return -EINVAL; - memset(code->reserved, 0, sizeof(code->reserved)); return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->pad, code); @@ -376,13 +557,6 @@ case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: { struct v4l2_subdev_frame_size_enum *fse = arg; - - if (fse->which != V4L2_SUBDEV_FORMAT_TRY && - fse->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (fse->pad >= sd->entity.num_pads) - return -EINVAL; memset(fse->reserved, 0, sizeof(fse->reserved)); return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->pad, @@ -392,18 +566,21 @@ case VIDIOC_SUBDEV_G_FRAME_INTERVAL: { struct v4l2_subdev_frame_interval *fi = arg; - if (fi->pad >= sd->entity.num_pads) - return -EINVAL; - memset(fi->reserved, 0, sizeof(fi->reserved)); return v4l2_subdev_call(sd, video, g_frame_interval, arg); } case VIDIOC_SUBDEV_S_FRAME_INTERVAL: { struct v4l2_subdev_frame_interval *fi = arg; + int ret = 0; - if (fi->pad >= sd->entity.num_pads) - return -EINVAL; + if (ro_subdev) + return -EPERM; + + trace_android_vh_v4l2subdev_set_frame_interval(sd, fi, &ret); + trace_android_rvh_v4l2subdev_set_frame_interval(sd, fi, &ret); + if (ret) + return ret; memset(fi->reserved, 0, sizeof(fi->reserved)); return v4l2_subdev_call(sd, video, s_frame_interval, arg); @@ -411,13 +588,6 @@ case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { struct v4l2_subdev_frame_interval_enum *fie = arg; - - if (fie->which != V4L2_SUBDEV_FORMAT_TRY && - fie->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (fie->pad >= sd->entity.num_pads) - return -EINVAL; memset(fie->reserved, 0, sizeof(fie->reserved)); return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->pad, @@ -427,10 +597,6 @@ case VIDIOC_SUBDEV_G_SELECTION: { struct v4l2_subdev_selection *sel = arg; - rval = check_selection(sd, sel); - if (rval) - return rval; - memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( sd, pad, get_selection, subdev_fh->pad, sel); @@ -438,10 +604,17 @@ case VIDIOC_SUBDEV_S_SELECTION: { struct v4l2_subdev_selection *sel = arg; + int ret = 0; - rval = check_selection(sd, sel); - if (rval) - return rval; + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + + trace_android_vh_v4l2subdev_set_selection(sd, subdev_fh->pad, + sel, &ret); + trace_android_rvh_v4l2subdev_set_selection(sd, subdev_fh->pad, + sel, &ret); + if (ret) + return ret; memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( @@ -451,19 +624,11 @@ case VIDIOC_G_EDID: { struct v4l2_subdev_edid *edid = arg; - rval = check_edid(sd, edid); - if (rval) - return rval; - return v4l2_subdev_call(sd, pad, get_edid, edid); } case VIDIOC_S_EDID: { struct v4l2_subdev_edid *edid = arg; - - rval = check_edid(sd, edid); - if (rval) - return rval; return v4l2_subdev_call(sd, pad, set_edid, edid); } @@ -471,17 +636,11 @@ case VIDIOC_SUBDEV_DV_TIMINGS_CAP: { struct v4l2_dv_timings_cap *cap = arg; - if (cap->pad >= sd->entity.num_pads) - return -EINVAL; - return v4l2_subdev_call(sd, pad, dv_timings_cap, cap); } case VIDIOC_SUBDEV_ENUM_DV_TIMINGS: { struct v4l2_enum_dv_timings *dvt = arg; - - if (dvt->pad >= sd->entity.num_pads) - return -EINVAL; return v4l2_subdev_call(sd, pad, enum_dv_timings, dvt); } @@ -493,16 +652,19 @@ return v4l2_subdev_call(sd, video, g_dv_timings, arg); case VIDIOC_SUBDEV_S_DV_TIMINGS: - return v4l2_subdev_call(sd, video, s_dv_timings, arg); + if (ro_subdev) + return -EPERM; - case VIDIOC_G_INPUT: - return v4l2_subdev_call(sd, video, g_input_status, arg); + return v4l2_subdev_call(sd, video, s_dv_timings, arg); case VIDIOC_SUBDEV_G_STD: return v4l2_subdev_call(sd, video, g_std, arg); case VIDIOC_SUBDEV_S_STD: { v4l2_std_id *std = arg; + + if (ro_subdev) + return -EPERM; return v4l2_subdev_call(sd, video, s_std, *std); } @@ -519,7 +681,7 @@ case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); -#endif + default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } @@ -559,6 +721,22 @@ } #endif +#else /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static long subdev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return -ENODEV; +} + +#ifdef CONFIG_COMPAT +static long subdev_compat_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return -ENODEV; +} +#endif +#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ + static __poll_t subdev_poll(struct file *file, poll_table *wait) { struct video_device *vdev = video_devdata(file); @@ -586,9 +764,30 @@ .release = subdev_close, .poll = subdev_poll, }; -EXPORT_SYMBOL_GPL(v4l2_subdev_fops); #ifdef CONFIG_MEDIA_CONTROLLER + +int v4l2_subdev_get_fwnode_pad_1_to_1(struct media_entity *entity, + struct fwnode_endpoint *endpoint) +{ + struct fwnode_handle *fwnode; + struct v4l2_subdev *sd; + + if (!is_media_entity_v4l2_subdev(entity)) + return -EINVAL; + + sd = media_entity_to_v4l2_subdev(entity); + + fwnode = fwnode_graph_get_port_parent(endpoint->local_fwnode); + fwnode_handle_put(fwnode); + + if (dev_fwnode(sd->dev) == fwnode) + return endpoint->port; + + return -ENXIO; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_get_fwnode_pad_1_to_1); + int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, struct media_link *link, struct v4l2_subdev_format *source_fmt, -- Gitblit v1.6.2