blob: a7f41b3235225c9a0a7e031035444275102dd6c9 [file] [log] [blame]
Mauro Carvalho Chehab54d0dba2016-02-05 07:02:43 -02001/*
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>
Mauro Carvalho Chehab7047f292016-02-05 13:16:18 -020018#include <linux/pci.h>
Mauro Carvalho Chehabeee7d352016-02-11 15:21:46 -020019#include <linux/usb.h>
Mauro Carvalho Chehab54d0dba2016-02-05 07:02:43 -020020#include <media/media-entity.h>
21#include <media/v4l2-mc.h>
22
Mauro Carvalho Chehab7047f292016-02-05 13:16:18 -020023
24struct media_device *v4l2_mc_pci_media_device_init(struct pci_dev *pci_dev,
Mauro Carvalho Chehabeee7d352016-02-11 15:21:46 -020025 const char *name)
Mauro Carvalho Chehab7047f292016-02-05 13:16:18 -020026{
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}
55EXPORT_SYMBOL_GPL(v4l2_mc_pci_media_device_init);
56
Mauro Carvalho Chehabeee7d352016-02-11 15:21:46 -020057struct 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}
93EXPORT_SYMBOL_GPL(__v4l2_mc_usb_media_device_init);
94
Mauro Carvalho Chehab54d0dba2016-02-05 07:02:43 -020095int v4l2_mc_create_media_graph(struct media_device *mdev)
96
97{
98 struct media_entity *entity;
Mauro Carvalho Chehab153d41a2016-02-12 08:07:59 -020099 struct media_entity *if_vid = NULL, *if_aud = NULL;
Mauro Carvalho Chehab54d0dba2016-02-05 07:02:43 -0200100 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:
Mauro Carvalho Chehab54d0dba2016-02-05 07:02:43 -0200133 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:
Mauro Carvalho Chehab54d0dba2016-02-05 07:02:43 -0200244 ret = media_create_pad_link(entity, 0, decoder,
245 DEMOD_PAD_IF_INPUT,
246 flags);
247 break;
248 default:
249 continue;
250 }
251 if (ret)
252 return ret;
253
254 flags = 0;
255 }
256 return 0;
257}
258EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);