1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <linux/module.h>
21#include <linux/pci.h>
22#include <linux/usb.h>
23#include <media/media-device.h>
24#include <media/media-entity.h>
25#include <media/v4l2-fh.h>
26#include <media/v4l2-mc.h>
27#include <media/v4l2-subdev.h>
28#include <media/media-device.h>
29#include <media/v4l2-mc.h>
30#include <media/videobuf2-core.h>
31
32int v4l2_mc_create_media_graph(struct media_device *mdev)
33
34{
35 struct media_entity *entity;
36 struct media_entity *if_vid = NULL, *if_aud = NULL;
37 struct media_entity *tuner = NULL, *decoder = NULL;
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:
70 is_webcam = true;
71 break;
72 }
73 }
74
75
76 if (!io_v4l && !io_vbi && !io_swradio)
77 return -EINVAL;
78
79
80
81
82
83
84
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
104 if (!decoder)
105 return -EINVAL;
106
107
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
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
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:
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 }
193
194 return 0;
195}
196EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
197
198int 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}
210EXPORT_SYMBOL_GPL(v4l_enable_media_source);
211
212void 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}
219EXPORT_SYMBOL_GPL(v4l_disable_media_source);
220
221int v4l_vb2q_enable_media_source(struct vb2_queue *q)
222{
223 struct v4l2_fh *fh = q->owner;
224
225 if (fh && fh->vdev)
226 return v4l_enable_media_source(fh->vdev);
227 return 0;
228}
229EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258static 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
275
276
277
278
279
280
281
282
283
284static 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
309
310
311
312
313
314
315
316
317static 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
345int 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
354 entity->use_count += change;
355 WARN_ON(entity->use_count < 0);
356
357
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}
366EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use);
367
368int 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
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}
403EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
404