Commit | Line | Data |
---|---|---|
54d0dbac MCC |
1 | /* |
2 | * Media Controller ancillary functions | |
3 | * | |
41b44e35 | 4 | * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@osg.samsung.com> |
d0a164f5 | 5 | * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com> |
76413791 SA |
6 | * Copyright (C) 2006-2010 Nokia Corporation |
7 | * Copyright (c) 2016 Intel Corporation. | |
54d0dbac MCC |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
76413791 SA |
21 | #include <linux/pci.h> |
22 | #include <linux/usb.h> | |
d0a164f5 | 23 | #include <media/media-device.h> |
76413791 | 24 | #include <media/media-entity.h> |
d0a164f5 | 25 | #include <media/v4l2-fh.h> |
54d0dbac | 26 | #include <media/v4l2-mc.h> |
76413791 SA |
27 | #include <media/v4l2-subdev.h> |
28 | #include <media/media-device.h> | |
29 | #include <media/v4l2-mc.h> | |
d0a164f5 | 30 | #include <media/videobuf2-core.h> |
54d0dbac MCC |
31 | |
32 | int v4l2_mc_create_media_graph(struct media_device *mdev) | |
33 | ||
34 | { | |
35 | struct media_entity *entity; | |
153d41a1 | 36 | struct media_entity *if_vid = NULL, *if_aud = NULL; |
17084b7e | 37 | struct media_entity *tuner = NULL, *decoder = NULL; |
54d0dbac MCC |
38 | struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL; |
39 | bool is_webcam = false; | |
40 | u32 flags; | |
41 | int ret; | |
42 | ||
43 | if (!mdev) | |
44 | return 0; | |
45 | ||
46 | media_device_for_each_entity(entity, mdev) { | |
47 | switch (entity->function) { | |
48 | case MEDIA_ENT_F_IF_VID_DECODER: | |
49 | if_vid = entity; | |
50 | break; | |
51 | case MEDIA_ENT_F_IF_AUD_DECODER: | |
52 | if_aud = entity; | |
53 | break; | |
54 | case MEDIA_ENT_F_TUNER: | |
55 | tuner = entity; | |
56 | break; | |
57 | case MEDIA_ENT_F_ATV_DECODER: | |
58 | decoder = entity; | |
59 | break; | |
60 | case MEDIA_ENT_F_IO_V4L: | |
61 | io_v4l = entity; | |
62 | break; | |
63 | case MEDIA_ENT_F_IO_VBI: | |
64 | io_vbi = entity; | |
65 | break; | |
66 | case MEDIA_ENT_F_IO_SWRADIO: | |
67 | io_swradio = entity; | |
68 | break; | |
69 | case MEDIA_ENT_F_CAM_SENSOR: | |
54d0dbac MCC |
70 | is_webcam = true; |
71 | break; | |
72 | } | |
73 | } | |
74 | ||
75 | /* It should have at least one I/O entity */ | |
76 | if (!io_v4l && !io_vbi && !io_swradio) | |
77 | return -EINVAL; | |
78 | ||
79 | /* | |
80 | * Here, webcams are modelled on a very simple way: the sensor is | |
81 | * connected directly to the I/O entity. All dirty details, like | |
82 | * scaler and crop HW are hidden. While such mapping is not enough | |
83 | * for mc-centric hardware, it is enough for v4l2 interface centric | |
84 | * PC-consumer's hardware. | |
85 | */ | |
86 | if (is_webcam) { | |
87 | if (!io_v4l) | |
88 | return -EINVAL; | |
89 | ||
90 | media_device_for_each_entity(entity, mdev) { | |
91 | if (entity->function != MEDIA_ENT_F_CAM_SENSOR) | |
92 | continue; | |
93 | ret = media_create_pad_link(entity, 0, | |
94 | io_v4l, 0, | |
95 | MEDIA_LNK_FL_ENABLED); | |
96 | if (ret) | |
97 | return ret; | |
98 | } | |
99 | if (!decoder) | |
100 | return 0; | |
101 | } | |
102 | ||
103 | /* The device isn't a webcam. So, it should have a decoder */ | |
104 | if (!decoder) | |
105 | return -EINVAL; | |
106 | ||
107 | /* Link the tuner and IF video output pads */ | |
108 | if (tuner) { | |
109 | if (if_vid) { | |
110 | ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, | |
111 | if_vid, | |
112 | IF_VID_DEC_PAD_IF_INPUT, | |
113 | MEDIA_LNK_FL_ENABLED); | |
114 | if (ret) | |
115 | return ret; | |
116 | ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT, | |
117 | decoder, DEMOD_PAD_IF_INPUT, | |
118 | MEDIA_LNK_FL_ENABLED); | |
119 | if (ret) | |
120 | return ret; | |
121 | } else { | |
122 | ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, | |
123 | decoder, DEMOD_PAD_IF_INPUT, | |
124 | MEDIA_LNK_FL_ENABLED); | |
125 | if (ret) | |
126 | return ret; | |
127 | } | |
128 | ||
129 | if (if_aud) { | |
130 | ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT, | |
131 | if_aud, | |
132 | IF_AUD_DEC_PAD_IF_INPUT, | |
133 | MEDIA_LNK_FL_ENABLED); | |
134 | if (ret) | |
135 | return ret; | |
136 | } else { | |
137 | if_aud = tuner; | |
138 | } | |
139 | ||
140 | } | |
141 | ||
142 | /* Create demod to V4L, VBI and SDR radio links */ | |
143 | if (io_v4l) { | |
144 | ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT, | |
145 | io_v4l, 0, | |
146 | MEDIA_LNK_FL_ENABLED); | |
147 | if (ret) | |
148 | return ret; | |
149 | } | |
150 | ||
151 | if (io_swradio) { | |
152 | ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT, | |
153 | io_swradio, 0, | |
154 | MEDIA_LNK_FL_ENABLED); | |
155 | if (ret) | |
156 | return ret; | |
157 | } | |
158 | ||
159 | if (io_vbi) { | |
160 | ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT, | |
161 | io_vbi, 0, | |
162 | MEDIA_LNK_FL_ENABLED); | |
163 | if (ret) | |
164 | return ret; | |
165 | } | |
166 | ||
167 | /* Create links for the media connectors */ | |
168 | flags = MEDIA_LNK_FL_ENABLED; | |
169 | media_device_for_each_entity(entity, mdev) { | |
170 | switch (entity->function) { | |
171 | case MEDIA_ENT_F_CONN_RF: | |
172 | if (!tuner) | |
173 | continue; | |
174 | ||
175 | ret = media_create_pad_link(entity, 0, tuner, | |
176 | TUNER_PAD_RF_INPUT, | |
177 | flags); | |
178 | break; | |
179 | case MEDIA_ENT_F_CONN_SVIDEO: | |
180 | case MEDIA_ENT_F_CONN_COMPOSITE: | |
54d0dbac MCC |
181 | ret = media_create_pad_link(entity, 0, decoder, |
182 | DEMOD_PAD_IF_INPUT, | |
183 | flags); | |
184 | break; | |
185 | default: | |
186 | continue; | |
187 | } | |
188 | if (ret) | |
189 | return ret; | |
190 | ||
191 | flags = 0; | |
192 | } | |
9822f417 | 193 | |
54d0dbac MCC |
194 | return 0; |
195 | } | |
196 | EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph); | |
d0a164f5 SK |
197 | |
198 | int v4l_enable_media_source(struct video_device *vdev) | |
199 | { | |
200 | struct media_device *mdev = vdev->entity.graph_obj.mdev; | |
201 | int ret; | |
202 | ||
203 | if (!mdev || !mdev->enable_source) | |
204 | return 0; | |
205 | ret = mdev->enable_source(&vdev->entity, &vdev->pipe); | |
206 | if (ret) | |
207 | return -EBUSY; | |
208 | return 0; | |
209 | } | |
210 | EXPORT_SYMBOL_GPL(v4l_enable_media_source); | |
211 | ||
212 | void v4l_disable_media_source(struct video_device *vdev) | |
213 | { | |
214 | struct media_device *mdev = vdev->entity.graph_obj.mdev; | |
215 | ||
216 | if (mdev && mdev->disable_source) | |
217 | mdev->disable_source(&vdev->entity); | |
218 | } | |
219 | EXPORT_SYMBOL_GPL(v4l_disable_media_source); | |
220 | ||
221 | int v4l_vb2q_enable_media_source(struct vb2_queue *q) | |
222 | { | |
223 | struct v4l2_fh *fh = q->owner; | |
224 | ||
7e53898a SK |
225 | if (fh && fh->vdev) |
226 | return v4l_enable_media_source(fh->vdev); | |
227 | return 0; | |
d0a164f5 SK |
228 | } |
229 | EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); | |
76413791 SA |
230 | |
231 | /* ----------------------------------------------------------------------------- | |
232 | * Pipeline power management | |
233 | * | |
234 | * Entities must be powered up when part of a pipeline that contains at least | |
235 | * one open video device node. | |
236 | * | |
237 | * To achieve this use the entity use_count field to track the number of users. | |
238 | * For entities corresponding to video device nodes the use_count field stores | |
239 | * the users count of the node. For entities corresponding to subdevs the | |
240 | * use_count field stores the total number of users of all video device nodes | |
241 | * in the pipeline. | |
242 | * | |
243 | * The v4l2_pipeline_pm_use() function must be called in the open() and | |
244 | * close() handlers of video device nodes. It increments or decrements the use | |
245 | * count of all subdev entities in the pipeline. | |
246 | * | |
247 | * To react to link management on powered pipelines, the link setup notification | |
248 | * callback updates the use count of all entities in the source and sink sides | |
249 | * of the link. | |
250 | */ | |
251 | ||
252 | /* | |
253 | * pipeline_pm_use_count - Count the number of users of a pipeline | |
254 | * @entity: The entity | |
255 | * | |
256 | * Return the total number of users of all video device nodes in the pipeline. | |
257 | */ | |
258 | static int pipeline_pm_use_count(struct media_entity *entity, | |
259 | struct media_entity_graph *graph) | |
260 | { | |
261 | int use = 0; | |
262 | ||
263 | media_entity_graph_walk_start(graph, entity); | |
264 | ||
265 | while ((entity = media_entity_graph_walk_next(graph))) { | |
266 | if (is_media_entity_v4l2_io(entity)) | |
267 | use += entity->use_count; | |
268 | } | |
269 | ||
270 | return use; | |
271 | } | |
272 | ||
273 | /* | |
274 | * pipeline_pm_power_one - Apply power change to an entity | |
275 | * @entity: The entity | |
276 | * @change: Use count change | |
277 | * | |
278 | * Change the entity use count by @change. If the entity is a subdev update its | |
279 | * power state by calling the core::s_power operation when the use count goes | |
280 | * from 0 to != 0 or from != 0 to 0. | |
281 | * | |
282 | * Return 0 on success or a negative error code on failure. | |
283 | */ | |
284 | static int pipeline_pm_power_one(struct media_entity *entity, int change) | |
285 | { | |
286 | struct v4l2_subdev *subdev; | |
287 | int ret; | |
288 | ||
289 | subdev = is_media_entity_v4l2_subdev(entity) | |
290 | ? media_entity_to_v4l2_subdev(entity) : NULL; | |
291 | ||
292 | if (entity->use_count == 0 && change > 0 && subdev != NULL) { | |
293 | ret = v4l2_subdev_call(subdev, core, s_power, 1); | |
294 | if (ret < 0 && ret != -ENOIOCTLCMD) | |
295 | return ret; | |
296 | } | |
297 | ||
298 | entity->use_count += change; | |
299 | WARN_ON(entity->use_count < 0); | |
300 | ||
301 | if (entity->use_count == 0 && change < 0 && subdev != NULL) | |
302 | v4l2_subdev_call(subdev, core, s_power, 0); | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | /* | |
308 | * pipeline_pm_power - Apply power change to all entities in a pipeline | |
309 | * @entity: The entity | |
310 | * @change: Use count change | |
311 | * | |
312 | * Walk the pipeline to update the use count and the power state of all non-node | |
313 | * entities. | |
314 | * | |
315 | * Return 0 on success or a negative error code on failure. | |
316 | */ | |
317 | static int pipeline_pm_power(struct media_entity *entity, int change, | |
318 | struct media_entity_graph *graph) | |
319 | { | |
320 | struct media_entity *first = entity; | |
321 | int ret = 0; | |
322 | ||
323 | if (!change) | |
324 | return 0; | |
325 | ||
326 | media_entity_graph_walk_start(graph, entity); | |
327 | ||
328 | while (!ret && (entity = media_entity_graph_walk_next(graph))) | |
329 | if (is_media_entity_v4l2_subdev(entity)) | |
330 | ret = pipeline_pm_power_one(entity, change); | |
331 | ||
332 | if (!ret) | |
333 | return ret; | |
334 | ||
335 | media_entity_graph_walk_start(graph, first); | |
336 | ||
337 | while ((first = media_entity_graph_walk_next(graph)) | |
338 | && first != entity) | |
339 | if (is_media_entity_v4l2_subdev(first)) | |
340 | pipeline_pm_power_one(first, -change); | |
341 | ||
342 | return ret; | |
343 | } | |
344 | ||
345 | int v4l2_pipeline_pm_use(struct media_entity *entity, int use) | |
346 | { | |
347 | struct media_device *mdev = entity->graph_obj.mdev; | |
348 | int change = use ? 1 : -1; | |
349 | int ret; | |
350 | ||
351 | mutex_lock(&mdev->graph_mutex); | |
352 | ||
353 | /* Apply use count to node. */ | |
354 | entity->use_count += change; | |
355 | WARN_ON(entity->use_count < 0); | |
356 | ||
357 | /* Apply power change to connected non-nodes. */ | |
358 | ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk); | |
359 | if (ret < 0) | |
360 | entity->use_count -= change; | |
361 | ||
362 | mutex_unlock(&mdev->graph_mutex); | |
363 | ||
364 | return ret; | |
365 | } | |
366 | EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use); | |
367 | ||
368 | int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, | |
369 | unsigned int notification) | |
370 | { | |
371 | struct media_entity_graph *graph = &link->graph_obj.mdev->pm_count_walk; | |
372 | struct media_entity *source = link->source->entity; | |
373 | struct media_entity *sink = link->sink->entity; | |
374 | int source_use; | |
375 | int sink_use; | |
376 | int ret = 0; | |
377 | ||
378 | source_use = pipeline_pm_use_count(source, graph); | |
379 | sink_use = pipeline_pm_use_count(sink, graph); | |
380 | ||
381 | if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && | |
382 | !(flags & MEDIA_LNK_FL_ENABLED)) { | |
383 | /* Powering off entities is assumed to never fail. */ | |
384 | pipeline_pm_power(source, -sink_use, graph); | |
385 | pipeline_pm_power(sink, -source_use, graph); | |
386 | return 0; | |
387 | } | |
388 | ||
389 | if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && | |
390 | (flags & MEDIA_LNK_FL_ENABLED)) { | |
391 | ||
392 | ret = pipeline_pm_power(source, sink_use, graph); | |
393 | if (ret < 0) | |
394 | return ret; | |
395 | ||
396 | ret = pipeline_pm_power(sink, source_use, graph); | |
397 | if (ret < 0) | |
398 | pipeline_pm_power(source, -sink_use, graph); | |
399 | } | |
400 | ||
401 | return ret; | |
402 | } | |
403 | EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify); |