18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *      uvc_entity.c  --  USB Video Class driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *      Copyright (C) 2005-2011
68c2ecf20Sopenharmony_ci *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/list.h>
118c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "uvcvideo.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic int uvc_mc_create_links(struct uvc_video_chain *chain,
188c2ecf20Sopenharmony_ci				    struct uvc_entity *entity)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
218c2ecf20Sopenharmony_ci	struct media_entity *sink;
228c2ecf20Sopenharmony_ci	unsigned int i;
238c2ecf20Sopenharmony_ci	int ret;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
268c2ecf20Sopenharmony_ci	     ? (entity->vdev ? &entity->vdev->entity : NULL)
278c2ecf20Sopenharmony_ci	     : &entity->subdev.entity;
288c2ecf20Sopenharmony_ci	if (sink == NULL)
298c2ecf20Sopenharmony_ci		return 0;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	for (i = 0; i < entity->num_pads; ++i) {
328c2ecf20Sopenharmony_ci		struct media_entity *source;
338c2ecf20Sopenharmony_ci		struct uvc_entity *remote;
348c2ecf20Sopenharmony_ci		u8 remote_pad;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci		if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK))
378c2ecf20Sopenharmony_ci			continue;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
408c2ecf20Sopenharmony_ci		if (remote == NULL || remote->num_pads == 0)
418c2ecf20Sopenharmony_ci			return -EINVAL;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci		source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
448c2ecf20Sopenharmony_ci		       ? (remote->vdev ? &remote->vdev->entity : NULL)
458c2ecf20Sopenharmony_ci		       : &remote->subdev.entity;
468c2ecf20Sopenharmony_ci		if (source == NULL)
478c2ecf20Sopenharmony_ci			continue;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		remote_pad = remote->num_pads - 1;
508c2ecf20Sopenharmony_ci		ret = media_create_pad_link(source, remote_pad,
518c2ecf20Sopenharmony_ci					       sink, i, flags);
528c2ecf20Sopenharmony_ci		if (ret < 0)
538c2ecf20Sopenharmony_ci			return ret;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return 0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops uvc_subdev_ops = {
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_civoid uvc_mc_cleanup_entity(struct uvc_entity *entity)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING)
658c2ecf20Sopenharmony_ci		media_entity_cleanup(&entity->subdev.entity);
668c2ecf20Sopenharmony_ci	else if (entity->vdev != NULL)
678c2ecf20Sopenharmony_ci		media_entity_cleanup(&entity->vdev->entity);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int uvc_mc_init_entity(struct uvc_video_chain *chain,
718c2ecf20Sopenharmony_ci			      struct uvc_entity *entity)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	int ret;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) {
768c2ecf20Sopenharmony_ci		u32 function;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops);
798c2ecf20Sopenharmony_ci		strscpy(entity->subdev.name, entity->name,
808c2ecf20Sopenharmony_ci			sizeof(entity->subdev.name));
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		switch (UVC_ENTITY_TYPE(entity)) {
838c2ecf20Sopenharmony_ci		case UVC_VC_SELECTOR_UNIT:
848c2ecf20Sopenharmony_ci			function = MEDIA_ENT_F_VID_MUX;
858c2ecf20Sopenharmony_ci			break;
868c2ecf20Sopenharmony_ci		case UVC_VC_PROCESSING_UNIT:
878c2ecf20Sopenharmony_ci		case UVC_VC_EXTENSION_UNIT:
888c2ecf20Sopenharmony_ci			/* For lack of a better option. */
898c2ecf20Sopenharmony_ci			function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
908c2ecf20Sopenharmony_ci			break;
918c2ecf20Sopenharmony_ci		case UVC_COMPOSITE_CONNECTOR:
928c2ecf20Sopenharmony_ci		case UVC_COMPONENT_CONNECTOR:
938c2ecf20Sopenharmony_ci			function = MEDIA_ENT_F_CONN_COMPOSITE;
948c2ecf20Sopenharmony_ci			break;
958c2ecf20Sopenharmony_ci		case UVC_SVIDEO_CONNECTOR:
968c2ecf20Sopenharmony_ci			function = MEDIA_ENT_F_CONN_SVIDEO;
978c2ecf20Sopenharmony_ci			break;
988c2ecf20Sopenharmony_ci		case UVC_ITT_CAMERA:
998c2ecf20Sopenharmony_ci			function = MEDIA_ENT_F_CAM_SENSOR;
1008c2ecf20Sopenharmony_ci			break;
1018c2ecf20Sopenharmony_ci		case UVC_TT_VENDOR_SPECIFIC:
1028c2ecf20Sopenharmony_ci		case UVC_ITT_VENDOR_SPECIFIC:
1038c2ecf20Sopenharmony_ci		case UVC_ITT_MEDIA_TRANSPORT_INPUT:
1048c2ecf20Sopenharmony_ci		case UVC_OTT_VENDOR_SPECIFIC:
1058c2ecf20Sopenharmony_ci		case UVC_OTT_DISPLAY:
1068c2ecf20Sopenharmony_ci		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
1078c2ecf20Sopenharmony_ci		case UVC_EXTERNAL_VENDOR_SPECIFIC:
1088c2ecf20Sopenharmony_ci		default:
1098c2ecf20Sopenharmony_ci			function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
1108c2ecf20Sopenharmony_ci			break;
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		entity->subdev.entity.function = function;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		ret = media_entity_pads_init(&entity->subdev.entity,
1168c2ecf20Sopenharmony_ci					entity->num_pads, entity->pads);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		if (ret < 0)
1198c2ecf20Sopenharmony_ci			return ret;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		ret = v4l2_device_register_subdev(&chain->dev->vdev,
1228c2ecf20Sopenharmony_ci						  &entity->subdev);
1238c2ecf20Sopenharmony_ci	} else if (entity->vdev != NULL) {
1248c2ecf20Sopenharmony_ci		ret = media_entity_pads_init(&entity->vdev->entity,
1258c2ecf20Sopenharmony_ci					entity->num_pads, entity->pads);
1268c2ecf20Sopenharmony_ci		if (entity->flags & UVC_ENTITY_FLAG_DEFAULT)
1278c2ecf20Sopenharmony_ci			entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
1288c2ecf20Sopenharmony_ci	} else
1298c2ecf20Sopenharmony_ci		ret = 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return ret;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ciint uvc_mc_register_entities(struct uvc_video_chain *chain)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct uvc_entity *entity;
1378c2ecf20Sopenharmony_ci	int ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	list_for_each_entry(entity, &chain->entities, chain) {
1408c2ecf20Sopenharmony_ci		ret = uvc_mc_init_entity(chain, entity);
1418c2ecf20Sopenharmony_ci		if (ret < 0) {
1428c2ecf20Sopenharmony_ci			uvc_printk(KERN_INFO, "Failed to initialize entity for "
1438c2ecf20Sopenharmony_ci				   "entity %u\n", entity->id);
1448c2ecf20Sopenharmony_ci			return ret;
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	list_for_each_entry(entity, &chain->entities, chain) {
1498c2ecf20Sopenharmony_ci		ret = uvc_mc_create_links(chain, entity);
1508c2ecf20Sopenharmony_ci		if (ret < 0) {
1518c2ecf20Sopenharmony_ci			uvc_printk(KERN_INFO, "Failed to create links for "
1528c2ecf20Sopenharmony_ci				   "entity %u\n", entity->id);
1538c2ecf20Sopenharmony_ci			return ret;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
159