xref: /openbmc/linux/drivers/media/v4l2-core/v4l2-mc.c (revision b802fb99ae964681d1754428f67970911e0476e9)
1 /*
2  * Media Controller ancillary functions
3  *
4  * (c) 2016 Mauro Carvalho Chehab <mchehab@osg.samsung.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  */
16 
17 #include <linux/module.h>
18 #include <linux/pci.h>
19 #include <media/media-entity.h>
20 #include <media/v4l2-mc.h>
21 
22 
23 struct media_device *v4l2_mc_pci_media_device_init(struct pci_dev *pci_dev,
24 						   char *name)
25 {
26 #ifdef CONFIG_PCI
27 	struct media_device *mdev;
28 
29 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
30 	if (!mdev)
31 		return NULL;
32 
33 	mdev->dev = &pci_dev->dev;
34 
35 	if (name)
36 		strlcpy(mdev->model, name, sizeof(mdev->model));
37 	else
38 		strlcpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model));
39 
40 	sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev));
41 
42 	mdev->hw_revision = pci_dev->subsystem_vendor << 16
43 			    || pci_dev->subsystem_device;
44 
45 	mdev->driver_version = LINUX_VERSION_CODE;
46 
47 	media_device_init(mdev);
48 
49 	return mdev;
50 #else
51 	return NULL;
52 #endif
53 }
54 EXPORT_SYMBOL_GPL(v4l2_mc_pci_media_device_init);
55 
56 int v4l2_mc_create_media_graph(struct media_device *mdev)
57 
58 {
59 	struct media_entity *entity;
60 	struct media_entity *if_vid = NULL, *if_aud = NULL, *sensor = NULL;
61 	struct media_entity *tuner = NULL, *decoder = NULL;
62 	struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;
63 	bool is_webcam = false;
64 	u32 flags;
65 	int ret;
66 
67 	if (!mdev)
68 		return 0;
69 
70 	media_device_for_each_entity(entity, mdev) {
71 		switch (entity->function) {
72 		case MEDIA_ENT_F_IF_VID_DECODER:
73 			if_vid = entity;
74 			break;
75 		case MEDIA_ENT_F_IF_AUD_DECODER:
76 			if_aud = entity;
77 			break;
78 		case MEDIA_ENT_F_TUNER:
79 			tuner = entity;
80 			break;
81 		case MEDIA_ENT_F_ATV_DECODER:
82 			decoder = entity;
83 			break;
84 		case MEDIA_ENT_F_IO_V4L:
85 			io_v4l = entity;
86 			break;
87 		case MEDIA_ENT_F_IO_VBI:
88 			io_vbi = entity;
89 			break;
90 		case MEDIA_ENT_F_IO_SWRADIO:
91 			io_swradio = entity;
92 			break;
93 		case MEDIA_ENT_F_CAM_SENSOR:
94 			sensor = entity;
95 			is_webcam = true;
96 			break;
97 		}
98 	}
99 
100 	/* It should have at least one I/O entity */
101 	if (!io_v4l && !io_vbi && !io_swradio)
102 		return -EINVAL;
103 
104 	/*
105 	 * Here, webcams are modelled on a very simple way: the sensor is
106 	 * connected directly to the I/O entity. All dirty details, like
107 	 * scaler and crop HW are hidden. While such mapping is not enough
108 	 * for mc-centric hardware, it is enough for v4l2 interface centric
109 	 * PC-consumer's hardware.
110 	 */
111 	if (is_webcam) {
112 		if (!io_v4l)
113 			return -EINVAL;
114 
115 		media_device_for_each_entity(entity, mdev) {
116 			if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
117 				continue;
118 			ret = media_create_pad_link(entity, 0,
119 						    io_v4l, 0,
120 						    MEDIA_LNK_FL_ENABLED);
121 			if (ret)
122 				return ret;
123 		}
124 		if (!decoder)
125 			return 0;
126 	}
127 
128 	/* The device isn't a webcam. So, it should have a decoder */
129 	if (!decoder)
130 		return -EINVAL;
131 
132 	/* Link the tuner and IF video output pads */
133 	if (tuner) {
134 		if (if_vid) {
135 			ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
136 						    if_vid,
137 						    IF_VID_DEC_PAD_IF_INPUT,
138 						    MEDIA_LNK_FL_ENABLED);
139 			if (ret)
140 				return ret;
141 			ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT,
142 						decoder, DEMOD_PAD_IF_INPUT,
143 						MEDIA_LNK_FL_ENABLED);
144 			if (ret)
145 				return ret;
146 		} else {
147 			ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
148 						decoder, DEMOD_PAD_IF_INPUT,
149 						MEDIA_LNK_FL_ENABLED);
150 			if (ret)
151 				return ret;
152 		}
153 
154 		if (if_aud) {
155 			ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT,
156 						    if_aud,
157 						    IF_AUD_DEC_PAD_IF_INPUT,
158 						    MEDIA_LNK_FL_ENABLED);
159 			if (ret)
160 				return ret;
161 		} else {
162 			if_aud = tuner;
163 		}
164 
165 	}
166 
167 	/* Create demod to V4L, VBI and SDR radio links */
168 	if (io_v4l) {
169 		ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
170 					io_v4l, 0,
171 					MEDIA_LNK_FL_ENABLED);
172 		if (ret)
173 			return ret;
174 	}
175 
176 	if (io_swradio) {
177 		ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
178 					io_swradio, 0,
179 					MEDIA_LNK_FL_ENABLED);
180 		if (ret)
181 			return ret;
182 	}
183 
184 	if (io_vbi) {
185 		ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT,
186 					    io_vbi, 0,
187 					    MEDIA_LNK_FL_ENABLED);
188 		if (ret)
189 			return ret;
190 	}
191 
192 	/* Create links for the media connectors */
193 	flags = MEDIA_LNK_FL_ENABLED;
194 	media_device_for_each_entity(entity, mdev) {
195 		switch (entity->function) {
196 		case MEDIA_ENT_F_CONN_RF:
197 			if (!tuner)
198 				continue;
199 
200 			ret = media_create_pad_link(entity, 0, tuner,
201 						    TUNER_PAD_RF_INPUT,
202 						    flags);
203 			break;
204 		case MEDIA_ENT_F_CONN_SVIDEO:
205 		case MEDIA_ENT_F_CONN_COMPOSITE:
206 		case MEDIA_ENT_F_CONN_TEST:
207 			ret = media_create_pad_link(entity, 0, decoder,
208 						    DEMOD_PAD_IF_INPUT,
209 						    flags);
210 			break;
211 		default:
212 			continue;
213 		}
214 		if (ret)
215 			return ret;
216 
217 		flags = 0;
218 	}
219 	return 0;
220 }
221 EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
222