brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · ebe7da8 Raw
394 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Driver for Analog Devices ADV748X CSI-2 Transmitter4 *5 * Copyright (C) 2017 Renesas Electronics Corp.6 */7 8#include <linux/module.h>9 10#include <media/v4l2-ctrls.h>11#include <media/v4l2-device.h>12#include <media/v4l2-ioctl.h>13 14#include "adv748x.h"15 16static const unsigned int adv748x_csi2_txa_fmts[] = {17	MEDIA_BUS_FMT_UYVY8_1X16,18	MEDIA_BUS_FMT_RGB888_1X24,19};20 21static const unsigned int adv748x_csi2_txb_fmts[] = {22	MEDIA_BUS_FMT_UYVY8_1X16,23};24 25int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc)26{27	return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT);28}29 30/**31 * adv748x_csi2_register_link : Register and link internal entities32 *33 * @tx: CSI2 private entity34 * @v4l2_dev: Video registration device35 * @src: Source subdevice to establish link36 * @src_pad: Pad number of source to link to this @tx37 * @enable: Link enabled flag38 *39 * Ensure that the subdevice is registered against the v4l2_device, and link the40 * source pad to the sink pad of the CSI2 bus entity.41 */42static 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)47{48	int ret;49 50	if (!src->v4l2_dev) {51		ret = v4l2_device_register_subdev(v4l2_dev, src);52		if (ret)53			return ret;54	}55 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;61 62	if (enable)63		tx->src = src;64 65	return 0;66}67 68/* -----------------------------------------------------------------------------69 * v4l2_subdev_internal_ops70 */71 72static int adv748x_csi2_init_state(struct v4l2_subdev *sd,73				   struct v4l2_subdev_state *state)74{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,84	};85	struct v4l2_mbus_framefmt *fmt;86 87	fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SINK);88	*fmt = adv748x_csi2_default_fmt;89 90	fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SOURCE);91	*fmt = adv748x_csi2_default_fmt;92 93	return 0;94}95 96/*97 * We use the internal registered operation to be able to ensure that our98 * incremental subdevices (not connected in the forward path) can be registered99 * against the resulting video path and media device.100 */101 102static int adv748x_csi2_registered(struct v4l2_subdev *sd)103{104	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);105	struct adv748x_state *state = tx->state;106	int ret;107 108	adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",109			sd->name);110 111	/*112	 * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output113	 * HDMI.114	 *115	 * The HDMI->TXA link is enabled by default, as is the AFE->TXB one.116	 */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;124 125		/* TXB can output AFE signals only. */126		if (is_txb(tx))127			state->afe.tx = tx;128	}129 130	/* Register link to HDMI for TXA only. */131	if (is_txb(tx) || !is_hdmi_enabled(state))132		return 0;133 134	ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd,135					 ADV748X_HDMI_SOURCE, true);136	if (ret)137		return ret;138 139	/* The default HDMI output is TXA. */140	state->hdmi.tx = tx;141 142	return 0;143}144 145static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {146	.init_state = adv748x_csi2_init_state,147	.registered = adv748x_csi2_registered,148};149 150/* -----------------------------------------------------------------------------151 * v4l2_subdev_video_ops152 */153 154static int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable)155{156	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);157	struct v4l2_subdev *src;158 159	src = adv748x_get_remote_sd(&tx->pads[ADV748X_CSI2_SINK]);160	if (!src)161		return -EPIPE;162 163	return v4l2_subdev_call(src, video, s_stream, enable);164}165 166static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = {167	.s_stream = adv748x_csi2_s_stream,168};169 170/* -----------------------------------------------------------------------------171 * v4l2_subdev_pad_ops172 *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.175 */176 177static 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)180{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);187 188	/*189	 * The format available on the source pad is the one applied on the sink190	 * pad.191	 */192	if (code->pad == ADV748X_CSI2_SOURCE) {193		struct v4l2_mbus_framefmt *fmt;194 195		if (code->index)196			return -EINVAL;197 198		fmt = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SINK);199		code->code = fmt->code;200 201		return 0;202	}203 204	if (code->index >= num_fmts)205		return -EINVAL;206 207	code->code = codes[code->index];208 209	return 0;210}211 212static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code)213{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);219 220	for (unsigned int i = 0; i < num_fmts; i++) {221		if (codes[i] == code)222			return true;223	}224 225	return false;226}227 228static int adv748x_csi2_set_format(struct v4l2_subdev *sd,229				   struct v4l2_subdev_state *sd_state,230				   struct v4l2_subdev_format *sdformat)231{232	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);233	struct v4l2_mbus_framefmt *mbusformat;234 235	if (sdformat->pad == ADV748X_CSI2_SOURCE)236		return v4l2_subdev_get_fmt(sd, sd_state, sdformat);237 238	/*239	 * Make sure the format is supported, if not default it to240	 * UYVY8 as it's supported by both TXes.241	 */242	if (!adv748x_csi2_is_fmt_supported(tx, sdformat->format.code))243		sdformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16;244 245	mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad);246	*mbusformat = sdformat->format;247 248	/* Propagate format to the source pad. */249	mbusformat = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SOURCE);250	*mbusformat = sdformat->format;251 252	return 0;253}254 255static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,256					struct v4l2_mbus_config *config)257{258	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);259 260	if (pad != ADV748X_CSI2_SOURCE)261		return -EINVAL;262 263	config->type = V4L2_MBUS_CSI2_DPHY;264	config->bus.mipi_csi2.num_data_lanes = tx->active_lanes;265 266	return 0;267}268 269static 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,274};275 276/* -----------------------------------------------------------------------------277 * v4l2_subdev_ops278 */279 280static const struct v4l2_subdev_ops adv748x_csi2_ops = {281	.video = &adv748x_csi2_video_ops,282	.pad = &adv748x_csi2_pad_ops,283};284 285/* -----------------------------------------------------------------------------286 * Subdev module and controls287 */288 289int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate)290{291	struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);292 293	if (!tx->pixel_rate)294		return -EINVAL;295 296	return v4l2_ctrl_s_ctrl_int64(tx->pixel_rate, rate);297}298 299static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl)300{301	switch (ctrl->id) {302	case V4L2_CID_PIXEL_RATE:303		return 0;304	default:305		return -EINVAL;306	}307}308 309static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = {310	.s_ctrl = adv748x_csi2_s_ctrl,311};312 313static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)314{315 316	v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1);317 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);322 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;327	}328 329	return v4l2_ctrl_handler_setup(&tx->ctrl_hdl);330}331 332int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)333{334	int ret;335 336	if (!is_tx_enabled(tx))337		return 0;338 339	adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops,340			    MEDIA_ENT_F_VID_IF_BRIDGE,341			    is_txa(tx) ? "txa" : "txb");342 343	/* Register internal ops for incremental subdev registration */344	tx->sd.internal_ops = &adv748x_csi2_internal_ops;345 346	tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;347	tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE;348 349	ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS,350				     tx->pads);351	if (ret)352		return ret;353 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;358 359	ret = adv748x_csi2_init_controls(tx);360	if (ret)361		goto err_cleanup_subdev;362 363	tx->sd.state_lock = &state->mutex;364	ret = v4l2_subdev_init_finalize(&tx->sd);365	if (ret)366		goto err_free_ctrl;367 368	ret = v4l2_async_register_subdev(&tx->sd);369	if (ret)370		goto err_free_ctrl;371 372	return 0;373 374err_free_ctrl:375	v4l2_ctrl_handler_free(&tx->ctrl_hdl);376err_cleanup_subdev:377	v4l2_subdev_cleanup(&tx->sd);378err_free_media:379	media_entity_cleanup(&tx->sd.entity);380 381	return ret;382}383 384void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)385{386	if (!is_tx_enabled(tx))387		return;388 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);393}394