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