xref: /linux/drivers/media/v4l2-core/v4l2-mc.c (revision 791d3ef2e11100449837dc0b6fe884e60ca3a484)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /*
4  * Media Controller ancillary functions
5  *
6  * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@kernel.org>
7  * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com>
8  * Copyright (C) 2006-2010 Nokia Corporation
9  * Copyright (c) 2016 Intel Corporation.
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 
22 int 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 	/* It should have at least one I/O entity */
66 	if (!io_v4l && !io_vbi && !io_swradio)
67 		return -EINVAL;
68 
69 	/*
70 	 * Here, webcams are modelled on a very simple way: the sensor is
71 	 * connected directly to the I/O entity. All dirty details, like
72 	 * scaler and crop HW are hidden. While such mapping is not enough
73 	 * for mc-centric hardware, it is enough for v4l2 interface centric
74 	 * PC-consumer's hardware.
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 	/* The device isn't a webcam. So, it should have a decoder */
94 	if (!decoder)
95 		return -EINVAL;
96 
97 	/* Link the tuner and IF video output pads */
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 	/* Create demod to V4L, VBI and SDR radio links */
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 	/* Create links for the media connectors */
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 }
186 EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
187 
188 int 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;
202 end:
203 	mutex_unlock(&mdev->graph_mutex);
204 	return ret;
205 }
206 EXPORT_SYMBOL_GPL(v4l_enable_media_source);
207 
208 void 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 }
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 
225 	if (fh && fh->vdev)
226 		return v4l_enable_media_source(fh->vdev);
227 	return 0;
228 }
229 EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
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_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  * 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_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 
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_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);
404