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/component.h>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <media/media-device.h>
13 #include <media/v4l2-device.h>
14 
15 #include "vimc-common.h"
16 
17 #define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
18 
19 #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
20 	.src_ent = src,						\
21 	.src_pad = srcpad,					\
22 	.sink_ent = sink,					\
23 	.sink_pad = sinkpad,					\
24 	.flags = link_flags,					\
25 }
26 
27 struct vimc_device {
28 	/* The platform device */
29 	struct platform_device pdev;
30 
31 	/* The pipeline configuration */
32 	const struct vimc_pipeline_config *pipe_cfg;
33 
34 	/* The Associated media_device parent */
35 	struct media_device mdev;
36 
37 	/* Internal v4l2 parent device*/
38 	struct v4l2_device v4l2_dev;
39 
40 	/* Subdevices */
41 	struct platform_device **subdevs;
42 };
43 
44 /* Structure which describes individual configuration for each entity */
45 struct vimc_ent_config {
46 	const char *name;
47 	const char *drv;
48 };
49 
50 /* Structure which describes links between entities */
51 struct vimc_ent_link {
52 	unsigned int src_ent;
53 	u16 src_pad;
54 	unsigned int sink_ent;
55 	u16 sink_pad;
56 	u32 flags;
57 };
58 
59 /* Structure which describes the whole topology */
60 struct vimc_pipeline_config {
61 	const struct vimc_ent_config *ents;
62 	size_t num_ents;
63 	const struct vimc_ent_link *links;
64 	size_t num_links;
65 };
66 
67 /* --------------------------------------------------------------------------
68  * Topology Configuration
69  */
70 
71 static const struct vimc_ent_config ent_config[] = {
72 	{
73 		.name = "Sensor A",
74 		.drv = "vimc-sensor",
75 	},
76 	{
77 		.name = "Sensor B",
78 		.drv = "vimc-sensor",
79 	},
80 	{
81 		.name = "Debayer A",
82 		.drv = "vimc-debayer",
83 	},
84 	{
85 		.name = "Debayer B",
86 		.drv = "vimc-debayer",
87 	},
88 	{
89 		.name = "Raw Capture 0",
90 		.drv = "vimc-capture",
91 	},
92 	{
93 		.name = "Raw Capture 1",
94 		.drv = "vimc-capture",
95 	},
96 	{
97 		.name = "RGB/YUV Input",
98 		/* TODO: change this to vimc-input when it is implemented */
99 		.drv = "vimc-sensor",
100 	},
101 	{
102 		.name = "Scaler",
103 		.drv = "vimc-scaler",
104 	},
105 	{
106 		.name = "RGB/YUV Capture",
107 		.drv = "vimc-capture",
108 	},
109 };
110 
111 static const struct vimc_ent_link ent_links[] = {
112 	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
113 	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
114 	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
115 	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
116 	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
117 	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
118 	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
119 	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
120 	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
121 	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
122 	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
123 	VIMC_ENT_LINK(3, 1, 7, 0, 0),
124 	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
125 	VIMC_ENT_LINK(6, 0, 7, 0, 0),
126 	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
127 	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
128 };
129 
130 static const struct vimc_pipeline_config pipe_cfg = {
131 	.ents		= ent_config,
132 	.num_ents	= ARRAY_SIZE(ent_config),
133 	.links		= ent_links,
134 	.num_links	= ARRAY_SIZE(ent_links)
135 };
136 
137 /* -------------------------------------------------------------------------- */
138 
vimc_create_links(struct vimc_device * vimc)139 static int vimc_create_links(struct vimc_device *vimc)
140 {
141 	unsigned int i;
142 	int ret;
143 
144 	/* Initialize the links between entities */
145 	for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
146 		const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
147 		/*
148 		 * TODO: Check another way of retrieving ved struct without
149 		 * relying on platform_get_drvdata
150 		 */
151 		struct vimc_ent_device *ved_src =
152 			platform_get_drvdata(vimc->subdevs[link->src_ent]);
153 		struct vimc_ent_device *ved_sink =
154 			platform_get_drvdata(vimc->subdevs[link->sink_ent]);
155 
156 		ret = media_create_pad_link(ved_src->ent, link->src_pad,
157 					    ved_sink->ent, link->sink_pad,
158 					    link->flags);
159 		if (ret)
160 			return ret;
161 	}
162 
163 	return 0;
164 }
165 
vimc_comp_bind(struct device * master)166 static int vimc_comp_bind(struct device *master)
167 {
168 	struct vimc_device *vimc = container_of(to_platform_device(master),
169 						struct vimc_device, pdev);
170 	int ret;
171 
172 	dev_dbg(master, "bind");
173 
174 	/* Register the v4l2 struct */
175 	ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
176 	if (ret) {
177 		dev_err(vimc->mdev.dev,
178 			"v4l2 device register failed (err=%d)\n", ret);
179 		return ret;
180 	}
181 
182 	/* Bind subdevices */
183 	ret = component_bind_all(master, &vimc->v4l2_dev);
184 	if (ret)
185 		goto err_v4l2_unregister;
186 
187 	/* Initialize links */
188 	ret = vimc_create_links(vimc);
189 	if (ret)
190 		goto err_comp_unbind_all;
191 
192 	/* Register the media device */
193 	ret = media_device_register(&vimc->mdev);
194 	if (ret) {
195 		dev_err(vimc->mdev.dev,
196 			"media device register failed (err=%d)\n", ret);
197 		goto err_comp_unbind_all;
198 	}
199 
200 	/* Expose all subdev's nodes*/
201 	ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev);
202 	if (ret) {
203 		dev_err(vimc->mdev.dev,
204 			"vimc subdev nodes registration failed (err=%d)\n",
205 			ret);
206 		goto err_mdev_unregister;
207 	}
208 
209 	return 0;
210 
211 err_mdev_unregister:
212 	media_device_unregister(&vimc->mdev);
213 	media_device_cleanup(&vimc->mdev);
214 err_comp_unbind_all:
215 	component_unbind_all(master, NULL);
216 err_v4l2_unregister:
217 	v4l2_device_unregister(&vimc->v4l2_dev);
218 
219 	return ret;
220 }
221 
vimc_comp_unbind(struct device * master)222 static void vimc_comp_unbind(struct device *master)
223 {
224 	struct vimc_device *vimc = container_of(to_platform_device(master),
225 						struct vimc_device, pdev);
226 
227 	dev_dbg(master, "unbind");
228 
229 	media_device_unregister(&vimc->mdev);
230 	media_device_cleanup(&vimc->mdev);
231 	component_unbind_all(master, NULL);
232 	v4l2_device_unregister(&vimc->v4l2_dev);
233 }
234 
vimc_comp_compare(struct device * comp,void * data)235 static int vimc_comp_compare(struct device *comp, void *data)
236 {
237 	return comp == data;
238 }
239 
vimc_add_subdevs(struct vimc_device * vimc)240 static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
241 {
242 	struct component_match *match = NULL;
243 	struct vimc_platform_data pdata;
244 	int i;
245 
246 	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
247 		dev_dbg(&vimc->pdev.dev, "new pdev for %s\n",
248 			vimc->pipe_cfg->ents[i].drv);
249 
250 		strscpy(pdata.entity_name, vimc->pipe_cfg->ents[i].name,
251 			sizeof(pdata.entity_name));
252 
253 		vimc->subdevs[i] = platform_device_register_data(&vimc->pdev.dev,
254 						vimc->pipe_cfg->ents[i].drv,
255 						PLATFORM_DEVID_AUTO,
256 						&pdata,
257 						sizeof(pdata));
258 		if (IS_ERR(vimc->subdevs[i])) {
259 			match = ERR_CAST(vimc->subdevs[i]);
260 			while (--i >= 0)
261 				platform_device_unregister(vimc->subdevs[i]);
262 
263 			return match;
264 		}
265 
266 		component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare,
267 				    &vimc->subdevs[i]->dev);
268 	}
269 
270 	return match;
271 }
272 
vimc_rm_subdevs(struct vimc_device * vimc)273 static void vimc_rm_subdevs(struct vimc_device *vimc)
274 {
275 	unsigned int i;
276 
277 	for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
278 		platform_device_unregister(vimc->subdevs[i]);
279 }
280 
281 static const struct component_master_ops vimc_comp_ops = {
282 	.bind = vimc_comp_bind,
283 	.unbind = vimc_comp_unbind,
284 };
285 
vimc_probe(struct platform_device * pdev)286 static int vimc_probe(struct platform_device *pdev)
287 {
288 	struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
289 	struct component_match *match = NULL;
290 	int ret;
291 
292 	dev_dbg(&pdev->dev, "probe");
293 
294 	memset(&vimc->mdev, 0, sizeof(vimc->mdev));
295 
296 	/* Create platform_device for each entity in the topology*/
297 	vimc->subdevs = devm_kcalloc(&vimc->pdev.dev, vimc->pipe_cfg->num_ents,
298 				     sizeof(*vimc->subdevs), GFP_KERNEL);
299 	if (!vimc->subdevs)
300 		return -ENOMEM;
301 
302 	match = vimc_add_subdevs(vimc);
303 	if (IS_ERR(match))
304 		return PTR_ERR(match);
305 
306 	/* Link the media device within the v4l2_device */
307 	vimc->v4l2_dev.mdev = &vimc->mdev;
308 
309 	/* Initialize media device */
310 	strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
311 		sizeof(vimc->mdev.model));
312 	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
313 		 "platform:%s", VIMC_PDEV_NAME);
314 	vimc->mdev.dev = &pdev->dev;
315 	media_device_init(&vimc->mdev);
316 
317 	/* Add self to the component system */
318 	ret = component_master_add_with_match(&pdev->dev, &vimc_comp_ops,
319 					      match);
320 	if (ret) {
321 		media_device_cleanup(&vimc->mdev);
322 		vimc_rm_subdevs(vimc);
323 		return ret;
324 	}
325 
326 	return 0;
327 }
328 
vimc_remove(struct platform_device * pdev)329 static int vimc_remove(struct platform_device *pdev)
330 {
331 	struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
332 
333 	dev_dbg(&pdev->dev, "remove");
334 
335 	component_master_del(&pdev->dev, &vimc_comp_ops);
336 	vimc_rm_subdevs(vimc);
337 
338 	return 0;
339 }
340 
vimc_dev_release(struct device * dev)341 static void vimc_dev_release(struct device *dev)
342 {
343 }
344 
345 static struct vimc_device vimc_dev = {
346 	.pipe_cfg = &pipe_cfg,
347 	.pdev = {
348 		.name = VIMC_PDEV_NAME,
349 		.dev.release = vimc_dev_release,
350 	}
351 };
352 
353 static struct platform_driver vimc_pdrv = {
354 	.probe		= vimc_probe,
355 	.remove		= vimc_remove,
356 	.driver		= {
357 		.name	= VIMC_PDEV_NAME,
358 	},
359 };
360 
vimc_init(void)361 static int __init vimc_init(void)
362 {
363 	int ret;
364 
365 	ret = platform_device_register(&vimc_dev.pdev);
366 	if (ret) {
367 		dev_err(&vimc_dev.pdev.dev,
368 			"platform device registration failed (err=%d)\n", ret);
369 		return ret;
370 	}
371 
372 	ret = platform_driver_register(&vimc_pdrv);
373 	if (ret) {
374 		dev_err(&vimc_dev.pdev.dev,
375 			"platform driver registration failed (err=%d)\n", ret);
376 		platform_driver_unregister(&vimc_pdrv);
377 		return ret;
378 	}
379 
380 	return 0;
381 }
382 
vimc_exit(void)383 static void __exit vimc_exit(void)
384 {
385 	platform_driver_unregister(&vimc_pdrv);
386 
387 	platform_device_unregister(&vimc_dev.pdev);
388 }
389 
390 module_init(vimc_init);
391 module_exit(vimc_exit);
392 
393 MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
394 MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
395 MODULE_LICENSE("GPL");
396