adv748x-csi2.c (8315B)
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 config->bus.mipi_csi2.num_data_lanes = tx->active_lanes; 226 227 return 0; 228} 229 230static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { 231 .get_fmt = adv748x_csi2_get_format, 232 .set_fmt = adv748x_csi2_set_format, 233 .get_mbus_config = adv748x_csi2_get_mbus_config, 234}; 235 236/* ----------------------------------------------------------------------------- 237 * v4l2_subdev_ops 238 */ 239 240static const struct v4l2_subdev_ops adv748x_csi2_ops = { 241 .video = &adv748x_csi2_video_ops, 242 .pad = &adv748x_csi2_pad_ops, 243}; 244 245/* ----------------------------------------------------------------------------- 246 * Subdev module and controls 247 */ 248 249int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate) 250{ 251 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 252 253 if (!tx->pixel_rate) 254 return -EINVAL; 255 256 return v4l2_ctrl_s_ctrl_int64(tx->pixel_rate, rate); 257} 258 259static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl) 260{ 261 switch (ctrl->id) { 262 case V4L2_CID_PIXEL_RATE: 263 return 0; 264 default: 265 return -EINVAL; 266 } 267} 268 269static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = { 270 .s_ctrl = adv748x_csi2_s_ctrl, 271}; 272 273static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx) 274{ 275 276 v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1); 277 278 tx->pixel_rate = v4l2_ctrl_new_std(&tx->ctrl_hdl, 279 &adv748x_csi2_ctrl_ops, 280 V4L2_CID_PIXEL_RATE, 1, INT_MAX, 281 1, 1); 282 283 tx->sd.ctrl_handler = &tx->ctrl_hdl; 284 if (tx->ctrl_hdl.error) { 285 v4l2_ctrl_handler_free(&tx->ctrl_hdl); 286 return tx->ctrl_hdl.error; 287 } 288 289 return v4l2_ctrl_handler_setup(&tx->ctrl_hdl); 290} 291 292int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) 293{ 294 int ret; 295 296 if (!is_tx_enabled(tx)) 297 return 0; 298 299 adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops, 300 MEDIA_ENT_F_VID_IF_BRIDGE, 301 is_txa(tx) ? "txa" : "txb"); 302 303 /* Ensure that matching is based upon the endpoint fwnodes */ 304 tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]); 305 306 /* Register internal ops for incremental subdev registration */ 307 tx->sd.internal_ops = &adv748x_csi2_internal_ops; 308 309 tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; 310 tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 311 312 ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS, 313 tx->pads); 314 if (ret) 315 return ret; 316 317 ret = adv748x_csi2_init_controls(tx); 318 if (ret) 319 goto err_free_media; 320 321 ret = v4l2_async_register_subdev(&tx->sd); 322 if (ret) 323 goto err_free_ctrl; 324 325 return 0; 326 327err_free_ctrl: 328 v4l2_ctrl_handler_free(&tx->ctrl_hdl); 329err_free_media: 330 media_entity_cleanup(&tx->sd.entity); 331 332 return ret; 333} 334 335void adv748x_csi2_cleanup(struct adv748x_csi2 *tx) 336{ 337 if (!is_tx_enabled(tx)) 338 return; 339 340 v4l2_async_unregister_subdev(&tx->sd); 341 media_entity_cleanup(&tx->sd.entity); 342 v4l2_ctrl_handler_free(&tx->ctrl_hdl); 343}