Linux Audio

Check our new training course

Loading...
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Media driver for Freescale i.MX5/6 SOC
  4 *
  5 * Adds the IPU internal subdevices and the media links between them.
  6 *
  7 * Copyright (c) 2016 Mentor Graphics Inc.
  8 */
  9#include <linux/platform_device.h>
 10#include "imx-media.h"
 11
 12/* max pads per internal-sd */
 13#define MAX_INTERNAL_PADS   8
 14/* max links per internal-sd pad */
 15#define MAX_INTERNAL_LINKS  8
 16
 17struct internal_subdev;
 18
 19struct internal_link {
 20	int remote;
 21	int local_pad;
 22	int remote_pad;
 23};
 24
 25struct internal_pad {
 26	int num_links;
 27	struct internal_link link[MAX_INTERNAL_LINKS];
 28};
 29
 30struct internal_subdev {
 31	u32 grp_id;
 32	struct internal_pad pad[MAX_INTERNAL_PADS];
 33
 34	struct v4l2_subdev * (*sync_register)(struct v4l2_device *v4l2_dev,
 35					      struct device *ipu_dev,
 36					      struct ipu_soc *ipu,
 37					      u32 grp_id);
 38	int (*sync_unregister)(struct v4l2_subdev *sd);
 39};
 40
 41static const struct internal_subdev int_subdev[NUM_IPU_SUBDEVS] = {
 42	[IPU_CSI0] = {
 43		.grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0,
 44		.pad[CSI_SRC_PAD_DIRECT] = {
 45			.num_links = 2,
 46			.link = {
 47				{
 48					.local_pad = CSI_SRC_PAD_DIRECT,
 49					.remote = IPU_IC_PRP,
 50					.remote_pad = PRP_SINK_PAD,
 51				}, {
 52					.local_pad = CSI_SRC_PAD_DIRECT,
 53					.remote = IPU_VDIC,
 54					.remote_pad = VDIC_SINK_PAD_DIRECT,
 55				},
 56			},
 57		},
 58	},
 59
 60	[IPU_CSI1] = {
 61		.grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1,
 62		.pad[CSI_SRC_PAD_DIRECT] = {
 63			.num_links = 2,
 64			.link = {
 65				{
 66					.local_pad = CSI_SRC_PAD_DIRECT,
 67					.remote = IPU_IC_PRP,
 68					.remote_pad = PRP_SINK_PAD,
 69				}, {
 70					.local_pad = CSI_SRC_PAD_DIRECT,
 71					.remote = IPU_VDIC,
 72					.remote_pad = VDIC_SINK_PAD_DIRECT,
 73				},
 74			},
 75		},
 76	},
 77
 78	[IPU_VDIC] = {
 79		.grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC,
 80		.sync_register = imx_media_vdic_register,
 81		.sync_unregister = imx_media_vdic_unregister,
 82		.pad[VDIC_SRC_PAD_DIRECT] = {
 83			.num_links = 1,
 84			.link = {
 85				{
 86					.local_pad = VDIC_SRC_PAD_DIRECT,
 87					.remote = IPU_IC_PRP,
 88					.remote_pad = PRP_SINK_PAD,
 89				},
 90			},
 91		},
 92	},
 93
 94	[IPU_IC_PRP] = {
 95		.grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP,
 96		.sync_register = imx_media_ic_register,
 97		.sync_unregister = imx_media_ic_unregister,
 98		.pad[PRP_SRC_PAD_PRPENC] = {
 99			.num_links = 1,
100			.link = {
101				{
102					.local_pad = PRP_SRC_PAD_PRPENC,
103					.remote = IPU_IC_PRPENC,
104					.remote_pad = PRPENCVF_SINK_PAD,
105				},
106			},
107		},
108		.pad[PRP_SRC_PAD_PRPVF] = {
109			.num_links = 1,
110			.link = {
111				{
112					.local_pad = PRP_SRC_PAD_PRPVF,
113					.remote = IPU_IC_PRPVF,
114					.remote_pad = PRPENCVF_SINK_PAD,
115				},
116			},
117		},
118	},
119
120	[IPU_IC_PRPENC] = {
121		.grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC,
122		.sync_register = imx_media_ic_register,
123		.sync_unregister = imx_media_ic_unregister,
124	},
125
126	[IPU_IC_PRPVF] = {
127		.grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF,
128		.sync_register = imx_media_ic_register,
129		.sync_unregister = imx_media_ic_unregister,
130	},
131};
132
133static int create_internal_link(struct imx_media_dev *imxmd,
134				struct v4l2_subdev *src,
135				struct v4l2_subdev *sink,
136				const struct internal_link *link)
137{
138	int ret;
139
140	/* skip if this link already created */
141	if (media_entity_find_link(&src->entity.pads[link->local_pad],
142				   &sink->entity.pads[link->remote_pad]))
143		return 0;
144
145	dev_dbg(imxmd->md.dev, "%s:%d -> %s:%d\n",
146		src->name, link->local_pad,
147		sink->name, link->remote_pad);
148
149	ret = media_create_pad_link(&src->entity, link->local_pad,
150				    &sink->entity, link->remote_pad, 0);
151	if (ret)
152		v4l2_err(&imxmd->v4l2_dev, "%s failed: %d\n", __func__, ret);
153
154	return ret;
155}
156
157static int create_ipu_internal_links(struct imx_media_dev *imxmd,
158				     const struct internal_subdev *intsd,
159				     struct v4l2_subdev *sd,
160				     int ipu_id)
161{
162	const struct internal_pad *intpad;
163	const struct internal_link *link;
164	struct media_pad *pad;
165	int i, j, ret;
166
167	/* create the source->sink links */
168	for (i = 0; i < sd->entity.num_pads; i++) {
169		intpad = &intsd->pad[i];
170		pad = &sd->entity.pads[i];
171
172		if (!(pad->flags & MEDIA_PAD_FL_SOURCE))
173			continue;
174
175		for (j = 0; j < intpad->num_links; j++) {
176			struct v4l2_subdev *sink;
177
178			link = &intpad->link[j];
179			sink = imxmd->sync_sd[ipu_id][link->remote];
180
181			ret = create_internal_link(imxmd, sd, sink, link);
182			if (ret)
183				return ret;
184		}
185	}
186
187	return 0;
188}
189
190int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd,
191					    struct v4l2_subdev *csi)
192{
193	struct device *ipu_dev = csi->dev->parent;
194	const struct internal_subdev *intsd;
195	struct v4l2_subdev *sd;
196	struct ipu_soc *ipu;
197	int i, ipu_id, ret;
198
199	ipu = dev_get_drvdata(ipu_dev);
200	if (!ipu) {
201		v4l2_err(&imxmd->v4l2_dev, "invalid IPU device!\n");
202		return -ENODEV;
203	}
204
205	ipu_id = ipu_get_num(ipu);
206	if (ipu_id > 1) {
207		v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id);
208		return -ENODEV;
209	}
210
211	mutex_lock(&imxmd->mutex);
212
213	/* record this IPU */
214	if (!imxmd->ipu[ipu_id])
215		imxmd->ipu[ipu_id] = ipu;
216
217	/* register the synchronous subdevs */
218	for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
219		intsd = &int_subdev[i];
220
221		sd = imxmd->sync_sd[ipu_id][i];
222
223		/*
224		 * skip if this sync subdev already registered or its
225		 * not a sync subdev (one of the CSIs)
226		 */
227		if (sd || !intsd->sync_register)
228			continue;
229
230		mutex_unlock(&imxmd->mutex);
231		sd = intsd->sync_register(&imxmd->v4l2_dev, ipu_dev, ipu,
232					  intsd->grp_id);
233		mutex_lock(&imxmd->mutex);
234		if (IS_ERR(sd)) {
235			ret = PTR_ERR(sd);
236			goto err_unwind;
237		}
238
239		imxmd->sync_sd[ipu_id][i] = sd;
240	}
241
242	/*
243	 * all the sync subdevs are registered, create the media links
244	 * between them.
245	 */
246	for (i = 0; i < NUM_IPU_SUBDEVS; i++) {
247		intsd = &int_subdev[i];
248
249		if (intsd->grp_id == csi->grp_id) {
250			sd = csi;
251		} else {
252			sd = imxmd->sync_sd[ipu_id][i];
253			if (!sd)
254				continue;
255		}
256
257		ret = create_ipu_internal_links(imxmd, intsd, sd, ipu_id);
258		if (ret) {
259			mutex_unlock(&imxmd->mutex);
260			imx_media_unregister_ipu_internal_subdevs(imxmd);
261			return ret;
262		}
263	}
264
265	mutex_unlock(&imxmd->mutex);
266	return 0;
267
268err_unwind:
269	while (--i >= 0) {
270		intsd = &int_subdev[i];
271		sd = imxmd->sync_sd[ipu_id][i];
272		if (!sd || !intsd->sync_unregister)
273			continue;
274		mutex_unlock(&imxmd->mutex);
275		intsd->sync_unregister(sd);
276		mutex_lock(&imxmd->mutex);
277	}
278
279	mutex_unlock(&imxmd->mutex);
280	return ret;
281}
282
283void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd)
284{
285	const struct internal_subdev *intsd;
286	struct v4l2_subdev *sd;
287	int i, j;
288
289	mutex_lock(&imxmd->mutex);
290
291	for (i = 0; i < 2; i++) {
292		for (j = 0; j < NUM_IPU_SUBDEVS; j++) {
293			intsd = &int_subdev[j];
294			sd = imxmd->sync_sd[i][j];
295
296			if (!sd || !intsd->sync_unregister)
297				continue;
298
299			mutex_unlock(&imxmd->mutex);
300			intsd->sync_unregister(sd);
301			mutex_lock(&imxmd->mutex);
302		}
303	}
304
305	mutex_unlock(&imxmd->mutex);
306}