1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * uvc_entity.c -- USB Video Class driver |
4 | * |
5 | * Copyright (C) 2005-2011 |
6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/list.h> |
11 | #include <linux/videodev2.h> |
12 | |
13 | #include <media/v4l2-common.h> |
14 | |
15 | #include "uvcvideo.h" |
16 | |
17 | static int uvc_mc_create_links(struct uvc_video_chain *chain, |
18 | struct uvc_entity *entity) |
19 | { |
20 | const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; |
21 | struct media_entity *sink; |
22 | unsigned int i; |
23 | int ret; |
24 | |
25 | sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) |
26 | ? (entity->vdev ? &entity->vdev->entity : NULL) |
27 | : &entity->subdev.entity; |
28 | if (sink == NULL) |
29 | return 0; |
30 | |
31 | for (i = 0; i < entity->num_pads; ++i) { |
32 | struct media_entity *source; |
33 | struct uvc_entity *remote; |
34 | u8 remote_pad; |
35 | |
36 | if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK)) |
37 | continue; |
38 | |
39 | remote = uvc_entity_by_id(dev: chain->dev, id: entity->baSourceID[i]); |
40 | if (remote == NULL || remote->num_pads == 0) |
41 | return -EINVAL; |
42 | |
43 | source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) |
44 | ? (remote->vdev ? &remote->vdev->entity : NULL) |
45 | : &remote->subdev.entity; |
46 | if (source == NULL) |
47 | continue; |
48 | |
49 | remote_pad = remote->num_pads - 1; |
50 | ret = media_create_pad_link(source, source_pad: remote_pad, |
51 | sink, sink_pad: i, flags); |
52 | if (ret < 0) |
53 | return ret; |
54 | } |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static const struct v4l2_subdev_ops uvc_subdev_ops = { |
60 | }; |
61 | |
62 | void uvc_mc_cleanup_entity(struct uvc_entity *entity) |
63 | { |
64 | if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) |
65 | media_entity_cleanup(entity: &entity->subdev.entity); |
66 | else if (entity->vdev != NULL) |
67 | media_entity_cleanup(entity: &entity->vdev->entity); |
68 | } |
69 | |
70 | static int uvc_mc_init_entity(struct uvc_video_chain *chain, |
71 | struct uvc_entity *entity) |
72 | { |
73 | int ret; |
74 | |
75 | if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { |
76 | u32 function; |
77 | |
78 | v4l2_subdev_init(sd: &entity->subdev, ops: &uvc_subdev_ops); |
79 | strscpy(entity->subdev.name, entity->name, |
80 | sizeof(entity->subdev.name)); |
81 | |
82 | switch (UVC_ENTITY_TYPE(entity)) { |
83 | case UVC_VC_SELECTOR_UNIT: |
84 | function = MEDIA_ENT_F_VID_MUX; |
85 | break; |
86 | case UVC_VC_PROCESSING_UNIT: |
87 | case UVC_VC_EXTENSION_UNIT: |
88 | /* For lack of a better option. */ |
89 | function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; |
90 | break; |
91 | case UVC_COMPOSITE_CONNECTOR: |
92 | case UVC_COMPONENT_CONNECTOR: |
93 | function = MEDIA_ENT_F_CONN_COMPOSITE; |
94 | break; |
95 | case UVC_SVIDEO_CONNECTOR: |
96 | function = MEDIA_ENT_F_CONN_SVIDEO; |
97 | break; |
98 | case UVC_ITT_CAMERA: |
99 | function = MEDIA_ENT_F_CAM_SENSOR; |
100 | break; |
101 | case UVC_TT_VENDOR_SPECIFIC: |
102 | case UVC_ITT_VENDOR_SPECIFIC: |
103 | case UVC_ITT_MEDIA_TRANSPORT_INPUT: |
104 | case UVC_OTT_VENDOR_SPECIFIC: |
105 | case UVC_OTT_DISPLAY: |
106 | case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: |
107 | case UVC_EXTERNAL_VENDOR_SPECIFIC: |
108 | case UVC_EXT_GPIO_UNIT: |
109 | default: |
110 | function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; |
111 | break; |
112 | } |
113 | |
114 | entity->subdev.entity.function = function; |
115 | |
116 | ret = media_entity_pads_init(entity: &entity->subdev.entity, |
117 | num_pads: entity->num_pads, pads: entity->pads); |
118 | |
119 | if (ret < 0) |
120 | return ret; |
121 | |
122 | ret = v4l2_device_register_subdev(v4l2_dev: &chain->dev->vdev, |
123 | sd: &entity->subdev); |
124 | } else if (entity->vdev != NULL) { |
125 | ret = media_entity_pads_init(entity: &entity->vdev->entity, |
126 | num_pads: entity->num_pads, pads: entity->pads); |
127 | if (entity->flags & UVC_ENTITY_FLAG_DEFAULT) |
128 | entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; |
129 | } else |
130 | ret = 0; |
131 | |
132 | return ret; |
133 | } |
134 | |
135 | int uvc_mc_register_entities(struct uvc_video_chain *chain) |
136 | { |
137 | struct uvc_entity *entity; |
138 | int ret; |
139 | |
140 | list_for_each_entry(entity, &chain->entities, chain) { |
141 | ret = uvc_mc_init_entity(chain, entity); |
142 | if (ret < 0) { |
143 | dev_info(&chain->dev->udev->dev, |
144 | "Failed to initialize entity for entity %u\n" , |
145 | entity->id); |
146 | return ret; |
147 | } |
148 | } |
149 | |
150 | list_for_each_entry(entity, &chain->entities, chain) { |
151 | ret = uvc_mc_create_links(chain, entity); |
152 | if (ret < 0) { |
153 | dev_info(&chain->dev->udev->dev, |
154 | "Failed to create links for entity %u\n" , |
155 | entity->id); |
156 | return ret; |
157 | } |
158 | } |
159 | |
160 | return 0; |
161 | } |
162 | |