Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / media / i2c / adv748x / adv748x-csi2.c
blobebe7da8ebed77377fb3f7b49d086b02767df5969
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Driver for Analog Devices ADV748X CSI-2 Transmitter
5 * Copyright (C) 2017 Renesas Electronics Corp.
6 */
8 #include <linux/module.h>
10 #include <media/v4l2-ctrls.h>
11 #include <media/v4l2-device.h>
12 #include <media/v4l2-ioctl.h>
14 #include "adv748x.h"
16 static const unsigned int adv748x_csi2_txa_fmts[] = {
17 MEDIA_BUS_FMT_UYVY8_1X16,
18 MEDIA_BUS_FMT_RGB888_1X24,
21 static const unsigned int adv748x_csi2_txb_fmts[] = {
22 MEDIA_BUS_FMT_UYVY8_1X16,
25 int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc)
27 return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT);
30 /**
31 * adv748x_csi2_register_link : Register and link internal entities
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
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.
42 static 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)
48 int ret;
50 if (!src->v4l2_dev) {
51 ret = v4l2_device_register_subdev(v4l2_dev, src);
52 if (ret)
53 return ret;
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;
62 if (enable)
63 tx->src = src;
65 return 0;
68 /* -----------------------------------------------------------------------------
69 * v4l2_subdev_internal_ops
72 static int adv748x_csi2_init_state(struct v4l2_subdev *sd,
73 struct v4l2_subdev_state *state)
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,
85 struct v4l2_mbus_framefmt *fmt;
87 fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SINK);
88 *fmt = adv748x_csi2_default_fmt;
90 fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SOURCE);
91 *fmt = adv748x_csi2_default_fmt;
93 return 0;
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.
102 static int adv748x_csi2_registered(struct v4l2_subdev *sd)
104 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
105 struct adv748x_state *state = tx->state;
106 int ret;
108 adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",
109 sd->name);
112 * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output
113 * HDMI.
115 * The HDMI->TXA link is enabled by default, as is the AFE->TXB one.
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;
125 /* TXB can output AFE signals only. */
126 if (is_txb(tx))
127 state->afe.tx = tx;
130 /* Register link to HDMI for TXA only. */
131 if (is_txb(tx) || !is_hdmi_enabled(state))
132 return 0;
134 ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd,
135 ADV748X_HDMI_SOURCE, true);
136 if (ret)
137 return ret;
139 /* The default HDMI output is TXA. */
140 state->hdmi.tx = tx;
142 return 0;
145 static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {
146 .init_state = adv748x_csi2_init_state,
147 .registered = adv748x_csi2_registered,
150 /* -----------------------------------------------------------------------------
151 * v4l2_subdev_video_ops
154 static int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable)
156 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
157 struct v4l2_subdev *src;
159 src = adv748x_get_remote_sd(&tx->pads[ADV748X_CSI2_SINK]);
160 if (!src)
161 return -EPIPE;
163 return v4l2_subdev_call(src, video, s_stream, enable);
166 static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = {
167 .s_stream = adv748x_csi2_s_stream,
170 /* -----------------------------------------------------------------------------
171 * v4l2_subdev_pad_ops
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.
177 static 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)
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);
189 * The format available on the source pad is the one applied on the sink
190 * pad.
192 if (code->pad == ADV748X_CSI2_SOURCE) {
193 struct v4l2_mbus_framefmt *fmt;
195 if (code->index)
196 return -EINVAL;
198 fmt = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SINK);
199 code->code = fmt->code;
201 return 0;
204 if (code->index >= num_fmts)
205 return -EINVAL;
207 code->code = codes[code->index];
209 return 0;
212 static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code)
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);
220 for (unsigned int i = 0; i < num_fmts; i++) {
221 if (codes[i] == code)
222 return true;
225 return false;
228 static int adv748x_csi2_set_format(struct v4l2_subdev *sd,
229 struct v4l2_subdev_state *sd_state,
230 struct v4l2_subdev_format *sdformat)
232 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
233 struct v4l2_mbus_framefmt *mbusformat;
235 if (sdformat->pad == ADV748X_CSI2_SOURCE)
236 return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
239 * Make sure the format is supported, if not default it to
240 * UYVY8 as it's supported by both TXes.
242 if (!adv748x_csi2_is_fmt_supported(tx, sdformat->format.code))
243 sdformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
245 mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad);
246 *mbusformat = sdformat->format;
248 /* Propagate format to the source pad. */
249 mbusformat = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SOURCE);
250 *mbusformat = sdformat->format;
252 return 0;
255 static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
256 struct v4l2_mbus_config *config)
258 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
260 if (pad != ADV748X_CSI2_SOURCE)
261 return -EINVAL;
263 config->type = V4L2_MBUS_CSI2_DPHY;
264 config->bus.mipi_csi2.num_data_lanes = tx->active_lanes;
266 return 0;
269 static 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,
276 /* -----------------------------------------------------------------------------
277 * v4l2_subdev_ops
280 static const struct v4l2_subdev_ops adv748x_csi2_ops = {
281 .video = &adv748x_csi2_video_ops,
282 .pad = &adv748x_csi2_pad_ops,
285 /* -----------------------------------------------------------------------------
286 * Subdev module and controls
289 int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate)
291 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
293 if (!tx->pixel_rate)
294 return -EINVAL;
296 return v4l2_ctrl_s_ctrl_int64(tx->pixel_rate, rate);
299 static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl)
301 switch (ctrl->id) {
302 case V4L2_CID_PIXEL_RATE:
303 return 0;
304 default:
305 return -EINVAL;
309 static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = {
310 .s_ctrl = adv748x_csi2_s_ctrl,
313 static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
316 v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1);
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);
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;
329 return v4l2_ctrl_handler_setup(&tx->ctrl_hdl);
332 int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
334 int ret;
336 if (!is_tx_enabled(tx))
337 return 0;
339 adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops,
340 MEDIA_ENT_F_VID_IF_BRIDGE,
341 is_txa(tx) ? "txa" : "txb");
343 /* Register internal ops for incremental subdev registration */
344 tx->sd.internal_ops = &adv748x_csi2_internal_ops;
346 tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
347 tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
349 ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS,
350 tx->pads);
351 if (ret)
352 return ret;
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;
359 ret = adv748x_csi2_init_controls(tx);
360 if (ret)
361 goto err_cleanup_subdev;
363 tx->sd.state_lock = &state->mutex;
364 ret = v4l2_subdev_init_finalize(&tx->sd);
365 if (ret)
366 goto err_free_ctrl;
368 ret = v4l2_async_register_subdev(&tx->sd);
369 if (ret)
370 goto err_free_ctrl;
372 return 0;
374 err_free_ctrl:
375 v4l2_ctrl_handler_free(&tx->ctrl_hdl);
376 err_cleanup_subdev:
377 v4l2_subdev_cleanup(&tx->sd);
378 err_free_media:
379 media_entity_cleanup(&tx->sd.entity);
381 return ret;
384 void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
386 if (!is_tx_enabled(tx))
387 return;
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);