1 // SPDX-License-Identifier: GPL-2.0+
3 * Driver for Analog Devices ADV748X CSI-2 Transmitter
5 * Copyright (C) 2017 Renesas Electronics Corp.
8 #include <linux/module.h>
10 #include <media/v4l2-ctrls.h>
11 #include <media/v4l2-device.h>
12 #include <media/v4l2-ioctl.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
);
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
,
51 ret
= v4l2_device_register_subdev(v4l2_dev
, src
);
56 ret
= media_create_pad_link(&src
->entity
, src_pad
,
57 &tx
->sd
.entity
, ADV748X_CSI2_SINK
,
58 enable
? MEDIA_LNK_FL_ENABLED
: 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
= {
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
;
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
;
108 adv_dbg(state
, "Registered %s (%s)", is_txa(tx
) ? "TXA":"TXB",
112 * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output
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
,
125 /* TXB can output AFE signals only. */
130 /* Register link to HDMI for TXA only. */
131 if (is_txb(tx
) || !is_hdmi_enabled(state
))
134 ret
= adv748x_csi2_register_link(tx
, sd
->v4l2_dev
, &state
->hdmi
.sd
,
135 ADV748X_HDMI_SOURCE
, true);
139 /* The default HDMI output is TXA. */
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
]);
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
192 if (code
->pad
== ADV748X_CSI2_SOURCE
) {
193 struct v4l2_mbus_framefmt
*fmt
;
198 fmt
= v4l2_subdev_state_get_format(sd_state
, ADV748X_CSI2_SINK
);
199 code
->code
= fmt
->code
;
204 if (code
->index
>= num_fmts
)
207 code
->code
= codes
[code
->index
];
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
)
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
;
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
)
263 config
->type
= V4L2_MBUS_CSI2_DPHY
;
264 config
->bus
.mipi_csi2
.num_data_lanes
= tx
->active_lanes
;
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 /* -----------------------------------------------------------------------------
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
);
296 return v4l2_ctrl_s_ctrl_int64(tx
->pixel_rate
, rate
);
299 static int adv748x_csi2_s_ctrl(struct v4l2_ctrl
*ctrl
)
302 case V4L2_CID_PIXEL_RATE
:
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
,
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
)
336 if (!is_tx_enabled(tx
))
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
,
354 ret
= v4l2_async_subdev_endpoint_add(&tx
->sd
,
355 of_fwnode_handle(state
->endpoints
[tx
->port
]));
359 ret
= adv748x_csi2_init_controls(tx
);
361 goto err_cleanup_subdev
;
363 tx
->sd
.state_lock
= &state
->mutex
;
364 ret
= v4l2_subdev_init_finalize(&tx
->sd
);
368 ret
= v4l2_async_register_subdev(&tx
->sd
);
375 v4l2_ctrl_handler_free(&tx
->ctrl_hdl
);
377 v4l2_subdev_cleanup(&tx
->sd
);
379 media_entity_cleanup(&tx
->sd
.entity
);
384 void adv748x_csi2_cleanup(struct adv748x_csi2
*tx
)
386 if (!is_tx_enabled(tx
))
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
);