162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *      uvc_entity.c  --  USB Video Class driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *      Copyright (C) 2005-2011
662306a36Sopenharmony_ci *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/list.h>
1162306a36Sopenharmony_ci#include <linux/videodev2.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <media/v4l2-common.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "uvcvideo.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int uvc_mc_create_links(struct uvc_video_chain *chain,
1862306a36Sopenharmony_ci				    struct uvc_entity *entity)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
2162306a36Sopenharmony_ci	struct media_entity *sink;
2262306a36Sopenharmony_ci	unsigned int i;
2362306a36Sopenharmony_ci	int ret;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
2662306a36Sopenharmony_ci	     ? (entity->vdev ? &entity->vdev->entity : NULL)
2762306a36Sopenharmony_ci	     : &entity->subdev.entity;
2862306a36Sopenharmony_ci	if (sink == NULL)
2962306a36Sopenharmony_ci		return 0;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	for (i = 0; i < entity->num_pads; ++i) {
3262306a36Sopenharmony_ci		struct media_entity *source;
3362306a36Sopenharmony_ci		struct uvc_entity *remote;
3462306a36Sopenharmony_ci		u8 remote_pad;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK))
3762306a36Sopenharmony_ci			continue;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci		remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
4062306a36Sopenharmony_ci		if (remote == NULL || remote->num_pads == 0)
4162306a36Sopenharmony_ci			return -EINVAL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci		source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
4462306a36Sopenharmony_ci		       ? (remote->vdev ? &remote->vdev->entity : NULL)
4562306a36Sopenharmony_ci		       : &remote->subdev.entity;
4662306a36Sopenharmony_ci		if (source == NULL)
4762306a36Sopenharmony_ci			continue;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		remote_pad = remote->num_pads - 1;
5062306a36Sopenharmony_ci		ret = media_create_pad_link(source, remote_pad,
5162306a36Sopenharmony_ci					       sink, i, flags);
5262306a36Sopenharmony_ci		if (ret < 0)
5362306a36Sopenharmony_ci			return ret;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return 0;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic const struct v4l2_subdev_ops uvc_subdev_ops = {
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_civoid uvc_mc_cleanup_entity(struct uvc_entity *entity)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING)
6562306a36Sopenharmony_ci		media_entity_cleanup(&entity->subdev.entity);
6662306a36Sopenharmony_ci	else if (entity->vdev != NULL)
6762306a36Sopenharmony_ci		media_entity_cleanup(&entity->vdev->entity);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int uvc_mc_init_entity(struct uvc_video_chain *chain,
7162306a36Sopenharmony_ci			      struct uvc_entity *entity)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int ret;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) {
7662306a36Sopenharmony_ci		u32 function;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops);
7962306a36Sopenharmony_ci		strscpy(entity->subdev.name, entity->name,
8062306a36Sopenharmony_ci			sizeof(entity->subdev.name));
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		switch (UVC_ENTITY_TYPE(entity)) {
8362306a36Sopenharmony_ci		case UVC_VC_SELECTOR_UNIT:
8462306a36Sopenharmony_ci			function = MEDIA_ENT_F_VID_MUX;
8562306a36Sopenharmony_ci			break;
8662306a36Sopenharmony_ci		case UVC_VC_PROCESSING_UNIT:
8762306a36Sopenharmony_ci		case UVC_VC_EXTENSION_UNIT:
8862306a36Sopenharmony_ci			/* For lack of a better option. */
8962306a36Sopenharmony_ci			function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
9062306a36Sopenharmony_ci			break;
9162306a36Sopenharmony_ci		case UVC_COMPOSITE_CONNECTOR:
9262306a36Sopenharmony_ci		case UVC_COMPONENT_CONNECTOR:
9362306a36Sopenharmony_ci			function = MEDIA_ENT_F_CONN_COMPOSITE;
9462306a36Sopenharmony_ci			break;
9562306a36Sopenharmony_ci		case UVC_SVIDEO_CONNECTOR:
9662306a36Sopenharmony_ci			function = MEDIA_ENT_F_CONN_SVIDEO;
9762306a36Sopenharmony_ci			break;
9862306a36Sopenharmony_ci		case UVC_ITT_CAMERA:
9962306a36Sopenharmony_ci			function = MEDIA_ENT_F_CAM_SENSOR;
10062306a36Sopenharmony_ci			break;
10162306a36Sopenharmony_ci		case UVC_TT_VENDOR_SPECIFIC:
10262306a36Sopenharmony_ci		case UVC_ITT_VENDOR_SPECIFIC:
10362306a36Sopenharmony_ci		case UVC_ITT_MEDIA_TRANSPORT_INPUT:
10462306a36Sopenharmony_ci		case UVC_OTT_VENDOR_SPECIFIC:
10562306a36Sopenharmony_ci		case UVC_OTT_DISPLAY:
10662306a36Sopenharmony_ci		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
10762306a36Sopenharmony_ci		case UVC_EXTERNAL_VENDOR_SPECIFIC:
10862306a36Sopenharmony_ci		case UVC_EXT_GPIO_UNIT:
10962306a36Sopenharmony_ci		default:
11062306a36Sopenharmony_ci			function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		entity->subdev.entity.function = function;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		ret = media_entity_pads_init(&entity->subdev.entity,
11762306a36Sopenharmony_ci					entity->num_pads, entity->pads);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		if (ret < 0)
12062306a36Sopenharmony_ci			return ret;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		ret = v4l2_device_register_subdev(&chain->dev->vdev,
12362306a36Sopenharmony_ci						  &entity->subdev);
12462306a36Sopenharmony_ci	} else if (entity->vdev != NULL) {
12562306a36Sopenharmony_ci		ret = media_entity_pads_init(&entity->vdev->entity,
12662306a36Sopenharmony_ci					entity->num_pads, entity->pads);
12762306a36Sopenharmony_ci		if (entity->flags & UVC_ENTITY_FLAG_DEFAULT)
12862306a36Sopenharmony_ci			entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
12962306a36Sopenharmony_ci	} else
13062306a36Sopenharmony_ci		ret = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return ret;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciint uvc_mc_register_entities(struct uvc_video_chain *chain)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct uvc_entity *entity;
13862306a36Sopenharmony_ci	int ret;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	list_for_each_entry(entity, &chain->entities, chain) {
14162306a36Sopenharmony_ci		ret = uvc_mc_init_entity(chain, entity);
14262306a36Sopenharmony_ci		if (ret < 0) {
14362306a36Sopenharmony_ci			dev_info(&chain->dev->udev->dev,
14462306a36Sopenharmony_ci				 "Failed to initialize entity for entity %u\n",
14562306a36Sopenharmony_ci				 entity->id);
14662306a36Sopenharmony_ci			return ret;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	list_for_each_entry(entity, &chain->entities, chain) {
15162306a36Sopenharmony_ci		ret = uvc_mc_create_links(chain, entity);
15262306a36Sopenharmony_ci		if (ret < 0) {
15362306a36Sopenharmony_ci			dev_info(&chain->dev->udev->dev,
15462306a36Sopenharmony_ci				 "Failed to create links for entity %u\n",
15562306a36Sopenharmony_ci				 entity->id);
15662306a36Sopenharmony_ci			return ret;
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
162