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