xref: /openbmc/linux/drivers/media/v4l2-core/v4l2-mc.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1f61c7bd9SShuah Khan // SPDX-License-Identifier: GPL-2.0-or-later
2f61c7bd9SShuah Khan 
354d0dbacSMauro Carvalho Chehab /*
454d0dbacSMauro Carvalho Chehab  * Media Controller ancillary functions
554d0dbacSMauro Carvalho Chehab  *
65dc8a864SMauro Carvalho Chehab  * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@kernel.org>
7d0a164f5SShuah Khan  * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com>
876413791SSakari Ailus  * Copyright (C) 2006-2010 Nokia Corporation
976413791SSakari Ailus  * Copyright (c) 2016 Intel Corporation.
1054d0dbacSMauro Carvalho Chehab  */
1154d0dbacSMauro Carvalho Chehab 
1254d0dbacSMauro Carvalho Chehab #include <linux/module.h>
1376413791SSakari Ailus #include <linux/pci.h>
1476413791SSakari Ailus #include <linux/usb.h>
15d0a164f5SShuah Khan #include <media/media-device.h>
1676413791SSakari Ailus #include <media/media-entity.h>
17d0a164f5SShuah Khan #include <media/v4l2-fh.h>
1854d0dbacSMauro Carvalho Chehab #include <media/v4l2-mc.h>
1976413791SSakari Ailus #include <media/v4l2-subdev.h>
20d0a164f5SShuah Khan #include <media/videobuf2-core.h>
2154d0dbacSMauro Carvalho Chehab 
v4l2_mc_create_media_graph(struct media_device * mdev)2254d0dbacSMauro Carvalho Chehab int v4l2_mc_create_media_graph(struct media_device *mdev)
2354d0dbacSMauro Carvalho Chehab 
2454d0dbacSMauro Carvalho Chehab {
2554d0dbacSMauro Carvalho Chehab 	struct media_entity *entity;
26153d41a1SMauro Carvalho Chehab 	struct media_entity *if_vid = NULL, *if_aud = NULL;
2717084b7eSLinus Torvalds 	struct media_entity *tuner = NULL, *decoder = NULL;
2854d0dbacSMauro Carvalho Chehab 	struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;
2954d0dbacSMauro Carvalho Chehab 	bool is_webcam = false;
3054d0dbacSMauro Carvalho Chehab 	u32 flags;
319d6d20e6SMauro Carvalho Chehab 	int ret, pad_sink, pad_source;
3254d0dbacSMauro Carvalho Chehab 
3354d0dbacSMauro Carvalho Chehab 	if (!mdev)
3454d0dbacSMauro Carvalho Chehab 		return 0;
3554d0dbacSMauro Carvalho Chehab 
3654d0dbacSMauro Carvalho Chehab 	media_device_for_each_entity(entity, mdev) {
3754d0dbacSMauro Carvalho Chehab 		switch (entity->function) {
3854d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_IF_VID_DECODER:
3954d0dbacSMauro Carvalho Chehab 			if_vid = entity;
4054d0dbacSMauro Carvalho Chehab 			break;
4154d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_IF_AUD_DECODER:
4254d0dbacSMauro Carvalho Chehab 			if_aud = entity;
4354d0dbacSMauro Carvalho Chehab 			break;
4454d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_TUNER:
4554d0dbacSMauro Carvalho Chehab 			tuner = entity;
4654d0dbacSMauro Carvalho Chehab 			break;
4754d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_ATV_DECODER:
4854d0dbacSMauro Carvalho Chehab 			decoder = entity;
4954d0dbacSMauro Carvalho Chehab 			break;
5054d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_IO_V4L:
5154d0dbacSMauro Carvalho Chehab 			io_v4l = entity;
5254d0dbacSMauro Carvalho Chehab 			break;
5354d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_IO_VBI:
5454d0dbacSMauro Carvalho Chehab 			io_vbi = entity;
5554d0dbacSMauro Carvalho Chehab 			break;
5654d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_IO_SWRADIO:
5754d0dbacSMauro Carvalho Chehab 			io_swradio = entity;
5854d0dbacSMauro Carvalho Chehab 			break;
5954d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_CAM_SENSOR:
6054d0dbacSMauro Carvalho Chehab 			is_webcam = true;
6154d0dbacSMauro Carvalho Chehab 			break;
6254d0dbacSMauro Carvalho Chehab 		}
6354d0dbacSMauro Carvalho Chehab 	}
6454d0dbacSMauro Carvalho Chehab 
6554d0dbacSMauro Carvalho Chehab 	/* It should have at least one I/O entity */
66caf276beSMauro Carvalho Chehab 	if (!io_v4l && !io_vbi && !io_swradio) {
67caf276beSMauro Carvalho Chehab 		dev_warn(mdev->dev, "Didn't find any I/O entity\n");
6854d0dbacSMauro Carvalho Chehab 		return -EINVAL;
69caf276beSMauro Carvalho Chehab 	}
7054d0dbacSMauro Carvalho Chehab 
7154d0dbacSMauro Carvalho Chehab 	/*
7254d0dbacSMauro Carvalho Chehab 	 * Here, webcams are modelled on a very simple way: the sensor is
7354d0dbacSMauro Carvalho Chehab 	 * connected directly to the I/O entity. All dirty details, like
7454d0dbacSMauro Carvalho Chehab 	 * scaler and crop HW are hidden. While such mapping is not enough
7554d0dbacSMauro Carvalho Chehab 	 * for mc-centric hardware, it is enough for v4l2 interface centric
7654d0dbacSMauro Carvalho Chehab 	 * PC-consumer's hardware.
7754d0dbacSMauro Carvalho Chehab 	 */
7854d0dbacSMauro Carvalho Chehab 	if (is_webcam) {
79caf276beSMauro Carvalho Chehab 		if (!io_v4l) {
80caf276beSMauro Carvalho Chehab 			dev_warn(mdev->dev, "Didn't find a MEDIA_ENT_F_IO_V4L\n");
8154d0dbacSMauro Carvalho Chehab 			return -EINVAL;
82caf276beSMauro Carvalho Chehab 		}
8354d0dbacSMauro Carvalho Chehab 
8454d0dbacSMauro Carvalho Chehab 		media_device_for_each_entity(entity, mdev) {
8554d0dbacSMauro Carvalho Chehab 			if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
8654d0dbacSMauro Carvalho Chehab 				continue;
8754d0dbacSMauro Carvalho Chehab 			ret = media_create_pad_link(entity, 0,
8854d0dbacSMauro Carvalho Chehab 						    io_v4l, 0,
8954d0dbacSMauro Carvalho Chehab 						    MEDIA_LNK_FL_ENABLED);
90caf276beSMauro Carvalho Chehab 			if (ret) {
91caf276beSMauro Carvalho Chehab 				dev_warn(mdev->dev, "Failed to create a sensor link\n");
9254d0dbacSMauro Carvalho Chehab 				return ret;
9354d0dbacSMauro Carvalho Chehab 			}
94caf276beSMauro Carvalho Chehab 		}
9554d0dbacSMauro Carvalho Chehab 		if (!decoder)
9654d0dbacSMauro Carvalho Chehab 			return 0;
9754d0dbacSMauro Carvalho Chehab 	}
9854d0dbacSMauro Carvalho Chehab 
9954d0dbacSMauro Carvalho Chehab 	/* The device isn't a webcam. So, it should have a decoder */
100caf276beSMauro Carvalho Chehab 	if (!decoder) {
101caf276beSMauro Carvalho Chehab 		dev_warn(mdev->dev, "Decoder not found\n");
10254d0dbacSMauro Carvalho Chehab 		return -EINVAL;
103caf276beSMauro Carvalho Chehab 	}
10454d0dbacSMauro Carvalho Chehab 
10554d0dbacSMauro Carvalho Chehab 	/* Link the tuner and IF video output pads */
10654d0dbacSMauro Carvalho Chehab 	if (tuner) {
10754d0dbacSMauro Carvalho Chehab 		if (if_vid) {
1089d6d20e6SMauro Carvalho Chehab 			pad_source = media_get_pad_index(tuner,
1099d6d20e6SMauro Carvalho Chehab 							 MEDIA_PAD_FL_SOURCE,
1109d6d20e6SMauro Carvalho Chehab 							 PAD_SIGNAL_ANALOG);
1119d6d20e6SMauro Carvalho Chehab 			pad_sink = media_get_pad_index(if_vid,
112caf276beSMauro Carvalho Chehab 						       MEDIA_PAD_FL_SINK,
113caf276beSMauro Carvalho Chehab 						       PAD_SIGNAL_ANALOG);
114caf276beSMauro Carvalho Chehab 			if (pad_source < 0 || pad_sink < 0) {
1159d6d20e6SMauro Carvalho Chehab 				dev_warn(mdev->dev, "Couldn't get tuner and/or PLL pad(s): (%d, %d)\n",
116caf276beSMauro Carvalho Chehab 					 pad_source, pad_sink);
1179d6d20e6SMauro Carvalho Chehab 				return -EINVAL;
1189d6d20e6SMauro Carvalho Chehab 			}
11954d0dbacSMauro Carvalho Chehab 			ret = media_create_pad_link(tuner, pad_source,
120caf276beSMauro Carvalho Chehab 						    if_vid, pad_sink,
121caf276beSMauro Carvalho Chehab 						    MEDIA_LNK_FL_ENABLED);
12254d0dbacSMauro Carvalho Chehab 			if (ret) {
123caf276beSMauro Carvalho Chehab 				dev_warn(mdev->dev, "Couldn't create tuner->PLL link)\n");
1249d6d20e6SMauro Carvalho Chehab 				return ret;
1259d6d20e6SMauro Carvalho Chehab 			}
1269d6d20e6SMauro Carvalho Chehab 
1279d6d20e6SMauro Carvalho Chehab 			pad_source = media_get_pad_index(if_vid,
1289d6d20e6SMauro Carvalho Chehab 							 MEDIA_PAD_FL_SOURCE,
129caf276beSMauro Carvalho Chehab 							 PAD_SIGNAL_ANALOG);
130caf276beSMauro Carvalho Chehab 			pad_sink = media_get_pad_index(decoder,
131caf276beSMauro Carvalho Chehab 						       MEDIA_PAD_FL_SINK,
1329d6d20e6SMauro Carvalho Chehab 						       PAD_SIGNAL_ANALOG);
133caf276beSMauro Carvalho Chehab 			if (pad_source < 0 || pad_sink < 0) {
1349d6d20e6SMauro Carvalho Chehab 				dev_warn(mdev->dev, "get decoder and/or PLL pad(s): (%d, %d)\n",
1359d6d20e6SMauro Carvalho Chehab 					 pad_source, pad_sink);
13654d0dbacSMauro Carvalho Chehab 				return -EINVAL;
137caf276beSMauro Carvalho Chehab 			}
138caf276beSMauro Carvalho Chehab 			ret = media_create_pad_link(if_vid, pad_source,
13954d0dbacSMauro Carvalho Chehab 						    decoder, pad_sink,
140caf276beSMauro Carvalho Chehab 						    MEDIA_LNK_FL_ENABLED);
14154d0dbacSMauro Carvalho Chehab 			if (ret) {
1429d6d20e6SMauro Carvalho Chehab 				dev_warn(mdev->dev, "couldn't link PLL to decoder\n");
1439d6d20e6SMauro Carvalho Chehab 				return ret;
1449d6d20e6SMauro Carvalho Chehab 			}
1459d6d20e6SMauro Carvalho Chehab 		} else {
146caf276beSMauro Carvalho Chehab 			pad_source = media_get_pad_index(tuner,
147caf276beSMauro Carvalho Chehab 							 MEDIA_PAD_FL_SOURCE,
148caf276beSMauro Carvalho Chehab 							 PAD_SIGNAL_ANALOG);
1499d6d20e6SMauro Carvalho Chehab 			pad_sink = media_get_pad_index(decoder,
150caf276beSMauro Carvalho Chehab 						       MEDIA_PAD_FL_SINK,
1519d6d20e6SMauro Carvalho Chehab 						       PAD_SIGNAL_ANALOG);
1529d6d20e6SMauro Carvalho Chehab 			if (pad_source < 0 || pad_sink < 0) {
15354d0dbacSMauro Carvalho Chehab 				dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s): (%d, %d)\n",
15454d0dbacSMauro Carvalho Chehab 					 pad_source, pad_sink);
15554d0dbacSMauro Carvalho Chehab 				return -EINVAL;
15654d0dbacSMauro Carvalho Chehab 			}
15754d0dbacSMauro Carvalho Chehab 			ret = media_create_pad_link(tuner, pad_source,
15854d0dbacSMauro Carvalho Chehab 						    decoder, pad_sink,
1599d6d20e6SMauro Carvalho Chehab 						    MEDIA_LNK_FL_ENABLED);
1609d6d20e6SMauro Carvalho Chehab 			if (ret)
1619d6d20e6SMauro Carvalho Chehab 				return ret;
1629d6d20e6SMauro Carvalho Chehab 		}
163caf276beSMauro Carvalho Chehab 
164caf276beSMauro Carvalho Chehab 		if (if_aud) {
165caf276beSMauro Carvalho Chehab 			pad_source = media_get_pad_index(tuner,
1669d6d20e6SMauro Carvalho Chehab 							 MEDIA_PAD_FL_SOURCE,
167caf276beSMauro Carvalho Chehab 							 PAD_SIGNAL_AUDIO);
1689d6d20e6SMauro Carvalho Chehab 			pad_sink = media_get_pad_index(if_aud,
1699d6d20e6SMauro Carvalho Chehab 						       MEDIA_PAD_FL_SINK,
17054d0dbacSMauro Carvalho Chehab 						       PAD_SIGNAL_AUDIO);
171caf276beSMauro Carvalho Chehab 			if (pad_source < 0 || pad_sink < 0) {
172caf276beSMauro Carvalho Chehab 				dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s) for audio: (%d, %d)\n",
17354d0dbacSMauro Carvalho Chehab 					 pad_source, pad_sink);
174caf276beSMauro Carvalho Chehab 				return -EINVAL;
17554d0dbacSMauro Carvalho Chehab 			}
17654d0dbacSMauro Carvalho Chehab 			ret = media_create_pad_link(tuner, pad_source,
17754d0dbacSMauro Carvalho Chehab 						    if_aud, pad_sink,
17854d0dbacSMauro Carvalho Chehab 						    MEDIA_LNK_FL_ENABLED);
17954d0dbacSMauro Carvalho Chehab 			if (ret) {
18054d0dbacSMauro Carvalho Chehab 				dev_warn(mdev->dev, "couldn't link tuner->audio PLL\n");
18154d0dbacSMauro Carvalho Chehab 				return ret;
18254d0dbacSMauro Carvalho Chehab 			}
1839d6d20e6SMauro Carvalho Chehab 		} else {
184caf276beSMauro Carvalho Chehab 			if_aud = tuner;
185caf276beSMauro Carvalho Chehab 		}
1869d6d20e6SMauro Carvalho Chehab 
187caf276beSMauro Carvalho Chehab 	}
1889d6d20e6SMauro Carvalho Chehab 
18954d0dbacSMauro Carvalho Chehab 	/* Create demod to V4L, VBI and SDR radio links */
19054d0dbacSMauro Carvalho Chehab 	if (io_v4l) {
191caf276beSMauro Carvalho Chehab 		pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
192caf276beSMauro Carvalho Chehab 						 PAD_SIGNAL_DV);
19354d0dbacSMauro Carvalho Chehab 		if (pad_source < 0) {
19454d0dbacSMauro Carvalho Chehab 			dev_warn(mdev->dev, "couldn't get decoder output pad for V4L I/O\n");
195caf276beSMauro Carvalho Chehab 			return -EINVAL;
19654d0dbacSMauro Carvalho Chehab 		}
19754d0dbacSMauro Carvalho Chehab 		ret = media_create_pad_link(decoder, pad_source,
1989d6d20e6SMauro Carvalho Chehab 					    io_v4l, 0,
199caf276beSMauro Carvalho Chehab 					    MEDIA_LNK_FL_ENABLED);
200caf276beSMauro Carvalho Chehab 		if (ret) {
2019d6d20e6SMauro Carvalho Chehab 			dev_warn(mdev->dev, "couldn't link decoder output to V4L I/O\n");
202caf276beSMauro Carvalho Chehab 			return ret;
2039d6d20e6SMauro Carvalho Chehab 		}
20454d0dbacSMauro Carvalho Chehab 	}
20554d0dbacSMauro Carvalho Chehab 
206caf276beSMauro Carvalho Chehab 	if (io_swradio) {
207caf276beSMauro Carvalho Chehab 		pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
20854d0dbacSMauro Carvalho Chehab 						 PAD_SIGNAL_DV);
20954d0dbacSMauro Carvalho Chehab 		if (pad_source < 0) {
210caf276beSMauro Carvalho Chehab 			dev_warn(mdev->dev, "couldn't get decoder output pad for SDR\n");
21154d0dbacSMauro Carvalho Chehab 			return -EINVAL;
21254d0dbacSMauro Carvalho Chehab 		}
2139d6d20e6SMauro Carvalho Chehab 		ret = media_create_pad_link(decoder, pad_source,
214caf276beSMauro Carvalho Chehab 					    io_swradio, 0,
215caf276beSMauro Carvalho Chehab 					    MEDIA_LNK_FL_ENABLED);
2169d6d20e6SMauro Carvalho Chehab 		if (ret) {
217caf276beSMauro Carvalho Chehab 			dev_warn(mdev->dev, "couldn't link decoder output to SDR\n");
2189d6d20e6SMauro Carvalho Chehab 			return ret;
21954d0dbacSMauro Carvalho Chehab 		}
22054d0dbacSMauro Carvalho Chehab 	}
221caf276beSMauro Carvalho Chehab 
222caf276beSMauro Carvalho Chehab 	if (io_vbi) {
22354d0dbacSMauro Carvalho Chehab 		pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,
22454d0dbacSMauro Carvalho Chehab 						 PAD_SIGNAL_DV);
225caf276beSMauro Carvalho Chehab 		if (pad_source < 0) {
22654d0dbacSMauro Carvalho Chehab 			dev_warn(mdev->dev, "couldn't get decoder output pad for VBI\n");
22754d0dbacSMauro Carvalho Chehab 			return -EINVAL;
22854d0dbacSMauro Carvalho Chehab 		}
22954d0dbacSMauro Carvalho Chehab 		ret = media_create_pad_link(decoder, pad_source,
23054d0dbacSMauro Carvalho Chehab 					    io_vbi, 0,
23154d0dbacSMauro Carvalho Chehab 					    MEDIA_LNK_FL_ENABLED);
23254d0dbacSMauro Carvalho Chehab 		if (ret) {
23354d0dbacSMauro Carvalho Chehab 			dev_warn(mdev->dev, "couldn't link decoder output to VBI\n");
2349d6d20e6SMauro Carvalho Chehab 			return ret;
2359d6d20e6SMauro Carvalho Chehab 		}
236caf276beSMauro Carvalho Chehab 	}
237caf276beSMauro Carvalho Chehab 
2389d6d20e6SMauro Carvalho Chehab 	/* Create links for the media connectors */
239caf276beSMauro Carvalho Chehab 	flags = MEDIA_LNK_FL_ENABLED;
24054d0dbacSMauro Carvalho Chehab 	media_device_for_each_entity(entity, mdev) {
2419d6d20e6SMauro Carvalho Chehab 		switch (entity->function) {
24254d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_CONN_RF:
24354d0dbacSMauro Carvalho Chehab 			if (!tuner)
24454d0dbacSMauro Carvalho Chehab 				continue;
24554d0dbacSMauro Carvalho Chehab 			pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK,
2469d6d20e6SMauro Carvalho Chehab 						       PAD_SIGNAL_ANALOG);
2479d6d20e6SMauro Carvalho Chehab 			if (pad_sink < 0) {
248caf276beSMauro Carvalho Chehab 				dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n");
2491ef5b9b8SAntti Keränen 				return -EINVAL;
2509d6d20e6SMauro Carvalho Chehab 			}
251caf276beSMauro Carvalho Chehab 			ret = media_create_pad_link(entity, 0, tuner,
25254d0dbacSMauro Carvalho Chehab 						    pad_sink,
2539d6d20e6SMauro Carvalho Chehab 						    flags);
25454d0dbacSMauro Carvalho Chehab 			break;
25554d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_CONN_SVIDEO:
25654d0dbacSMauro Carvalho Chehab 		case MEDIA_ENT_F_CONN_COMPOSITE:
25754d0dbacSMauro Carvalho Chehab 			pad_sink = media_get_pad_index(decoder,
25854d0dbacSMauro Carvalho Chehab 						       MEDIA_PAD_FL_SINK,
25954d0dbacSMauro Carvalho Chehab 						       PAD_SIGNAL_ANALOG);
26054d0dbacSMauro Carvalho Chehab 			if (pad_sink < 0) {
26154d0dbacSMauro Carvalho Chehab 				dev_warn(mdev->dev, "couldn't get decoder analog pad sink\n");
26254d0dbacSMauro Carvalho Chehab 				return -EINVAL;
26354d0dbacSMauro Carvalho Chehab 			}
2649822f417SMauro Carvalho Chehab 			ret = media_create_pad_link(entity, 0, decoder,
26554d0dbacSMauro Carvalho Chehab 						    pad_sink,
26654d0dbacSMauro Carvalho Chehab 						    flags);
26754d0dbacSMauro Carvalho Chehab 			break;
268d0a164f5SShuah Khan 		default:
269d0a164f5SShuah Khan 			continue;
270d0a164f5SShuah Khan 		}
271d0a164f5SShuah Khan 		if (ret)
27290cd366bSShuah Khan 			return ret;
273d0a164f5SShuah Khan 
27490cd366bSShuah Khan 		flags = 0;
275d0a164f5SShuah Khan 	}
27690cd366bSShuah Khan 
27790cd366bSShuah Khan 	return 0;
27890cd366bSShuah Khan }
27990cd366bSShuah Khan EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
28090cd366bSShuah Khan 
v4l_enable_media_source(struct video_device * vdev)28190cd366bSShuah Khan int v4l_enable_media_source(struct video_device *vdev)
28290cd366bSShuah Khan {
28390cd366bSShuah Khan 	struct media_device *mdev = vdev->entity.graph_obj.mdev;
28490cd366bSShuah Khan 	int ret = 0, err;
28590cd366bSShuah Khan 
286d0a164f5SShuah Khan 	if (!mdev)
287d0a164f5SShuah Khan 		return 0;
288d0a164f5SShuah Khan 
289d0a164f5SShuah Khan 	mutex_lock(&mdev->graph_mutex);
290d0a164f5SShuah Khan 	if (!mdev->enable_source)
291d0a164f5SShuah Khan 		goto end;
292d0a164f5SShuah Khan 	err = mdev->enable_source(&vdev->entity, &vdev->pipe);
29390cd366bSShuah Khan 	if (err)
29490cd366bSShuah Khan 		ret = -EBUSY;
29590cd366bSShuah Khan end:
296d0a164f5SShuah Khan 	mutex_unlock(&mdev->graph_mutex);
29790cd366bSShuah Khan 	return ret;
29890cd366bSShuah Khan }
299d0a164f5SShuah Khan EXPORT_SYMBOL_GPL(v4l_enable_media_source);
300d0a164f5SShuah Khan 
v4l_disable_media_source(struct video_device * vdev)301d0a164f5SShuah Khan void v4l_disable_media_source(struct video_device *vdev)
302d0a164f5SShuah Khan {
303d0a164f5SShuah Khan 	struct media_device *mdev = vdev->entity.graph_obj.mdev;
304d0a164f5SShuah Khan 
305d0a164f5SShuah Khan 	if (mdev) {
3067e53898aSShuah Khan 		mutex_lock(&mdev->graph_mutex);
307d0a164f5SShuah Khan 		if (mdev->disable_source)
3087e53898aSShuah Khan 			mdev->disable_source(&vdev->entity);
309d0a164f5SShuah Khan 		mutex_unlock(&mdev->graph_mutex);
310d0a164f5SShuah Khan 	}
31176413791SSakari Ailus }
3120d3c81e8SSteve Longerbeam EXPORT_SYMBOL_GPL(v4l_disable_media_source);
313dbedd2f4SLaurent Pinchart 
v4l_vb2q_enable_media_source(struct vb2_queue * q)3140d3c81e8SSteve Longerbeam int v4l_vb2q_enable_media_source(struct vb2_queue *q)
3150d3c81e8SSteve Longerbeam {
3160d3c81e8SSteve Longerbeam 	struct v4l2_fh *fh = q->owner;
317*3a4cdef1SVaishnav Achath 
3180d3c81e8SSteve Longerbeam 	if (fh && fh->vdev)
3190d3c81e8SSteve Longerbeam 		return v4l_enable_media_source(fh->vdev);
3200d3c81e8SSteve Longerbeam 	return 0;
3210d3c81e8SSteve Longerbeam }
3220d3c81e8SSteve Longerbeam EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
3230d3c81e8SSteve Longerbeam 
v4l2_create_fwnode_links_to_pad(struct v4l2_subdev * src_sd,struct media_pad * sink,u32 flags)3240d3c81e8SSteve Longerbeam int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
3250d3c81e8SSteve Longerbeam 				    struct media_pad *sink, u32 flags)
3260d3c81e8SSteve Longerbeam {
3270d3c81e8SSteve Longerbeam 	struct fwnode_handle *endpoint;
3280d3c81e8SSteve Longerbeam 
3290d3c81e8SSteve Longerbeam 	if (!(sink->flags & MEDIA_PAD_FL_SINK))
3300d3c81e8SSteve Longerbeam 		return -EINVAL;
3310d3c81e8SSteve Longerbeam 
3320d3c81e8SSteve Longerbeam 	fwnode_graph_for_each_endpoint(dev_fwnode(src_sd->dev), endpoint) {
3330d3c81e8SSteve Longerbeam 		struct fwnode_handle *remote_ep;
3340d3c81e8SSteve Longerbeam 		int src_idx, sink_idx, ret;
3350d3c81e8SSteve Longerbeam 		struct media_pad *src;
3360d3c81e8SSteve Longerbeam 
3370d3c81e8SSteve Longerbeam 		src_idx = media_entity_get_fwnode_pad(&src_sd->entity,
3380d3c81e8SSteve Longerbeam 						      endpoint,
339bd5a03bcSLaurent Pinchart 						      MEDIA_PAD_FL_SOURCE);
3400d3c81e8SSteve Longerbeam 		if (src_idx < 0)
3410d3c81e8SSteve Longerbeam 			continue;
3420d3c81e8SSteve Longerbeam 
3430d3c81e8SSteve Longerbeam 		remote_ep = fwnode_graph_get_remote_endpoint(endpoint);
3440d3c81e8SSteve Longerbeam 		if (!remote_ep)
3450d3c81e8SSteve Longerbeam 			continue;
3460d3c81e8SSteve Longerbeam 
3470d3c81e8SSteve Longerbeam 		/*
3480d3c81e8SSteve Longerbeam 		 * ask the sink to verify it owns the remote endpoint,
3490d3c81e8SSteve Longerbeam 		 * and translate to a sink pad.
3500d3c81e8SSteve Longerbeam 		 */
3510d3c81e8SSteve Longerbeam 		sink_idx = media_entity_get_fwnode_pad(sink->entity,
3520d3c81e8SSteve Longerbeam 						       remote_ep,
3530d3c81e8SSteve Longerbeam 						       MEDIA_PAD_FL_SINK);
3540d3c81e8SSteve Longerbeam 		fwnode_handle_put(remote_ep);
3550d3c81e8SSteve Longerbeam 
3560d3c81e8SSteve Longerbeam 		if (sink_idx < 0 || sink_idx != sink->index)
3570d3c81e8SSteve Longerbeam 			continue;
3580d3c81e8SSteve Longerbeam 
3590d3c81e8SSteve Longerbeam 		/*
3600d3c81e8SSteve Longerbeam 		 * the source endpoint corresponds to one of its source pads,
361bd5a03bcSLaurent Pinchart 		 * the source endpoint connects to an endpoint at the sink
3620d3c81e8SSteve Longerbeam 		 * entity, and the sink endpoint corresponds to the sink
363bd5a03bcSLaurent Pinchart 		 * pad requested, so we have found an endpoint connection
3640d3c81e8SSteve Longerbeam 		 * that works, create the media link for it.
3650d3c81e8SSteve Longerbeam 		 */
366bd5a03bcSLaurent Pinchart 
3670d3c81e8SSteve Longerbeam 		src = &src_sd->entity.pads[src_idx];
368bd5a03bcSLaurent Pinchart 
3690d3c81e8SSteve Longerbeam 		/* skip if link already exists */
3700d3c81e8SSteve Longerbeam 		if (media_entity_find_link(src, sink))
371bd5a03bcSLaurent Pinchart 			continue;
3720d3c81e8SSteve Longerbeam 
3730d3c81e8SSteve Longerbeam 		dev_dbg(src_sd->dev, "creating link %s:%d -> %s:%d\n",
3740d3c81e8SSteve Longerbeam 			src_sd->entity.name, src_idx,
3750d3c81e8SSteve Longerbeam 			sink->entity->name, sink_idx);
3760d3c81e8SSteve Longerbeam 
3770d3c81e8SSteve Longerbeam 		ret = media_create_pad_link(&src_sd->entity, src_idx,
3780d3c81e8SSteve Longerbeam 					    sink->entity, sink_idx, flags);
3790d3c81e8SSteve Longerbeam 		if (ret) {
3800d3c81e8SSteve Longerbeam 			dev_err(src_sd->dev,
3810d3c81e8SSteve Longerbeam 				"link %s:%d -> %s:%d failed with %d\n",
3820d3c81e8SSteve Longerbeam 				src_sd->entity.name, src_idx,
3830d3c81e8SSteve Longerbeam 				sink->entity->name, sink_idx, ret);
3840d3c81e8SSteve Longerbeam 
3850d3c81e8SSteve Longerbeam 			fwnode_handle_put(endpoint);
3860d3c81e8SSteve Longerbeam 			return ret;
3870d3c81e8SSteve Longerbeam 		}
3880d3c81e8SSteve Longerbeam 	}
3890d3c81e8SSteve Longerbeam 
3900d3c81e8SSteve Longerbeam 	return 0;
3910d3c81e8SSteve Longerbeam }
3920d3c81e8SSteve Longerbeam EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad);
3930d3c81e8SSteve Longerbeam 
v4l2_create_fwnode_links(struct v4l2_subdev * src_sd,struct v4l2_subdev * sink_sd)394dbedd2f4SLaurent Pinchart int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd,
3950d3c81e8SSteve Longerbeam 			     struct v4l2_subdev *sink_sd)
3960d3c81e8SSteve Longerbeam {
3970d3c81e8SSteve Longerbeam 	unsigned int i;
3980d3c81e8SSteve Longerbeam 
3990d3c81e8SSteve Longerbeam 	for (i = 0; i < sink_sd->entity.num_pads; i++) {
4000d3c81e8SSteve Longerbeam 		struct media_pad *pad = &sink_sd->entity.pads[i];
4010d3c81e8SSteve Longerbeam 		int ret;
4020d3c81e8SSteve Longerbeam 
40376413791SSakari Ailus 		if (!(pad->flags & MEDIA_PAD_FL_SINK))
40476413791SSakari Ailus 			continue;
40576413791SSakari Ailus 
40676413791SSakari Ailus 		ret = v4l2_create_fwnode_links_to_pad(src_sd, pad, 0);
40776413791SSakari Ailus 		if (ret)
40876413791SSakari Ailus 			return ret;
40976413791SSakari Ailus 	}
41076413791SSakari Ailus 
41176413791SSakari Ailus 	return 0;
41276413791SSakari Ailus }
41376413791SSakari Ailus EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links);
41476413791SSakari Ailus 
4158fd390b8SEzequiel Garcia /* -----------------------------------------------------------------------------
41676413791SSakari Ailus  * Pipeline power management
41776413791SSakari Ailus  *
41876413791SSakari Ailus  * Entities must be powered up when part of a pipeline that contains at least
41976413791SSakari Ailus  * one open video device node.
42076413791SSakari Ailus  *
42176413791SSakari Ailus  * To achieve this use the entity use_count field to track the number of users.
42276413791SSakari Ailus  * For entities corresponding to video device nodes the use_count field stores
42376413791SSakari Ailus  * the users count of the node. For entities corresponding to subdevs the
42476413791SSakari Ailus  * use_count field stores the total number of users of all video device nodes
42576413791SSakari Ailus  * in the pipeline.
42676413791SSakari Ailus  *
42776413791SSakari Ailus  * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and
42876413791SSakari Ailus  * close() handlers of video device nodes. It increments or decrements the use
42976413791SSakari Ailus  * count of all subdev entities in the pipeline.
43076413791SSakari Ailus  *
43120b85227SSakari Ailus  * To react to link management on powered pipelines, the link setup notification
43276413791SSakari Ailus  * callback updates the use count of all entities in the source and sink sides
43376413791SSakari Ailus  * of the link.
43476413791SSakari Ailus  */
43520b85227SSakari Ailus 
43676413791SSakari Ailus /*
43720b85227SSakari Ailus  * pipeline_pm_use_count - Count the number of users of a pipeline
43845b46879SLaurent Pinchart  * @entity: The entity
43976413791SSakari Ailus  *
44076413791SSakari Ailus  * Return the total number of users of all video device nodes in the pipeline.
44176413791SSakari Ailus  */
pipeline_pm_use_count(struct media_entity * entity,struct media_graph * graph)44276413791SSakari Ailus static int pipeline_pm_use_count(struct media_entity *entity,
44376413791SSakari Ailus 	struct media_graph *graph)
44476413791SSakari Ailus {
44576413791SSakari Ailus 	int use = 0;
44676413791SSakari Ailus 
44776413791SSakari Ailus 	media_graph_walk_start(graph, entity);
44876413791SSakari Ailus 
44976413791SSakari Ailus 	while ((entity = media_graph_walk_next(graph))) {
45076413791SSakari Ailus 		if (is_media_entity_v4l2_video_device(entity))
45176413791SSakari Ailus 			use += entity->use_count;
45276413791SSakari Ailus 	}
45376413791SSakari Ailus 
45476413791SSakari Ailus 	return use;
45576413791SSakari Ailus }
45676413791SSakari Ailus 
45776413791SSakari Ailus /*
45876413791SSakari Ailus  * pipeline_pm_power_one - Apply power change to an entity
45976413791SSakari Ailus  * @entity: The entity
46076413791SSakari Ailus  * @change: Use count change
46176413791SSakari Ailus  *
46276413791SSakari Ailus  * Change the entity use count by @change. If the entity is a subdev update its
46376413791SSakari Ailus  * power state by calling the core::s_power operation when the use count goes
46476413791SSakari Ailus  * from 0 to != 0 or from != 0 to 0.
46576413791SSakari Ailus  *
46676413791SSakari Ailus  * Return 0 on success or a negative error code on failure.
46776413791SSakari Ailus  */
pipeline_pm_power_one(struct media_entity * entity,int change)46876413791SSakari Ailus static int pipeline_pm_power_one(struct media_entity *entity, int change)
46976413791SSakari Ailus {
47076413791SSakari Ailus 	struct v4l2_subdev *subdev;
47176413791SSakari Ailus 	int ret;
47276413791SSakari Ailus 
47376413791SSakari Ailus 	subdev = is_media_entity_v4l2_subdev(entity)
47476413791SSakari Ailus 	       ? media_entity_to_v4l2_subdev(entity) : NULL;
47576413791SSakari Ailus 
47676413791SSakari Ailus 	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
47776413791SSakari Ailus 		ret = v4l2_subdev_call(subdev, core, s_power, 1);
47876413791SSakari Ailus 		if (ret < 0 && ret != -ENOIOCTLCMD)
47976413791SSakari Ailus 			return ret;
48076413791SSakari Ailus 	}
48176413791SSakari Ailus 
48276413791SSakari Ailus 	entity->use_count += change;
48376413791SSakari Ailus 	WARN_ON(entity->use_count < 0);
48476413791SSakari Ailus 
48576413791SSakari Ailus 	if (entity->use_count == 0 && change < 0 && subdev != NULL)
48676413791SSakari Ailus 		v4l2_subdev_call(subdev, core, s_power, 0);
48776413791SSakari Ailus 
48876413791SSakari Ailus 	return 0;
48976413791SSakari Ailus }
49020b85227SSakari Ailus 
49176413791SSakari Ailus /*
49276413791SSakari Ailus  * pipeline_pm_power - Apply power change to all entities in a pipeline
49376413791SSakari Ailus  * @entity: The entity
49476413791SSakari Ailus  * @change: Use count change
49576413791SSakari Ailus  *
49676413791SSakari Ailus  * Walk the pipeline to update the use count and the power state of all non-node
49776413791SSakari Ailus  * entities.
49820b85227SSakari Ailus  *
49976413791SSakari Ailus  * Return 0 on success or a negative error code on failure.
50020b85227SSakari Ailus  */
pipeline_pm_power(struct media_entity * entity,int change,struct media_graph * graph)50176413791SSakari Ailus static int pipeline_pm_power(struct media_entity *entity, int change,
50276413791SSakari Ailus 	struct media_graph *graph)
50376413791SSakari Ailus {
50476413791SSakari Ailus 	struct media_entity *first = entity;
50576413791SSakari Ailus 	int ret = 0;
50676413791SSakari Ailus 
50720b85227SSakari Ailus 	if (!change)
50876413791SSakari Ailus 		return 0;
50920b85227SSakari Ailus 
51076413791SSakari Ailus 	media_graph_walk_start(graph, entity);
51176413791SSakari Ailus 
51276413791SSakari Ailus 	while (!ret && (entity = media_graph_walk_next(graph)))
51376413791SSakari Ailus 		if (is_media_entity_v4l2_subdev(entity))
51476413791SSakari Ailus 			ret = pipeline_pm_power_one(entity, change);
51576413791SSakari Ailus 
51676413791SSakari Ailus 	if (!ret)
5178fd390b8SEzequiel Garcia 		return ret;
51876413791SSakari Ailus 
51976413791SSakari Ailus 	media_graph_walk_start(graph, first);
52076413791SSakari Ailus 
52176413791SSakari Ailus 	while ((first = media_graph_walk_next(graph))
52276413791SSakari Ailus 	       && first != entity)
52376413791SSakari Ailus 		if (is_media_entity_v4l2_subdev(first))
52476413791SSakari Ailus 			pipeline_pm_power_one(first, -change);
52576413791SSakari Ailus 
52676413791SSakari Ailus 	return ret;
52776413791SSakari Ailus }
52876413791SSakari Ailus 
v4l2_pipeline_pm_use(struct media_entity * entity,unsigned int use)52976413791SSakari Ailus static int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use)
53076413791SSakari Ailus {
53176413791SSakari Ailus 	struct media_device *mdev = entity->graph_obj.mdev;
53276413791SSakari Ailus 	int change = use ? 1 : -1;
53376413791SSakari Ailus 	int ret;
53476413791SSakari Ailus 
53576413791SSakari Ailus 	mutex_lock(&mdev->graph_mutex);
53676413791SSakari Ailus 
53776413791SSakari Ailus 	/* Apply use count to node. */
5388fd390b8SEzequiel Garcia 	entity->use_count += change;
5398fd390b8SEzequiel Garcia 	WARN_ON(entity->use_count < 0);
5408fd390b8SEzequiel Garcia 
5418fd390b8SEzequiel Garcia 	/* Apply power change to connected non-nodes. */
5428fd390b8SEzequiel Garcia 	ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk);
5438fd390b8SEzequiel Garcia 	if (ret < 0)
5448fd390b8SEzequiel Garcia 		entity->use_count -= change;
5458fd390b8SEzequiel Garcia 
5468fd390b8SEzequiel Garcia 	mutex_unlock(&mdev->graph_mutex);
5478fd390b8SEzequiel Garcia 
5488fd390b8SEzequiel Garcia 	return ret;
5498fd390b8SEzequiel Garcia }
5508fd390b8SEzequiel Garcia 
v4l2_pipeline_pm_get(struct media_entity * entity)55176413791SSakari Ailus int v4l2_pipeline_pm_get(struct media_entity *entity)
55276413791SSakari Ailus {
55376413791SSakari Ailus 	return v4l2_pipeline_pm_use(entity, 1);
55476413791SSakari Ailus }
55520b85227SSakari Ailus EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get);
55676413791SSakari Ailus 
v4l2_pipeline_pm_put(struct media_entity * entity)55776413791SSakari Ailus void v4l2_pipeline_pm_put(struct media_entity *entity)
55876413791SSakari Ailus {
55976413791SSakari Ailus 	/* Powering off entities shouldn't fail. */
56076413791SSakari Ailus 	WARN_ON(v4l2_pipeline_pm_use(entity, 0));
56176413791SSakari Ailus }
56276413791SSakari Ailus EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put);
56376413791SSakari Ailus 
v4l2_pipeline_link_notify(struct media_link * link,u32 flags,unsigned int notification)56476413791SSakari Ailus int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
56576413791SSakari Ailus 			      unsigned int notification)
56676413791SSakari Ailus {
56776413791SSakari Ailus 	struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk;
56876413791SSakari Ailus 	struct media_entity *source = link->source->entity;
56976413791SSakari Ailus 	struct media_entity *sink = link->sink->entity;
57076413791SSakari Ailus 	int source_use;
57176413791SSakari Ailus 	int sink_use;
57276413791SSakari Ailus 	int ret = 0;
57376413791SSakari Ailus 
57476413791SSakari Ailus 	source_use = pipeline_pm_use_count(source, graph);
57576413791SSakari Ailus 	sink_use = pipeline_pm_use_count(sink, graph);
57676413791SSakari Ailus 
57776413791SSakari Ailus 	if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
57876413791SSakari Ailus 	    !(flags & MEDIA_LNK_FL_ENABLED)) {
57976413791SSakari Ailus 		/* Powering off entities is assumed to never fail. */
58076413791SSakari Ailus 		pipeline_pm_power(source, -sink_use, graph);
58176413791SSakari Ailus 		pipeline_pm_power(sink, -source_use, graph);
58276413791SSakari Ailus 		return 0;
58376413791SSakari Ailus 	}
58476413791SSakari Ailus 
58576413791SSakari Ailus 	if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
58676413791SSakari Ailus 		(flags & MEDIA_LNK_FL_ENABLED)) {
58776413791SSakari Ailus 
588 		ret = pipeline_pm_power(source, sink_use, graph);
589 		if (ret < 0)
590 			return ret;
591 
592 		ret = pipeline_pm_power(sink, source_use, graph);
593 		if (ret < 0)
594 			pipeline_pm_power(source, -sink_use, graph);
595 	}
596 
597 	return ret;
598 }
599 EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
600