diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/media/usb/uvc/uvc_entity.c | |
parent | Initial commit. (diff) | |
download | linux-upstream.tar.xz linux-upstream.zip |
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/media/usb/uvc/uvc_entity.c')
-rw-r--r-- | drivers/media/usb/uvc/uvc_entity.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c new file mode 100644 index 000000000..cc68dd24e --- /dev/null +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * uvc_entity.c -- USB Video Class driver + * + * Copyright (C) 2005-2011 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/videodev2.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +static int uvc_mc_create_links(struct uvc_video_chain *chain, + struct uvc_entity *entity) +{ + const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; + struct media_entity *sink; + unsigned int i; + int ret; + + sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) + ? (entity->vdev ? &entity->vdev->entity : NULL) + : &entity->subdev.entity; + if (sink == NULL) + return 0; + + for (i = 0; i < entity->num_pads; ++i) { + struct media_entity *source; + struct uvc_entity *remote; + u8 remote_pad; + + if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK)) + continue; + + remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]); + if (remote == NULL || remote->num_pads == 0) + return -EINVAL; + + source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) + ? (remote->vdev ? &remote->vdev->entity : NULL) + : &remote->subdev.entity; + if (source == NULL) + continue; + + remote_pad = remote->num_pads - 1; + ret = media_create_pad_link(source, remote_pad, + sink, i, flags); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct v4l2_subdev_ops uvc_subdev_ops = { +}; + +void uvc_mc_cleanup_entity(struct uvc_entity *entity) +{ + if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) + media_entity_cleanup(&entity->subdev.entity); + else if (entity->vdev != NULL) + media_entity_cleanup(&entity->vdev->entity); +} + +static int uvc_mc_init_entity(struct uvc_video_chain *chain, + struct uvc_entity *entity) +{ + int ret; + + if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { + u32 function; + + v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops); + strscpy(entity->subdev.name, entity->name, + sizeof(entity->subdev.name)); + + switch (UVC_ENTITY_TYPE(entity)) { + case UVC_VC_SELECTOR_UNIT: + function = MEDIA_ENT_F_VID_MUX; + break; + case UVC_VC_PROCESSING_UNIT: + case UVC_VC_EXTENSION_UNIT: + /* For lack of a better option. */ + function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + break; + case UVC_COMPOSITE_CONNECTOR: + case UVC_COMPONENT_CONNECTOR: + function = MEDIA_ENT_F_CONN_COMPOSITE; + break; + case UVC_SVIDEO_CONNECTOR: + function = MEDIA_ENT_F_CONN_SVIDEO; + break; + case UVC_ITT_CAMERA: + function = MEDIA_ENT_F_CAM_SENSOR; + break; + case UVC_TT_VENDOR_SPECIFIC: + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_EXTERNAL_VENDOR_SPECIFIC: + case UVC_EXT_GPIO_UNIT: + default: + function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + break; + } + + entity->subdev.entity.function = function; + + ret = media_entity_pads_init(&entity->subdev.entity, + entity->num_pads, entity->pads); + + if (ret < 0) + return ret; + + ret = v4l2_device_register_subdev(&chain->dev->vdev, + &entity->subdev); + } else if (entity->vdev != NULL) { + ret = media_entity_pads_init(&entity->vdev->entity, + entity->num_pads, entity->pads); + if (entity->flags & UVC_ENTITY_FLAG_DEFAULT) + entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + } else + ret = 0; + + return ret; +} + +int uvc_mc_register_entities(struct uvc_video_chain *chain) +{ + struct uvc_entity *entity; + int ret; + + list_for_each_entry(entity, &chain->entities, chain) { + ret = uvc_mc_init_entity(chain, entity); + if (ret < 0) { + dev_info(&chain->dev->udev->dev, + "Failed to initialize entity for entity %u\n", + entity->id); + return ret; + } + } + + list_for_each_entry(entity, &chain->entities, chain) { + ret = uvc_mc_create_links(chain, entity); + if (ret < 0) { + dev_info(&chain->dev->udev->dev, + "Failed to create links for entity %u\n", + entity->id); + return ret; + } + } + + return 0; +} |