File Index
Files and directories
Sign in
FILE

adv748x-csi2.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/i2c/adv748x/adv748x-csi2.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Driver for Analog Devices ADV748X CSI-2 Transmitter
  4 *
  5 * Copyright (C) 2017 Renesas Electronics Corp.
  6 */
  7
  8#include <linux/module.h>
  9
 10#include <media/v4l2-ctrls.h>
 11#include <media/v4l2-device.h>
 12#include <media/v4l2-ioctl.h>
 13
 14#include "adv748x.h"
 15
 16static const unsigned int adv748x_csi2_txa_fmts[] = {
 17	MEDIA_BUS_FMT_UYVY8_1X16,
 18	MEDIA_BUS_FMT_RGB888_1X24,
 19};
 20
 21static const unsigned int adv748x_csi2_txb_fmts[] = {
 22	MEDIA_BUS_FMT_UYVY8_1X16,
 23};
 24
 25int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc)
 26{
 27	return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT);
 28}
 29
 30/**
 31 * adv748x_csi2_register_link : Register and link internal entities
 32 *
 33 * @tx: CSI2 private entity
 34 * @v4l2_dev: Video registration device
 35 * @src: Source subdevice to establish link
 36 * @src_pad: Pad number of source to link to this @tx
 37 * @enable: Link enabled flag
 38 *
 39 * Ensure that the subdevice is registered against the v4l2_device, and link the
 40 * source pad to the sink pad of the CSI2 bus entity.
 41 */
 42static int adv748x_csi2_register_link(struct adv748x_csi2 *tx,
 43				      struct v4l2_device *v4l2_dev,
 44				      struct v4l2_subdev *src,
 45				      unsigned int src_pad,
 46				      bool enable)
 47{
 48	int ret;
 49
 50	if (!src->v4l2_dev) {
 51		ret = v4l2_device_register_subdev(v4l2_dev, src);
 52		if (ret)
 53			return ret;
 54	}
 55
 56	ret = media_create_pad_link(&src->entity, src_pad,
 57				    &tx->sd.entity, ADV748X_CSI2_SINK,
 58				    enable ? MEDIA_LNK_FL_ENABLED : 0);
 59	if (ret)
 60		return ret;
 61
 62	if (enable)
 63		tx->src = src;
 64
 65	return 0;
 66}
 67
 68/* -----------------------------------------------------------------------------
 69 * v4l2_subdev_internal_ops
 70 */
 71
 72static int adv748x_csi2_init_state(struct v4l2_subdev *sd,
 73				   struct v4l2_subdev_state *state)
 74{
 75	static const struct v4l2_mbus_framefmt adv748x_csi2_default_fmt = {
 76		.width		= 1280,
 77		.height		= 720,
 78		.code		= MEDIA_BUS_FMT_UYVY8_1X16,
 79		.colorspace	= V4L2_COLORSPACE_SRGB,
 80		.field		= V4L2_FIELD_NONE,
 81		.ycbcr_enc	= V4L2_YCBCR_ENC_DEFAULT,
 82		.quantization	= V4L2_QUANTIZATION_DEFAULT,
 83		.xfer_func	= V4L2_XFER_FUNC_DEFAULT,
 84	};
 85	struct v4l2_mbus_framefmt *fmt;
 86
 87	fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SINK);
 88	*fmt = adv748x_csi2_default_fmt;
 89
 90	fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SOURCE);
 91	*fmt = adv748x_csi2_default_fmt;
 92
 93	return 0;
 94}
 95
 96/*
 97 * We use the internal registered operation to be able to ensure that our
 98 * incremental subdevices (not connected in the forward path) can be registered
 99 * against the resulting video path and media device.
100 */
101
102static int adv748x_csi2_registered(struct v4l2_subdev *sd)
103{
104	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
105	struct adv748x_state *state = tx->state;
106	int ret;
107
108	adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",
109			sd->name);
110
111	/*
112	 * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output
113	 * HDMI.
114	 *
115	 * The HDMI->TXA link is enabled by default, as is the AFE->TXB one.
116	 */
117	if (is_afe_enabled(state)) {
118		ret = adv748x_csi2_register_link(tx, sd->v4l2_dev,
119						 &state->afe.sd,
120						 ADV748X_AFE_SOURCE,
121						 is_txb(tx));
122		if (ret)
123			return ret;
124
125		/* TXB can output AFE signals only. */
126		if (is_txb(tx))
127			state->afe.tx = tx;
128	}
129
130	/* Register link to HDMI for TXA only. */
131	if (is_txb(tx) || !is_hdmi_enabled(state))
132		return 0;
133
134	ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd,
135					 ADV748X_HDMI_SOURCE, true);
136	if (ret)
137		return ret;
138
139	/* The default HDMI output is TXA. */
140	state->hdmi.tx = tx;
141
142	return 0;
143}
144
145static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {
146	.init_state = adv748x_csi2_init_state,
147	.registered = adv748x_csi2_registered,
148};
149
150/* -----------------------------------------------------------------------------
151 * v4l2_subdev_video_ops
152 */
153
154static int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable)
155{
156	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
157	struct v4l2_subdev *src;
158
159	src = adv748x_get_remote_sd(&tx->pads[ADV748X_CSI2_SINK]);
160	if (!src)
161		return -EPIPE;
162
163	return v4l2_subdev_call(src, video, s_stream, enable);
164}
165
166static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = {
167	.s_stream = adv748x_csi2_s_stream,
168};
169
170/* -----------------------------------------------------------------------------
171 * v4l2_subdev_pad_ops
172 *
173 * The CSI2 bus pads are ignorant to the data sizes or formats.
174 * But we must support setting the pad formats for format propagation.
175 */
176
177static int adv748x_csi2_enum_mbus_code(struct v4l2_subdev *sd,
178				       struct v4l2_subdev_state *sd_state,
179				       struct v4l2_subdev_mbus_code_enum *code)
180{
181	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
182	const unsigned int *codes = is_txa(tx) ?
183				    adv748x_csi2_txa_fmts :
184				    adv748x_csi2_txb_fmts;
185	size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts)
186				     : ARRAY_SIZE(adv748x_csi2_txb_fmts);
187
188	/*
189	 * The format available on the source pad is the one applied on the sink
190	 * pad.
191	 */
192	if (code->pad == ADV748X_CSI2_SOURCE) {
193		struct v4l2_mbus_framefmt *fmt;
194
195		if (code->index)
196			return -EINVAL;
197
198		fmt = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SINK);
199		code->code = fmt->code;
200
201		return 0;
202	}
203
204	if (code->index >= num_fmts)
205		return -EINVAL;
206
207	code->code = codes[code->index];
208
209	return 0;
210}
211
212static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code)
213{
214	const unsigned int *codes = is_txa(tx) ?
215				    adv748x_csi2_txa_fmts :
216				    adv748x_csi2_txb_fmts;
217	size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts)
218				     : ARRAY_SIZE(adv748x_csi2_txb_fmts);
219
220	for (unsigned int i = 0; i < num_fmts; i++) {
221		if (codes[i] == code)
222			return true;
223	}
224
225	return false;
226}
227
228static int adv748x_csi2_set_format(struct v4l2_subdev *sd,
229				   struct v4l2_subdev_state *sd_state,
230				   struct v4l2_subdev_format *sdformat)
231{
232	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
233	struct v4l2_mbus_framefmt *mbusformat;
234
235	if (sdformat->pad == ADV748X_CSI2_SOURCE)
236		return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
237
238	/*
239	 * Make sure the format is supported, if not default it to
240	 * UYVY8 as it's supported by both TXes.
241	 */
242	if (!adv748x_csi2_is_fmt_supported(tx, sdformat->format.code))
243		sdformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
244
245	mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad);
246	*mbusformat = sdformat->format;
247
248	/* Propagate format to the source pad. */
249	mbusformat = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SOURCE);
250	*mbusformat = sdformat->format;
251
252	return 0;
253}
254
255static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
256					struct v4l2_mbus_config *config)
257{
258	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
259
260	if (pad != ADV748X_CSI2_SOURCE)
261		return -EINVAL;
262
263	config->type = V4L2_MBUS_CSI2_DPHY;
264	config->bus.mipi_csi2.num_data_lanes = tx->active_lanes;
265
266	return 0;
267}
268
269static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = {
270	.enum_mbus_code = adv748x_csi2_enum_mbus_code,
271	.get_fmt = v4l2_subdev_get_fmt,
272	.set_fmt = adv748x_csi2_set_format,
273	.get_mbus_config = adv748x_csi2_get_mbus_config,
274};
275
276/* -----------------------------------------------------------------------------
277 * v4l2_subdev_ops
278 */
279
280static const struct v4l2_subdev_ops adv748x_csi2_ops = {
281	.video = &adv748x_csi2_video_ops,
282	.pad = &adv748x_csi2_pad_ops,
283};
284
285/* -----------------------------------------------------------------------------
286 * Subdev module and controls
287 */
288
289int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate)
290{
291	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
292
293	if (!tx->pixel_rate)
294		return -EINVAL;
295
296	return v4l2_ctrl_s_ctrl_int64(tx->pixel_rate, rate);
297}
298
299static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl)
300{
301	switch (ctrl->id) {
302	case V4L2_CID_PIXEL_RATE:
303		return 0;
304	default:
305		return -EINVAL;
306	}
307}
308
309static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = {
310	.s_ctrl = adv748x_csi2_s_ctrl,
311};
312
313static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
314{
315
316	v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1);
317
318	tx->pixel_rate = v4l2_ctrl_new_std(&tx->ctrl_hdl,
319					   &adv748x_csi2_ctrl_ops,
320					   V4L2_CID_PIXEL_RATE, 1, INT_MAX,
321					   1, 1);
322
323	tx->sd.ctrl_handler = &tx->ctrl_hdl;
324	if (tx->ctrl_hdl.error) {
325		v4l2_ctrl_handler_free(&tx->ctrl_hdl);
326		return tx->ctrl_hdl.error;
327	}
328
329	return v4l2_ctrl_handler_setup(&tx->ctrl_hdl);
330}
331
332int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
333{
334	int ret;
335
336	if (!is_tx_enabled(tx))
337		return 0;
338
339	adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops,
340			    MEDIA_ENT_F_VID_IF_BRIDGE,
341			    is_txa(tx) ? "txa" : "txb");
342
343	/* Register internal ops for incremental subdev registration */
344	tx->sd.internal_ops = &adv748x_csi2_internal_ops;
345
346	tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
347	tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
348
349	ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS,
350				     tx->pads);
351	if (ret)
352		return ret;
353
354	ret = v4l2_async_subdev_endpoint_add(&tx->sd,
355					     of_fwnode_handle(state->endpoints[tx->port]));
356	if (ret)
357		goto err_free_media;
358
359	ret = adv748x_csi2_init_controls(tx);
360	if (ret)
361		goto err_cleanup_subdev;
362
363	tx->sd.state_lock = &state->mutex;
364	ret = v4l2_subdev_init_finalize(&tx->sd);
365	if (ret)
366		goto err_free_ctrl;
367
368	ret = v4l2_async_register_subdev(&tx->sd);
369	if (ret)
370		goto err_free_ctrl;
371
372	return 0;
373
374err_free_ctrl:
375	v4l2_ctrl_handler_free(&tx->ctrl_hdl);
376err_cleanup_subdev:
377	v4l2_subdev_cleanup(&tx->sd);
378err_free_media:
379	media_entity_cleanup(&tx->sd.entity);
380
381	return ret;
382}
383
384void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
385{
386	if (!is_tx_enabled(tx))
387		return;
388
389	v4l2_async_unregister_subdev(&tx->sd);
390	media_entity_cleanup(&tx->sd.entity);
391	v4l2_ctrl_handler_free(&tx->ctrl_hdl);
392	v4l2_subdev_cleanup(&tx->sd);
393}
~/ Read-only file preview.