1
2
3
4
5
6
7
8
9
10
11
12#include <linux/module.h>
13#include <linux/pci.h>
14#include <linux/usb.h>
15#include <media/media-device.h>
16#include <media/media-entity.h>
17#include <media/v4l2-fh.h>
18#include <media/v4l2-mc.h>
19#include <media/v4l2-subdev.h>
20#include <media/videobuf2-core.h>
21
22int v4l2_mc_create_media_graph(struct media_device *mdev)
23
24{
25 struct media_entity *entity;
26 struct media_entity *if_vid = NULL, *if_aud = NULL;
27 struct media_entity *tuner = NULL, *decoder = NULL;
28 struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;
29 bool is_webcam = false;
30 u32 flags;
31 int ret;
32
33 if (!mdev)
34 return 0;
35
36 media_device_for_each_entity(entity, mdev) {
37 switch (entity->function) {
38 case MEDIA_ENT_F_IF_VID_DECODER:
39 if_vid = entity;
40 break;
41 case MEDIA_ENT_F_IF_AUD_DECODER:
42 if_aud = entity;
43 break;
44 case MEDIA_ENT_F_TUNER:
45 tuner = entity;
46 break;
47 case MEDIA_ENT_F_ATV_DECODER:
48 decoder = entity;
49 break;
50 case MEDIA_ENT_F_IO_V4L:
51 io_v4l = entity;
52 break;
53 case MEDIA_ENT_F_IO_VBI:
54 io_vbi = entity;
55 break;
56 case MEDIA_ENT_F_IO_SWRADIO:
57 io_swradio = entity;
58 break;
59 case MEDIA_ENT_F_CAM_SENSOR:
60 is_webcam = true;
61 break;
62 }
63 }
64
65
66 if (!io_v4l && !io_vbi && !io_swradio)
67 return -EINVAL;
68
69
70
71
72
73
74
75
76 if (is_webcam) {
77 if (!io_v4l)
78 return -EINVAL;
79
80 media_device_for_each_entity(entity, mdev) {
81 if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
82 continue;
83 ret = media_create_pad_link(entity, 0,
84 io_v4l, 0,
85 MEDIA_LNK_FL_ENABLED);
86 if (ret)
87 return ret;
88 }
89 if (!decoder)
90 return 0;
91 }
92
93
94 if (!decoder)
95 return -EINVAL;
96
97
98 if (tuner) {
99 if (if_vid) {
100 ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
101 if_vid,
102 IF_VID_DEC_PAD_IF_INPUT,
103 MEDIA_LNK_FL_ENABLED);
104 if (ret)
105 return ret;
106 ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT,
107 decoder, DEMOD_PAD_IF_INPUT,
108 MEDIA_LNK_FL_ENABLED);
109 if (ret)
110 return ret;
111 } else {
112 ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
113 decoder, DEMOD_PAD_IF_INPUT,
114 MEDIA_LNK_FL_ENABLED);
115 if (ret)
116 return ret;
117 }
118
119 if (if_aud) {
120 ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT,
121 if_aud,
122 IF_AUD_DEC_PAD_IF_INPUT,
123 MEDIA_LNK_FL_ENABLED);
124 if (ret)
125 return ret;
126 } else {
127 if_aud = tuner;
128 }
129
130 }
131
132
133 if (io_v4l) {
134 ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
135 io_v4l, 0,
136 MEDIA_LNK_FL_ENABLED);
137 if (ret)
138 return ret;
139 }
140
141 if (io_swradio) {
142 ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
143 io_swradio, 0,
144 MEDIA_LNK_FL_ENABLED);
145 if (ret)
146 return ret;
147 }
148
149 if (io_vbi) {
150 ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT,
151 io_vbi, 0,
152 MEDIA_LNK_FL_ENABLED);
153 if (ret)
154 return ret;
155 }
156
157
158 flags = MEDIA_LNK_FL_ENABLED;
159 media_device_for_each_entity(entity, mdev) {
160 switch (entity->function) {
161 case MEDIA_ENT_F_CONN_RF:
162 if (!tuner)
163 continue;
164
165 ret = media_create_pad_link(entity, 0, tuner,
166 TUNER_PAD_RF_INPUT,
167 flags);
168 break;
169 case MEDIA_ENT_F_CONN_SVIDEO:
170 case MEDIA_ENT_F_CONN_COMPOSITE:
171 ret = media_create_pad_link(entity, 0, decoder,
172 DEMOD_PAD_IF_INPUT,
173 flags);
174 break;
175 default:
176 continue;
177 }
178 if (ret)
179 return ret;
180
181 flags = 0;
182 }
183
184 return 0;
185}
186EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
187
188int v4l_enable_media_source(struct video_device *vdev)
189{
190 struct media_device *mdev = vdev->entity.graph_obj.mdev;
191 int ret = 0, err;
192
193 if (!mdev)
194 return 0;
195
196 mutex_lock(&mdev->graph_mutex);
197 if (!mdev->enable_source)
198 goto end;
199 err = mdev->enable_source(&vdev->entity, &vdev->pipe);
200 if (err)
201 ret = -EBUSY;
202end:
203 mutex_unlock(&mdev->graph_mutex);
204 return ret;
205}
206EXPORT_SYMBOL_GPL(v4l_enable_media_source);
207
208void v4l_disable_media_source(struct video_device *vdev)
209{
210 struct media_device *mdev = vdev->entity.graph_obj.mdev;
211
212 if (mdev) {
213 mutex_lock(&mdev->graph_mutex);
214 if (mdev->disable_source)
215 mdev->disable_source(&vdev->entity);
216 mutex_unlock(&mdev->graph_mutex);
217 }
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_graph *graph)
260{
261 int use = 0;
262
263 media_graph_walk_start(graph, entity);
264
265 while ((entity = media_graph_walk_next(graph))) {
266 if (is_media_entity_v4l2_video_device(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_graph *graph)
319{
320 struct media_entity *first = entity;
321 int ret = 0;
322
323 if (!change)
324 return 0;
325
326 media_graph_walk_start(graph, entity);
327
328 while (!ret && (entity = media_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_graph_walk_start(graph, first);
336
337 while ((first = media_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_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