1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * vimc-core.c Virtual Media Controller Driver |
4 | * |
5 | * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/dma-mapping.h> |
9 | #include <linux/font.h> |
10 | #include <linux/init.h> |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <media/media-device.h> |
14 | #include <media/tpg/v4l2-tpg.h> |
15 | #include <media/v4l2-device.h> |
16 | |
17 | #include "vimc-common.h" |
18 | |
19 | unsigned int vimc_allocator; |
20 | module_param_named(allocator, vimc_allocator, uint, 0444); |
21 | MODULE_PARM_DESC(allocator, " memory allocator selection, default is 0.\n" |
22 | "\t\t 0 == vmalloc\n" |
23 | "\t\t 1 == dma-contig" ); |
24 | |
25 | #define VIMC_MDEV_MODEL_NAME "VIMC MDEV" |
26 | |
27 | #define VIMC_DATA_LINK(src, srcpad, sink, sinkpad, link_flags) { \ |
28 | .src_ent = src, \ |
29 | .src_pad = srcpad, \ |
30 | .sink_ent = sink, \ |
31 | .sink_pad = sinkpad, \ |
32 | .flags = link_flags, \ |
33 | } |
34 | |
35 | #define VIMC_ANCILLARY_LINK(primary, ancillary) { \ |
36 | .primary_ent = primary, \ |
37 | .ancillary_ent = ancillary \ |
38 | } |
39 | |
40 | /* Structure which describes data links between entities */ |
41 | struct vimc_data_link { |
42 | unsigned int src_ent; |
43 | u16 src_pad; |
44 | unsigned int sink_ent; |
45 | u16 sink_pad; |
46 | u32 flags; |
47 | }; |
48 | |
49 | /* Enum to improve clarity when defining vimc_data_links */ |
50 | enum vimc_data_link_ents { |
51 | SENSOR_A, |
52 | SENSOR_B, |
53 | DEBAYER_A, |
54 | DEBAYER_B, |
55 | RAW_CAPTURE_0, |
56 | RAW_CAPTURE_1, |
57 | RGB_YUV_INPUT, |
58 | SCALER, |
59 | RGB_YUV_CAPTURE, |
60 | LENS_A, |
61 | LENS_B, |
62 | }; |
63 | |
64 | /* Structure which describes ancillary links between entities */ |
65 | struct vimc_ancillary_link { |
66 | unsigned int primary_ent; |
67 | unsigned int ancillary_ent; |
68 | }; |
69 | |
70 | /* Structure which describes the whole topology */ |
71 | struct vimc_pipeline_config { |
72 | const struct vimc_ent_config *ents; |
73 | size_t num_ents; |
74 | const struct vimc_data_link *data_links; |
75 | size_t num_data_links; |
76 | const struct vimc_ancillary_link *ancillary_links; |
77 | size_t num_ancillary_links; |
78 | }; |
79 | |
80 | /* -------------------------------------------------------------------------- |
81 | * Topology Configuration |
82 | */ |
83 | |
84 | static struct vimc_ent_config ent_config[] = { |
85 | [SENSOR_A] = { |
86 | .name = "Sensor A" , |
87 | .type = &vimc_sensor_type |
88 | }, |
89 | [SENSOR_B] = { |
90 | .name = "Sensor B" , |
91 | .type = &vimc_sensor_type |
92 | }, |
93 | [DEBAYER_A] = { |
94 | .name = "Debayer A" , |
95 | .type = &vimc_debayer_type |
96 | }, |
97 | [DEBAYER_B] = { |
98 | .name = "Debayer B" , |
99 | .type = &vimc_debayer_type |
100 | }, |
101 | [RAW_CAPTURE_0] = { |
102 | .name = "Raw Capture 0" , |
103 | .type = &vimc_capture_type |
104 | }, |
105 | [RAW_CAPTURE_1] = { |
106 | .name = "Raw Capture 1" , |
107 | .type = &vimc_capture_type |
108 | }, |
109 | [RGB_YUV_INPUT] = { |
110 | /* TODO: change this to vimc-input when it is implemented */ |
111 | .name = "RGB/YUV Input" , |
112 | .type = &vimc_sensor_type |
113 | }, |
114 | [SCALER] = { |
115 | .name = "Scaler" , |
116 | .type = &vimc_scaler_type |
117 | }, |
118 | [RGB_YUV_CAPTURE] = { |
119 | .name = "RGB/YUV Capture" , |
120 | .type = &vimc_capture_type |
121 | }, |
122 | [LENS_A] = { |
123 | .name = "Lens A" , |
124 | .type = &vimc_lens_type |
125 | }, |
126 | [LENS_B] = { |
127 | .name = "Lens B" , |
128 | .type = &vimc_lens_type |
129 | }, |
130 | }; |
131 | |
132 | static const struct vimc_data_link data_links[] = { |
133 | /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */ |
134 | VIMC_DATA_LINK(SENSOR_A, 0, DEBAYER_A, 0, |
135 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), |
136 | /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */ |
137 | VIMC_DATA_LINK(SENSOR_A, 0, RAW_CAPTURE_0, 0, |
138 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), |
139 | /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */ |
140 | VIMC_DATA_LINK(SENSOR_B, 0, DEBAYER_B, 0, |
141 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), |
142 | /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */ |
143 | VIMC_DATA_LINK(SENSOR_B, 0, RAW_CAPTURE_1, 0, |
144 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), |
145 | /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */ |
146 | VIMC_DATA_LINK(DEBAYER_A, 1, SCALER, 0, MEDIA_LNK_FL_ENABLED), |
147 | /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */ |
148 | VIMC_DATA_LINK(DEBAYER_B, 1, SCALER, 0, 0), |
149 | /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */ |
150 | VIMC_DATA_LINK(RGB_YUV_INPUT, 0, SCALER, 0, 0), |
151 | /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */ |
152 | VIMC_DATA_LINK(SCALER, 1, RGB_YUV_CAPTURE, 0, |
153 | MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), |
154 | }; |
155 | |
156 | static const struct vimc_ancillary_link ancillary_links[] = { |
157 | /* Link: Sensor A -> Lens A */ |
158 | VIMC_ANCILLARY_LINK(0, 9), |
159 | /* Link: Sensor B -> Lens B */ |
160 | VIMC_ANCILLARY_LINK(1, 10), |
161 | }; |
162 | |
163 | static struct vimc_pipeline_config pipe_cfg = { |
164 | .ents = ent_config, |
165 | .num_ents = ARRAY_SIZE(ent_config), |
166 | .data_links = data_links, |
167 | .num_data_links = ARRAY_SIZE(data_links), |
168 | .ancillary_links = ancillary_links, |
169 | .num_ancillary_links = ARRAY_SIZE(ancillary_links), |
170 | }; |
171 | |
172 | /* -------------------------------------------------------------------------- */ |
173 | |
174 | static void vimc_rm_links(struct vimc_device *vimc) |
175 | { |
176 | unsigned int i; |
177 | |
178 | for (i = 0; i < vimc->pipe_cfg->num_ents; i++) |
179 | media_entity_remove_links(entity: vimc->ent_devs[i]->ent); |
180 | } |
181 | |
182 | static int vimc_create_links(struct vimc_device *vimc) |
183 | { |
184 | unsigned int i; |
185 | int ret; |
186 | |
187 | /* Initialize the links between entities */ |
188 | for (i = 0; i < vimc->pipe_cfg->num_data_links; i++) { |
189 | const struct vimc_data_link *link = &vimc->pipe_cfg->data_links[i]; |
190 | |
191 | struct vimc_ent_device *ved_src = |
192 | vimc->ent_devs[link->src_ent]; |
193 | struct vimc_ent_device *ved_sink = |
194 | vimc->ent_devs[link->sink_ent]; |
195 | |
196 | ret = media_create_pad_link(source: ved_src->ent, source_pad: link->src_pad, |
197 | sink: ved_sink->ent, sink_pad: link->sink_pad, |
198 | flags: link->flags); |
199 | if (ret) |
200 | goto err_rm_links; |
201 | } |
202 | |
203 | for (i = 0; i < vimc->pipe_cfg->num_ancillary_links; i++) { |
204 | const struct vimc_ancillary_link *link = &vimc->pipe_cfg->ancillary_links[i]; |
205 | |
206 | struct vimc_ent_device *ved_primary = |
207 | vimc->ent_devs[link->primary_ent]; |
208 | struct vimc_ent_device *ved_ancillary = |
209 | vimc->ent_devs[link->ancillary_ent]; |
210 | struct media_link *ret_link = |
211 | media_create_ancillary_link(primary: ved_primary->ent, ancillary: ved_ancillary->ent); |
212 | |
213 | if (IS_ERR(ptr: ret_link)) { |
214 | ret = PTR_ERR(ptr: ret_link); |
215 | goto err_rm_links; |
216 | } |
217 | } |
218 | |
219 | return 0; |
220 | |
221 | err_rm_links: |
222 | vimc_rm_links(vimc); |
223 | return ret; |
224 | } |
225 | |
226 | static void vimc_release_subdevs(struct vimc_device *vimc) |
227 | { |
228 | unsigned int i; |
229 | |
230 | for (i = 0; i < vimc->pipe_cfg->num_ents; i++) |
231 | if (vimc->ent_devs[i]) |
232 | vimc->pipe_cfg->ents[i].type->release(vimc->ent_devs[i]); |
233 | } |
234 | |
235 | static void vimc_unregister_subdevs(struct vimc_device *vimc) |
236 | { |
237 | unsigned int i; |
238 | |
239 | for (i = 0; i < vimc->pipe_cfg->num_ents; i++) |
240 | if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].type->unregister) |
241 | vimc->pipe_cfg->ents[i].type->unregister(vimc->ent_devs[i]); |
242 | } |
243 | |
244 | static int vimc_add_subdevs(struct vimc_device *vimc) |
245 | { |
246 | unsigned int i; |
247 | |
248 | for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { |
249 | dev_dbg(vimc->mdev.dev, "new entity for %s\n" , |
250 | vimc->pipe_cfg->ents[i].name); |
251 | vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].type->add(vimc, |
252 | vimc->pipe_cfg->ents[i].name); |
253 | if (IS_ERR(ptr: vimc->ent_devs[i])) { |
254 | int err = PTR_ERR(ptr: vimc->ent_devs[i]); |
255 | |
256 | dev_err(vimc->mdev.dev, "adding entity %s failed (%d)\n" , |
257 | vimc->pipe_cfg->ents[i].name, err); |
258 | vimc->ent_devs[i] = NULL; |
259 | vimc_unregister_subdevs(vimc); |
260 | vimc_release_subdevs(vimc); |
261 | return err; |
262 | } |
263 | } |
264 | return 0; |
265 | } |
266 | |
267 | static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev) |
268 | { |
269 | struct vimc_device *vimc = |
270 | container_of(v4l2_dev, struct vimc_device, v4l2_dev); |
271 | |
272 | vimc_release_subdevs(vimc); |
273 | media_device_cleanup(mdev: &vimc->mdev); |
274 | kfree(objp: vimc->ent_devs); |
275 | kfree(objp: vimc); |
276 | } |
277 | |
278 | static int vimc_register_devices(struct vimc_device *vimc) |
279 | { |
280 | int ret; |
281 | |
282 | /* Register the v4l2 struct */ |
283 | ret = v4l2_device_register(dev: vimc->mdev.dev, v4l2_dev: &vimc->v4l2_dev); |
284 | if (ret) { |
285 | dev_err(vimc->mdev.dev, |
286 | "v4l2 device register failed (err=%d)\n" , ret); |
287 | return ret; |
288 | } |
289 | /* allocate ent_devs */ |
290 | vimc->ent_devs = kcalloc(n: vimc->pipe_cfg->num_ents, |
291 | size: sizeof(*vimc->ent_devs), GFP_KERNEL); |
292 | if (!vimc->ent_devs) { |
293 | ret = -ENOMEM; |
294 | goto err_v4l2_unregister; |
295 | } |
296 | |
297 | /* Invoke entity config hooks to initialize and register subdevs */ |
298 | ret = vimc_add_subdevs(vimc); |
299 | if (ret) |
300 | goto err_free_ent_devs; |
301 | |
302 | /* Initialize links */ |
303 | ret = vimc_create_links(vimc); |
304 | if (ret) |
305 | goto err_rm_subdevs; |
306 | |
307 | /* Register the media device */ |
308 | ret = media_device_register(&vimc->mdev); |
309 | if (ret) { |
310 | dev_err(vimc->mdev.dev, |
311 | "media device register failed (err=%d)\n" , ret); |
312 | goto err_rm_subdevs; |
313 | } |
314 | |
315 | /* Expose all subdev's nodes*/ |
316 | ret = v4l2_device_register_subdev_nodes(v4l2_dev: &vimc->v4l2_dev); |
317 | if (ret) { |
318 | dev_err(vimc->mdev.dev, |
319 | "vimc subdev nodes registration failed (err=%d)\n" , |
320 | ret); |
321 | goto err_mdev_unregister; |
322 | } |
323 | |
324 | return 0; |
325 | |
326 | err_mdev_unregister: |
327 | media_device_unregister(mdev: &vimc->mdev); |
328 | err_rm_subdevs: |
329 | vimc_unregister_subdevs(vimc); |
330 | vimc_release_subdevs(vimc); |
331 | err_free_ent_devs: |
332 | kfree(objp: vimc->ent_devs); |
333 | err_v4l2_unregister: |
334 | v4l2_device_unregister(v4l2_dev: &vimc->v4l2_dev); |
335 | |
336 | return ret; |
337 | } |
338 | |
339 | static int vimc_probe(struct platform_device *pdev) |
340 | { |
341 | const struct font_desc *font = find_font(name: "VGA8x16" ); |
342 | struct vimc_device *vimc; |
343 | int ret; |
344 | |
345 | dev_dbg(&pdev->dev, "probe" ); |
346 | |
347 | if (!font) { |
348 | dev_err(&pdev->dev, "could not find font\n" ); |
349 | return -ENODEV; |
350 | } |
351 | |
352 | tpg_set_font(f: font->data); |
353 | |
354 | if (vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG) |
355 | dma_coerce_mask_and_coherent(dev: &pdev->dev, DMA_BIT_MASK(32)); |
356 | |
357 | vimc = kzalloc(size: sizeof(*vimc), GFP_KERNEL); |
358 | if (!vimc) |
359 | return -ENOMEM; |
360 | |
361 | vimc->pipe_cfg = &pipe_cfg; |
362 | |
363 | /* Link the media device within the v4l2_device */ |
364 | vimc->v4l2_dev.mdev = &vimc->mdev; |
365 | |
366 | /* Initialize media device */ |
367 | strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME, |
368 | sizeof(vimc->mdev.model)); |
369 | snprintf(buf: vimc->mdev.bus_info, size: sizeof(vimc->mdev.bus_info), |
370 | fmt: "platform:%s" , VIMC_PDEV_NAME); |
371 | vimc->mdev.dev = &pdev->dev; |
372 | media_device_init(mdev: &vimc->mdev); |
373 | |
374 | ret = vimc_register_devices(vimc); |
375 | if (ret) { |
376 | media_device_cleanup(mdev: &vimc->mdev); |
377 | kfree(objp: vimc); |
378 | return ret; |
379 | } |
380 | /* |
381 | * the release cb is set only after successful registration. |
382 | * if the registration fails, we release directly from probe |
383 | */ |
384 | |
385 | vimc->v4l2_dev.release = vimc_v4l2_dev_release; |
386 | platform_set_drvdata(pdev, data: vimc); |
387 | return 0; |
388 | } |
389 | |
390 | static void vimc_remove(struct platform_device *pdev) |
391 | { |
392 | struct vimc_device *vimc = platform_get_drvdata(pdev); |
393 | |
394 | dev_dbg(&pdev->dev, "remove" ); |
395 | |
396 | vimc_unregister_subdevs(vimc); |
397 | media_device_unregister(mdev: &vimc->mdev); |
398 | v4l2_device_unregister(v4l2_dev: &vimc->v4l2_dev); |
399 | v4l2_device_put(v4l2_dev: &vimc->v4l2_dev); |
400 | } |
401 | |
402 | static void vimc_dev_release(struct device *dev) |
403 | { |
404 | } |
405 | |
406 | static struct platform_device vimc_pdev = { |
407 | .name = VIMC_PDEV_NAME, |
408 | .dev.release = vimc_dev_release, |
409 | }; |
410 | |
411 | static struct platform_driver vimc_pdrv = { |
412 | .probe = vimc_probe, |
413 | .remove_new = vimc_remove, |
414 | .driver = { |
415 | .name = VIMC_PDEV_NAME, |
416 | }, |
417 | }; |
418 | |
419 | static int __init vimc_init(void) |
420 | { |
421 | int ret; |
422 | |
423 | ret = platform_device_register(&vimc_pdev); |
424 | if (ret) { |
425 | dev_err(&vimc_pdev.dev, |
426 | "platform device registration failed (err=%d)\n" , ret); |
427 | return ret; |
428 | } |
429 | |
430 | ret = platform_driver_register(&vimc_pdrv); |
431 | if (ret) { |
432 | dev_err(&vimc_pdev.dev, |
433 | "platform driver registration failed (err=%d)\n" , ret); |
434 | platform_device_unregister(&vimc_pdev); |
435 | return ret; |
436 | } |
437 | |
438 | return 0; |
439 | } |
440 | |
441 | static void __exit vimc_exit(void) |
442 | { |
443 | platform_driver_unregister(&vimc_pdrv); |
444 | |
445 | platform_device_unregister(&vimc_pdev); |
446 | } |
447 | |
448 | module_init(vimc_init); |
449 | module_exit(vimc_exit); |
450 | |
451 | MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)" ); |
452 | MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>" ); |
453 | MODULE_LICENSE("GPL" ); |
454 | |