xref: /openbmc/linux/drivers/media/v4l2-core/v4l2-mc.c (revision c43875f66140f5457f90fc5f6f6840c74b2762cd)
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 <linux/usb.h>
20 #include <media/media-entity.h>
21 #include <media/v4l2-mc.h>
22 
23 
24 struct media_device *v4l2_mc_pci_media_device_init(struct pci_dev *pci_dev,
25 						   const char *name)
26 {
27 #ifdef CONFIG_PCI
28 	struct media_device *mdev;
29 
30 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
31 	if (!mdev)
32 		return NULL;
33 
34 	mdev->dev = &pci_dev->dev;
35 
36 	if (name)
37 		strlcpy(mdev->model, name, sizeof(mdev->model));
38 	else
39 		strlcpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model));
40 
41 	sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev));
42 
43 	mdev->hw_revision = pci_dev->subsystem_vendor << 16
44 			    || pci_dev->subsystem_device;
45 
46 	mdev->driver_version = LINUX_VERSION_CODE;
47 
48 	media_device_init(mdev);
49 
50 	return mdev;
51 #else
52 	return NULL;
53 #endif
54 }
55 EXPORT_SYMBOL_GPL(v4l2_mc_pci_media_device_init);
56 
57 struct media_device *__v4l2_mc_usb_media_device_init(struct usb_device *udev,
58 						     const char *board_name,
59 						     const char *driver_name)
60 {
61 #ifdef CONFIG_USB
62 	struct media_device *mdev;
63 
64 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
65 	if (!mdev)
66 		return NULL;
67 
68 	mdev->dev = &udev->dev;
69 
70 	if (driver_name)
71 		strlcpy(mdev->driver_name, driver_name,
72 			sizeof(mdev->driver_name));
73 
74 	if (board_name)
75 		strlcpy(mdev->model, board_name, sizeof(mdev->model));
76 	else if (udev->product)
77 		strlcpy(mdev->model, udev->product, sizeof(mdev->model));
78 	else
79 		strlcpy(mdev->model, "unknown model", sizeof(mdev->model));
80 	if (udev->serial)
81 		strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
82 	usb_make_path(udev, mdev->bus_info, sizeof(mdev->bus_info));
83 	mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
84 	mdev->driver_version = LINUX_VERSION_CODE;
85 
86 	media_device_init(mdev);
87 
88 	return mdev;
89 #else
90 	return NULL;
91 #endif
92 }
93 EXPORT_SYMBOL_GPL(__v4l2_mc_usb_media_device_init);
94 
95 int v4l2_mc_create_media_graph(struct media_device *mdev)
96 
97 {
98 	struct media_entity *entity;
99 	struct media_entity *if_vid = NULL, *if_aud = NULL;
100 	struct media_entity *tuner = NULL, *decoder = NULL;
101 	struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;
102 	bool is_webcam = false;
103 	u32 flags;
104 	int ret;
105 
106 	if (!mdev)
107 		return 0;
108 
109 	media_device_for_each_entity(entity, mdev) {
110 		switch (entity->function) {
111 		case MEDIA_ENT_F_IF_VID_DECODER:
112 			if_vid = entity;
113 			break;
114 		case MEDIA_ENT_F_IF_AUD_DECODER:
115 			if_aud = entity;
116 			break;
117 		case MEDIA_ENT_F_TUNER:
118 			tuner = entity;
119 			break;
120 		case MEDIA_ENT_F_ATV_DECODER:
121 			decoder = entity;
122 			break;
123 		case MEDIA_ENT_F_IO_V4L:
124 			io_v4l = entity;
125 			break;
126 		case MEDIA_ENT_F_IO_VBI:
127 			io_vbi = entity;
128 			break;
129 		case MEDIA_ENT_F_IO_SWRADIO:
130 			io_swradio = entity;
131 			break;
132 		case MEDIA_ENT_F_CAM_SENSOR:
133 			is_webcam = true;
134 			break;
135 		}
136 	}
137 
138 	/* It should have at least one I/O entity */
139 	if (!io_v4l && !io_vbi && !io_swradio)
140 		return -EINVAL;
141 
142 	/*
143 	 * Here, webcams are modelled on a very simple way: the sensor is
144 	 * connected directly to the I/O entity. All dirty details, like
145 	 * scaler and crop HW are hidden. While such mapping is not enough
146 	 * for mc-centric hardware, it is enough for v4l2 interface centric
147 	 * PC-consumer's hardware.
148 	 */
149 	if (is_webcam) {
150 		if (!io_v4l)
151 			return -EINVAL;
152 
153 		media_device_for_each_entity(entity, mdev) {
154 			if (entity->function != MEDIA_ENT_F_CAM_SENSOR)
155 				continue;
156 			ret = media_create_pad_link(entity, 0,
157 						    io_v4l, 0,
158 						    MEDIA_LNK_FL_ENABLED);
159 			if (ret)
160 				return ret;
161 		}
162 		if (!decoder)
163 			return 0;
164 	}
165 
166 	/* The device isn't a webcam. So, it should have a decoder */
167 	if (!decoder)
168 		return -EINVAL;
169 
170 	/* Link the tuner and IF video output pads */
171 	if (tuner) {
172 		if (if_vid) {
173 			ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
174 						    if_vid,
175 						    IF_VID_DEC_PAD_IF_INPUT,
176 						    MEDIA_LNK_FL_ENABLED);
177 			if (ret)
178 				return ret;
179 			ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT,
180 						decoder, DEMOD_PAD_IF_INPUT,
181 						MEDIA_LNK_FL_ENABLED);
182 			if (ret)
183 				return ret;
184 		} else {
185 			ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT,
186 						decoder, DEMOD_PAD_IF_INPUT,
187 						MEDIA_LNK_FL_ENABLED);
188 			if (ret)
189 				return ret;
190 		}
191 
192 		if (if_aud) {
193 			ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT,
194 						    if_aud,
195 						    IF_AUD_DEC_PAD_IF_INPUT,
196 						    MEDIA_LNK_FL_ENABLED);
197 			if (ret)
198 				return ret;
199 		} else {
200 			if_aud = tuner;
201 		}
202 
203 	}
204 
205 	/* Create demod to V4L, VBI and SDR radio links */
206 	if (io_v4l) {
207 		ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
208 					io_v4l, 0,
209 					MEDIA_LNK_FL_ENABLED);
210 		if (ret)
211 			return ret;
212 	}
213 
214 	if (io_swradio) {
215 		ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT,
216 					io_swradio, 0,
217 					MEDIA_LNK_FL_ENABLED);
218 		if (ret)
219 			return ret;
220 	}
221 
222 	if (io_vbi) {
223 		ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT,
224 					    io_vbi, 0,
225 					    MEDIA_LNK_FL_ENABLED);
226 		if (ret)
227 			return ret;
228 	}
229 
230 	/* Create links for the media connectors */
231 	flags = MEDIA_LNK_FL_ENABLED;
232 	media_device_for_each_entity(entity, mdev) {
233 		switch (entity->function) {
234 		case MEDIA_ENT_F_CONN_RF:
235 			if (!tuner)
236 				continue;
237 
238 			ret = media_create_pad_link(entity, 0, tuner,
239 						    TUNER_PAD_RF_INPUT,
240 						    flags);
241 			break;
242 		case MEDIA_ENT_F_CONN_SVIDEO:
243 		case MEDIA_ENT_F_CONN_COMPOSITE:
244 		case MEDIA_ENT_F_CONN_TEST:
245 			ret = media_create_pad_link(entity, 0, decoder,
246 						    DEMOD_PAD_IF_INPUT,
247 						    flags);
248 			break;
249 		default:
250 			continue;
251 		}
252 		if (ret)
253 			return ret;
254 
255 		flags = 0;
256 	}
257 	return 0;
258 }
259 EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);
260