1e58f3082SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0+
23e89586aSKieran Bingham /*
33e89586aSKieran Bingham * Driver for Analog Devices ADV748X CSI-2 Transmitter
43e89586aSKieran Bingham *
53e89586aSKieran Bingham * Copyright (C) 2017 Renesas Electronics Corp.
63e89586aSKieran Bingham */
73e89586aSKieran Bingham
83e89586aSKieran Bingham #include <linux/module.h>
93e89586aSKieran Bingham #include <linux/mutex.h>
103e89586aSKieran Bingham
113e89586aSKieran Bingham #include <media/v4l2-ctrls.h>
123e89586aSKieran Bingham #include <media/v4l2-device.h>
133e89586aSKieran Bingham #include <media/v4l2-ioctl.h>
143e89586aSKieran Bingham
153e89586aSKieran Bingham #include "adv748x.h"
163e89586aSKieran Bingham
adv748x_csi2_set_virtual_channel(struct adv748x_csi2 * tx,unsigned int vc)1742bff048SNiklas Söderlund int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc)
183e89586aSKieran Bingham {
193e89586aSKieran Bingham return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT);
203e89586aSKieran Bingham }
213e89586aSKieran Bingham
223e89586aSKieran Bingham /**
233e89586aSKieran Bingham * adv748x_csi2_register_link : Register and link internal entities
243e89586aSKieran Bingham *
253e89586aSKieran Bingham * @tx: CSI2 private entity
263e89586aSKieran Bingham * @v4l2_dev: Video registration device
273e89586aSKieran Bingham * @src: Source subdevice to establish link
283e89586aSKieran Bingham * @src_pad: Pad number of source to link to this @tx
2921325e19SJacopo Mondi * @enable: Link enabled flag
303e89586aSKieran Bingham *
313e89586aSKieran Bingham * Ensure that the subdevice is registered against the v4l2_device, and link the
323e89586aSKieran Bingham * source pad to the sink pad of the CSI2 bus entity.
333e89586aSKieran Bingham */
adv748x_csi2_register_link(struct adv748x_csi2 * tx,struct v4l2_device * v4l2_dev,struct v4l2_subdev * src,unsigned int src_pad,bool enable)343e89586aSKieran Bingham static int adv748x_csi2_register_link(struct adv748x_csi2 *tx,
353e89586aSKieran Bingham struct v4l2_device *v4l2_dev,
363e89586aSKieran Bingham struct v4l2_subdev *src,
3721325e19SJacopo Mondi unsigned int src_pad,
3821325e19SJacopo Mondi bool enable)
393e89586aSKieran Bingham {
403e89586aSKieran Bingham int ret;
413e89586aSKieran Bingham
423e89586aSKieran Bingham if (!src->v4l2_dev) {
433e89586aSKieran Bingham ret = v4l2_device_register_subdev(v4l2_dev, src);
443e89586aSKieran Bingham if (ret)
453e89586aSKieran Bingham return ret;
463e89586aSKieran Bingham }
473e89586aSKieran Bingham
483361b9c4SJacopo Mondi ret = media_create_pad_link(&src->entity, src_pad,
493e89586aSKieran Bingham &tx->sd.entity, ADV748X_CSI2_SINK,
5021325e19SJacopo Mondi enable ? MEDIA_LNK_FL_ENABLED : 0);
513361b9c4SJacopo Mondi if (ret)
523361b9c4SJacopo Mondi return ret;
533361b9c4SJacopo Mondi
543361b9c4SJacopo Mondi if (enable)
553361b9c4SJacopo Mondi tx->src = src;
563361b9c4SJacopo Mondi
573361b9c4SJacopo Mondi return 0;
583e89586aSKieran Bingham }
593e89586aSKieran Bingham
603e89586aSKieran Bingham /* -----------------------------------------------------------------------------
613e89586aSKieran Bingham * v4l2_subdev_internal_ops
623e89586aSKieran Bingham *
633e89586aSKieran Bingham * We use the internal registered operation to be able to ensure that our
643e89586aSKieran Bingham * incremental subdevices (not connected in the forward path) can be registered
653e89586aSKieran Bingham * against the resulting video path and media device.
663e89586aSKieran Bingham */
673e89586aSKieran Bingham
adv748x_csi2_registered(struct v4l2_subdev * sd)683e89586aSKieran Bingham static int adv748x_csi2_registered(struct v4l2_subdev *sd)
693e89586aSKieran Bingham {
703e89586aSKieran Bingham struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
713e89586aSKieran Bingham struct adv748x_state *state = tx->state;
7221325e19SJacopo Mondi int ret;
733e89586aSKieran Bingham
743e89586aSKieran Bingham adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",
753e89586aSKieran Bingham sd->name);
763e89586aSKieran Bingham
773e89586aSKieran Bingham /*
7821325e19SJacopo Mondi * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output
7921325e19SJacopo Mondi * HDMI.
803e89586aSKieran Bingham *
8121325e19SJacopo Mondi * The HDMI->TXA link is enabled by default, as is the AFE->TXB one.
823e89586aSKieran Bingham */
8321325e19SJacopo Mondi if (is_afe_enabled(state)) {
8421325e19SJacopo Mondi ret = adv748x_csi2_register_link(tx, sd->v4l2_dev,
853e89586aSKieran Bingham &state->afe.sd,
8621325e19SJacopo Mondi ADV748X_AFE_SOURCE,
8721325e19SJacopo Mondi is_txb(tx));
8821325e19SJacopo Mondi if (ret)
8921325e19SJacopo Mondi return ret;
90a33df6acSJacopo Mondi
91a33df6acSJacopo Mondi /* TXB can output AFE signals only. */
92a33df6acSJacopo Mondi if (is_txb(tx))
93a33df6acSJacopo Mondi state->afe.tx = tx;
9421325e19SJacopo Mondi }
9521325e19SJacopo Mondi
9621325e19SJacopo Mondi /* Register link to HDMI for TXA only. */
9721325e19SJacopo Mondi if (is_txb(tx) || !is_hdmi_enabled(state))
981e2cb06fSJacopo Mondi return 0;
9921325e19SJacopo Mondi
100a33df6acSJacopo Mondi ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd,
10121325e19SJacopo Mondi ADV748X_HDMI_SOURCE, true);
102a33df6acSJacopo Mondi if (ret)
103a33df6acSJacopo Mondi return ret;
104a33df6acSJacopo Mondi
105a33df6acSJacopo Mondi /* The default HDMI output is TXA. */
106a33df6acSJacopo Mondi state->hdmi.tx = tx;
107a33df6acSJacopo Mondi
108a33df6acSJacopo Mondi return 0;
1093e89586aSKieran Bingham }
1103e89586aSKieran Bingham
1113e89586aSKieran Bingham static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {
1123e89586aSKieran Bingham .registered = adv748x_csi2_registered,
1133e89586aSKieran Bingham };
1143e89586aSKieran Bingham
1153e89586aSKieran Bingham /* -----------------------------------------------------------------------------
1163e89586aSKieran Bingham * v4l2_subdev_video_ops
1173e89586aSKieran Bingham */
1183e89586aSKieran Bingham
adv748x_csi2_s_stream(struct v4l2_subdev * sd,int enable)1193e89586aSKieran Bingham static int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable)
1203e89586aSKieran Bingham {
1213e89586aSKieran Bingham struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
1223e89586aSKieran Bingham struct v4l2_subdev *src;
1233e89586aSKieran Bingham
1243e89586aSKieran Bingham src = adv748x_get_remote_sd(&tx->pads[ADV748X_CSI2_SINK]);
1253e89586aSKieran Bingham if (!src)
1263e89586aSKieran Bingham return -EPIPE;
1273e89586aSKieran Bingham
1283e89586aSKieran Bingham return v4l2_subdev_call(src, video, s_stream, enable);
1293e89586aSKieran Bingham }
1303e89586aSKieran Bingham
1313e89586aSKieran Bingham static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = {
1323e89586aSKieran Bingham .s_stream = adv748x_csi2_s_stream,
1333e89586aSKieran Bingham };
1343e89586aSKieran Bingham
1353e89586aSKieran Bingham /* -----------------------------------------------------------------------------
1363e89586aSKieran Bingham * v4l2_subdev_pad_ops
1373e89586aSKieran Bingham *
1383e89586aSKieran Bingham * The CSI2 bus pads are ignorant to the data sizes or formats.
1393e89586aSKieran Bingham * But we must support setting the pad formats for format propagation.
1403e89586aSKieran Bingham */
1413e89586aSKieran Bingham
1423e89586aSKieran Bingham static struct v4l2_mbus_framefmt *
adv748x_csi2_get_pad_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,unsigned int pad,u32 which)1433e89586aSKieran Bingham adv748x_csi2_get_pad_format(struct v4l2_subdev *sd,
1440d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
1453e89586aSKieran Bingham unsigned int pad, u32 which)
1463e89586aSKieran Bingham {
1473e89586aSKieran Bingham struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
1483e89586aSKieran Bingham
1493e89586aSKieran Bingham if (which == V4L2_SUBDEV_FORMAT_TRY)
1500d346d2aSTomi Valkeinen return v4l2_subdev_get_try_format(sd, sd_state, pad);
1513e89586aSKieran Bingham
1523e89586aSKieran Bingham return &tx->format;
1533e89586aSKieran Bingham }
1543e89586aSKieran Bingham
adv748x_csi2_get_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * sdformat)1553e89586aSKieran Bingham static int adv748x_csi2_get_format(struct v4l2_subdev *sd,
1560d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
1573e89586aSKieran Bingham struct v4l2_subdev_format *sdformat)
1583e89586aSKieran Bingham {
1593e89586aSKieran Bingham struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
1603e89586aSKieran Bingham struct adv748x_state *state = tx->state;
1613e89586aSKieran Bingham struct v4l2_mbus_framefmt *mbusformat;
1623e89586aSKieran Bingham
1630d346d2aSTomi Valkeinen mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad,
1643e89586aSKieran Bingham sdformat->which);
1653e89586aSKieran Bingham if (!mbusformat)
1663e89586aSKieran Bingham return -EINVAL;
1673e89586aSKieran Bingham
1683e89586aSKieran Bingham mutex_lock(&state->mutex);
1693e89586aSKieran Bingham
1703e89586aSKieran Bingham sdformat->format = *mbusformat;
1713e89586aSKieran Bingham
1723e89586aSKieran Bingham mutex_unlock(&state->mutex);
1733e89586aSKieran Bingham
1743e89586aSKieran Bingham return 0;
1753e89586aSKieran Bingham }
1763e89586aSKieran Bingham
adv748x_csi2_set_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * sdformat)1773e89586aSKieran Bingham static int adv748x_csi2_set_format(struct v4l2_subdev *sd,
1780d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state,
1793e89586aSKieran Bingham struct v4l2_subdev_format *sdformat)
1803e89586aSKieran Bingham {
1813e89586aSKieran Bingham struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
1823e89586aSKieran Bingham struct adv748x_state *state = tx->state;
1833e89586aSKieran Bingham struct v4l2_mbus_framefmt *mbusformat;
1843e89586aSKieran Bingham int ret = 0;
1853e89586aSKieran Bingham
1860d346d2aSTomi Valkeinen mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad,
1873e89586aSKieran Bingham sdformat->which);
1883e89586aSKieran Bingham if (!mbusformat)
1893e89586aSKieran Bingham return -EINVAL;
1903e89586aSKieran Bingham
1913e89586aSKieran Bingham mutex_lock(&state->mutex);
1923e89586aSKieran Bingham
1933e89586aSKieran Bingham if (sdformat->pad == ADV748X_CSI2_SOURCE) {
1943e89586aSKieran Bingham const struct v4l2_mbus_framefmt *sink_fmt;
1953e89586aSKieran Bingham
1960d346d2aSTomi Valkeinen sink_fmt = adv748x_csi2_get_pad_format(sd, sd_state,
1973e89586aSKieran Bingham ADV748X_CSI2_SINK,
1983e89586aSKieran Bingham sdformat->which);
1993e89586aSKieran Bingham
2003e89586aSKieran Bingham if (!sink_fmt) {
2013e89586aSKieran Bingham ret = -EINVAL;
2023e89586aSKieran Bingham goto unlock;
2033e89586aSKieran Bingham }
2043e89586aSKieran Bingham
2053e89586aSKieran Bingham sdformat->format = *sink_fmt;
2063e89586aSKieran Bingham }
2073e89586aSKieran Bingham
2083e89586aSKieran Bingham *mbusformat = sdformat->format;
2093e89586aSKieran Bingham
2103e89586aSKieran Bingham unlock:
2113e89586aSKieran Bingham mutex_unlock(&state->mutex);
2123e89586aSKieran Bingham
2133e89586aSKieran Bingham return ret;
2143e89586aSKieran Bingham }
2153e89586aSKieran Bingham
adv748x_csi2_get_mbus_config(struct v4l2_subdev * sd,unsigned int pad,struct v4l2_mbus_config * config)216a61b1b5dSJacopo Mondi static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
217a61b1b5dSJacopo Mondi struct v4l2_mbus_config *config)
218a61b1b5dSJacopo Mondi {
219a61b1b5dSJacopo Mondi struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
220a61b1b5dSJacopo Mondi
221a61b1b5dSJacopo Mondi if (pad != ADV748X_CSI2_SOURCE)
222a61b1b5dSJacopo Mondi return -EINVAL;
223a61b1b5dSJacopo Mondi
224a61b1b5dSJacopo Mondi config->type = V4L2_MBUS_CSI2_DPHY;
2256a7bdd89SLaurent Pinchart config->bus.mipi_csi2.num_data_lanes = tx->active_lanes;
226a61b1b5dSJacopo Mondi
227a61b1b5dSJacopo Mondi return 0;
228a61b1b5dSJacopo Mondi }
229a61b1b5dSJacopo Mondi
2303e89586aSKieran Bingham static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = {
2313e89586aSKieran Bingham .get_fmt = adv748x_csi2_get_format,
2323e89586aSKieran Bingham .set_fmt = adv748x_csi2_set_format,
233a61b1b5dSJacopo Mondi .get_mbus_config = adv748x_csi2_get_mbus_config,
2343e89586aSKieran Bingham };
2353e89586aSKieran Bingham
2363e89586aSKieran Bingham /* -----------------------------------------------------------------------------
2373e89586aSKieran Bingham * v4l2_subdev_ops
2383e89586aSKieran Bingham */
2393e89586aSKieran Bingham
2403e89586aSKieran Bingham static const struct v4l2_subdev_ops adv748x_csi2_ops = {
2413e89586aSKieran Bingham .video = &adv748x_csi2_video_ops,
2423e89586aSKieran Bingham .pad = &adv748x_csi2_pad_ops,
2433e89586aSKieran Bingham };
2443e89586aSKieran Bingham
2453e89586aSKieran Bingham /* -----------------------------------------------------------------------------
2463e89586aSKieran Bingham * Subdev module and controls
2473e89586aSKieran Bingham */
2483e89586aSKieran Bingham
adv748x_csi2_set_pixelrate(struct v4l2_subdev * sd,s64 rate)2493e89586aSKieran Bingham int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate)
2503e89586aSKieran Bingham {
251b0fe7778SKieran Bingham struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
2523e89586aSKieran Bingham
253b0fe7778SKieran Bingham if (!tx->pixel_rate)
2543e89586aSKieran Bingham return -EINVAL;
2553e89586aSKieran Bingham
256b0fe7778SKieran Bingham return v4l2_ctrl_s_ctrl_int64(tx->pixel_rate, rate);
2573e89586aSKieran Bingham }
2583e89586aSKieran Bingham
adv748x_csi2_s_ctrl(struct v4l2_ctrl * ctrl)2593e89586aSKieran Bingham static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl)
2603e89586aSKieran Bingham {
2613e89586aSKieran Bingham switch (ctrl->id) {
2623e89586aSKieran Bingham case V4L2_CID_PIXEL_RATE:
2633e89586aSKieran Bingham return 0;
2643e89586aSKieran Bingham default:
2653e89586aSKieran Bingham return -EINVAL;
2663e89586aSKieran Bingham }
2673e89586aSKieran Bingham }
2683e89586aSKieran Bingham
2693e89586aSKieran Bingham static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = {
2703e89586aSKieran Bingham .s_ctrl = adv748x_csi2_s_ctrl,
2713e89586aSKieran Bingham };
2723e89586aSKieran Bingham
adv748x_csi2_init_controls(struct adv748x_csi2 * tx)2733e89586aSKieran Bingham static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
2743e89586aSKieran Bingham {
2753e89586aSKieran Bingham
2763e89586aSKieran Bingham v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1);
2773e89586aSKieran Bingham
278b0fe7778SKieran Bingham tx->pixel_rate = v4l2_ctrl_new_std(&tx->ctrl_hdl,
279b0fe7778SKieran Bingham &adv748x_csi2_ctrl_ops,
280b0fe7778SKieran Bingham V4L2_CID_PIXEL_RATE, 1, INT_MAX,
281b0fe7778SKieran Bingham 1, 1);
2823e89586aSKieran Bingham
2833e89586aSKieran Bingham tx->sd.ctrl_handler = &tx->ctrl_hdl;
2843e89586aSKieran Bingham if (tx->ctrl_hdl.error) {
2853e89586aSKieran Bingham v4l2_ctrl_handler_free(&tx->ctrl_hdl);
2863e89586aSKieran Bingham return tx->ctrl_hdl.error;
2873e89586aSKieran Bingham }
2883e89586aSKieran Bingham
2893e89586aSKieran Bingham return v4l2_ctrl_handler_setup(&tx->ctrl_hdl);
2903e89586aSKieran Bingham }
2913e89586aSKieran Bingham
adv748x_csi2_init(struct adv748x_state * state,struct adv748x_csi2 * tx)2923e89586aSKieran Bingham int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
2933e89586aSKieran Bingham {
2943e89586aSKieran Bingham int ret;
2953e89586aSKieran Bingham
296eccf442cSJacopo Mondi if (!is_tx_enabled(tx))
297eccf442cSJacopo Mondi return 0;
2983e89586aSKieran Bingham
2993e89586aSKieran Bingham adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops,
300b7bf15c4SSteve Longerbeam MEDIA_ENT_F_VID_IF_BRIDGE,
3013e89586aSKieran Bingham is_txa(tx) ? "txa" : "txb");
3023e89586aSKieran Bingham
3033e89586aSKieran Bingham /* Register internal ops for incremental subdev registration */
3043e89586aSKieran Bingham tx->sd.internal_ops = &adv748x_csi2_internal_ops;
3053e89586aSKieran Bingham
3063e89586aSKieran Bingham tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
3073e89586aSKieran Bingham tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
3083e89586aSKieran Bingham
3093e89586aSKieran Bingham ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS,
3103e89586aSKieran Bingham tx->pads);
3113e89586aSKieran Bingham if (ret)
3123e89586aSKieran Bingham return ret;
3133e89586aSKieran Bingham
314*1e345458SSakari Ailus ret = v4l2_async_subdev_endpoint_add(&tx->sd,
315*1e345458SSakari Ailus of_fwnode_handle(state->endpoints[tx->port]));
3163e89586aSKieran Bingham if (ret)
3173e89586aSKieran Bingham goto err_free_media;
3183e89586aSKieran Bingham
319*1e345458SSakari Ailus ret = adv748x_csi2_init_controls(tx);
320*1e345458SSakari Ailus if (ret)
321*1e345458SSakari Ailus goto err_cleanup_subdev;
322*1e345458SSakari Ailus
3233e89586aSKieran Bingham ret = v4l2_async_register_subdev(&tx->sd);
3243e89586aSKieran Bingham if (ret)
3253e89586aSKieran Bingham goto err_free_ctrl;
3263e89586aSKieran Bingham
3273e89586aSKieran Bingham return 0;
3283e89586aSKieran Bingham
3293e89586aSKieran Bingham err_free_ctrl:
3303e89586aSKieran Bingham v4l2_ctrl_handler_free(&tx->ctrl_hdl);
331*1e345458SSakari Ailus err_cleanup_subdev:
332*1e345458SSakari Ailus v4l2_subdev_cleanup(&tx->sd);
3333e89586aSKieran Bingham err_free_media:
3343e89586aSKieran Bingham media_entity_cleanup(&tx->sd.entity);
3353e89586aSKieran Bingham
3363e89586aSKieran Bingham return ret;
3373e89586aSKieran Bingham }
3383e89586aSKieran Bingham
adv748x_csi2_cleanup(struct adv748x_csi2 * tx)3393e89586aSKieran Bingham void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
3403e89586aSKieran Bingham {
341eccf442cSJacopo Mondi if (!is_tx_enabled(tx))
342eccf442cSJacopo Mondi return;
343eccf442cSJacopo Mondi
3443e89586aSKieran Bingham v4l2_async_unregister_subdev(&tx->sd);
3453e89586aSKieran Bingham media_entity_cleanup(&tx->sd.entity);
3463e89586aSKieran Bingham v4l2_ctrl_handler_free(&tx->ctrl_hdl);
347*1e345458SSakari Ailus v4l2_subdev_cleanup(&tx->sd);
3483e89586aSKieran Bingham }
349