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