brintos

brintos / linux-shallow public Read only

0
0
Text · 23.5 KiB · d76eb58 Raw
871 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * V4L2 Capture ISI subdev driver for i.MX8QXP/QM platform4 *5 * ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which6 * used to process image from camera sensor to memory or DC7 *8 * Copyright (c) 2019 NXP Semiconductor9 */10 11#include <linux/device.h>12#include <linux/errno.h>13#include <linux/interrupt.h>14#include <linux/kernel.h>15#include <linux/minmax.h>16#include <linux/mutex.h>17#include <linux/of.h>18#include <linux/platform_device.h>19#include <linux/types.h>20#include <linux/videodev2.h>21 22#include <media/media-entity.h>23#include <media/v4l2-subdev.h>24#include <media/videobuf2-v4l2.h>25 26#include "imx8-isi-core.h"27#include "imx8-isi-regs.h"28 29/*30 * While the ISI receives data from the gasket on a 3x12-bit bus, the pipeline31 * subdev conceptually includes the gasket in order to avoid exposing an extra32 * subdev between the CSIS and the ISI. We thus need to expose media bus codes33 * corresponding to the CSIS output, which is narrower.34 */35static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = {36	/* YUV formats */37	{38		.mbus_code	= MEDIA_BUS_FMT_UYVY8_1X16,39		.output		= MEDIA_BUS_FMT_YUV8_1X24,40		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK),41		.encoding	= MXC_ISI_ENC_YUV,42	}, {43		.mbus_code	= MEDIA_BUS_FMT_YUV8_1X24,44		.output		= MEDIA_BUS_FMT_YUV8_1X24,45		.pads		= BIT(MXC_ISI_PIPE_PAD_SOURCE),46		.encoding	= MXC_ISI_ENC_YUV,47	},48	/* RGB formats */49	{50		.mbus_code	= MEDIA_BUS_FMT_RGB565_1X16,51		.output		= MEDIA_BUS_FMT_RGB888_1X24,52		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK),53		.encoding	= MXC_ISI_ENC_RGB,54	}, {55		.mbus_code	= MEDIA_BUS_FMT_RGB888_1X24,56		.output		= MEDIA_BUS_FMT_RGB888_1X24,57		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)58				| BIT(MXC_ISI_PIPE_PAD_SOURCE),59		.encoding	= MXC_ISI_ENC_RGB,60	},61	/* RAW formats */62	{63		.mbus_code	= MEDIA_BUS_FMT_Y8_1X8,64		.output		= MEDIA_BUS_FMT_Y8_1X8,65		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)66				| BIT(MXC_ISI_PIPE_PAD_SOURCE),67		.encoding	= MXC_ISI_ENC_RAW,68	}, {69		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,70		.output		= MEDIA_BUS_FMT_Y10_1X10,71		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)72				| BIT(MXC_ISI_PIPE_PAD_SOURCE),73		.encoding	= MXC_ISI_ENC_RAW,74	}, {75		.mbus_code	= MEDIA_BUS_FMT_Y12_1X12,76		.output		= MEDIA_BUS_FMT_Y12_1X12,77		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)78				| BIT(MXC_ISI_PIPE_PAD_SOURCE),79		.encoding	= MXC_ISI_ENC_RAW,80	}, {81		.mbus_code	= MEDIA_BUS_FMT_Y14_1X14,82		.output		= MEDIA_BUS_FMT_Y14_1X14,83		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)84				| BIT(MXC_ISI_PIPE_PAD_SOURCE),85		.encoding	= MXC_ISI_ENC_RAW,86	}, {87		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,88		.output		= MEDIA_BUS_FMT_SBGGR8_1X8,89		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)90				| BIT(MXC_ISI_PIPE_PAD_SOURCE),91		.encoding	= MXC_ISI_ENC_RAW,92	}, {93		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,94		.output		= MEDIA_BUS_FMT_SGBRG8_1X8,95		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)96				| BIT(MXC_ISI_PIPE_PAD_SOURCE),97		.encoding	= MXC_ISI_ENC_RAW,98	}, {99		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,100		.output		= MEDIA_BUS_FMT_SGRBG8_1X8,101		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)102				| BIT(MXC_ISI_PIPE_PAD_SOURCE),103		.encoding	= MXC_ISI_ENC_RAW,104	}, {105		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,106		.output		= MEDIA_BUS_FMT_SRGGB8_1X8,107		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)108				| BIT(MXC_ISI_PIPE_PAD_SOURCE),109		.encoding	= MXC_ISI_ENC_RAW,110	}, {111		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,112		.output		= MEDIA_BUS_FMT_SBGGR10_1X10,113		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)114				| BIT(MXC_ISI_PIPE_PAD_SOURCE),115		.encoding	= MXC_ISI_ENC_RAW,116	}, {117		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,118		.output		= MEDIA_BUS_FMT_SGBRG10_1X10,119		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)120				| BIT(MXC_ISI_PIPE_PAD_SOURCE),121		.encoding	= MXC_ISI_ENC_RAW,122	}, {123		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,124		.output		= MEDIA_BUS_FMT_SGRBG10_1X10,125		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)126				| BIT(MXC_ISI_PIPE_PAD_SOURCE),127		.encoding	= MXC_ISI_ENC_RAW,128	}, {129		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,130		.output		= MEDIA_BUS_FMT_SRGGB10_1X10,131		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)132				| BIT(MXC_ISI_PIPE_PAD_SOURCE),133		.encoding	= MXC_ISI_ENC_RAW,134	}, {135		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,136		.output		= MEDIA_BUS_FMT_SBGGR12_1X12,137		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)138				| BIT(MXC_ISI_PIPE_PAD_SOURCE),139		.encoding	= MXC_ISI_ENC_RAW,140	}, {141		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,142		.output		= MEDIA_BUS_FMT_SGBRG12_1X12,143		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)144				| BIT(MXC_ISI_PIPE_PAD_SOURCE),145		.encoding	= MXC_ISI_ENC_RAW,146	}, {147		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,148		.output		= MEDIA_BUS_FMT_SGRBG12_1X12,149		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)150				| BIT(MXC_ISI_PIPE_PAD_SOURCE),151		.encoding	= MXC_ISI_ENC_RAW,152	}, {153		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,154		.output		= MEDIA_BUS_FMT_SRGGB12_1X12,155		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)156				| BIT(MXC_ISI_PIPE_PAD_SOURCE),157		.encoding	= MXC_ISI_ENC_RAW,158	}, {159		.mbus_code	= MEDIA_BUS_FMT_SBGGR14_1X14,160		.output		= MEDIA_BUS_FMT_SBGGR14_1X14,161		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)162				| BIT(MXC_ISI_PIPE_PAD_SOURCE),163		.encoding	= MXC_ISI_ENC_RAW,164	}, {165		.mbus_code	= MEDIA_BUS_FMT_SGBRG14_1X14,166		.output		= MEDIA_BUS_FMT_SGBRG14_1X14,167		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)168				| BIT(MXC_ISI_PIPE_PAD_SOURCE),169		.encoding	= MXC_ISI_ENC_RAW,170	}, {171		.mbus_code	= MEDIA_BUS_FMT_SGRBG14_1X14,172		.output		= MEDIA_BUS_FMT_SGRBG14_1X14,173		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)174				| BIT(MXC_ISI_PIPE_PAD_SOURCE),175		.encoding	= MXC_ISI_ENC_RAW,176	}, {177		.mbus_code	= MEDIA_BUS_FMT_SRGGB14_1X14,178		.output		= MEDIA_BUS_FMT_SRGGB14_1X14,179		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)180				| BIT(MXC_ISI_PIPE_PAD_SOURCE),181		.encoding	= MXC_ISI_ENC_RAW,182	},183	/* JPEG */184	{185		.mbus_code	= MEDIA_BUS_FMT_JPEG_1X8,186		.output		= MEDIA_BUS_FMT_JPEG_1X8,187		.pads		= BIT(MXC_ISI_PIPE_PAD_SINK)188				| BIT(MXC_ISI_PIPE_PAD_SOURCE),189		.encoding	= MXC_ISI_ENC_RAW,190	}191};192 193const struct mxc_isi_bus_format_info *194mxc_isi_bus_format_by_code(u32 code, unsigned int pad)195{196	unsigned int i;197 198	for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) {199		const struct mxc_isi_bus_format_info *info =200			&mxc_isi_bus_formats[i];201 202		if (info->mbus_code == code && info->pads & BIT(pad))203			return info;204	}205 206	return NULL;207}208 209const struct mxc_isi_bus_format_info *210mxc_isi_bus_format_by_index(unsigned int index, unsigned int pad)211{212	unsigned int i;213 214	for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); i++) {215		const struct mxc_isi_bus_format_info *info =216			&mxc_isi_bus_formats[i];217 218		if (!(info->pads & BIT(pad)))219			continue;220 221		if (!index)222			return info;223 224		index--;225	}226 227	return NULL;228}229 230static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd)231{232	return container_of(sd, struct mxc_isi_pipe, sd);233}234 235int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)236{237	struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;238	const struct mxc_isi_bus_format_info *sink_info;239	const struct mxc_isi_bus_format_info *src_info;240	const struct v4l2_mbus_framefmt *sink_fmt;241	const struct v4l2_mbus_framefmt *src_fmt;242	const struct v4l2_rect *compose;243	struct v4l2_subdev_state *state;244	struct v4l2_subdev *sd = &pipe->sd;245	struct v4l2_area in_size, scale;246	struct v4l2_rect crop;247	u32 input;248	int ret;249 250	/*251	 * Find the connected input by inspecting the crossbar switch routing252	 * table.253	 */254	state = v4l2_subdev_lock_and_get_active_state(&xbar->sd);255	ret = v4l2_subdev_routing_find_opposite_end(&state->routing,256						    xbar->num_sinks + pipe->id,257						    0, &input, NULL);258	v4l2_subdev_unlock_state(state);259 260	if (ret)261		return -EPIPE;262 263	/* Configure the pipeline. */264	state = v4l2_subdev_lock_and_get_active_state(sd);265 266	sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);267	src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);268	compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK);269	crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE);270 271	sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,272					       MXC_ISI_PIPE_PAD_SINK);273	src_info = mxc_isi_bus_format_by_code(src_fmt->code,274					      MXC_ISI_PIPE_PAD_SOURCE);275 276	in_size.width = sink_fmt->width;277	in_size.height = sink_fmt->height;278	scale.width = compose->width;279	scale.height = compose->height;280 281	v4l2_subdev_unlock_state(state);282 283	/* Configure the ISI channel. */284	mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop,285			       sink_info->encoding, src_info->encoding);286 287	mxc_isi_channel_enable(pipe);288 289	/* Enable streams on the crossbar switch. */290	ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id,291					 BIT(0));292	if (ret) {293		mxc_isi_channel_disable(pipe);294		dev_err(pipe->isi->dev, "Failed to enable pipe %u\n",295			pipe->id);296		return ret;297	}298 299	return 0;300}301 302void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe)303{304	struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;305	int ret;306 307	ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id,308					  BIT(0));309	if (ret)310		dev_err(pipe->isi->dev, "Failed to disable pipe %u\n",311			pipe->id);312 313	mxc_isi_channel_disable(pipe);314}315 316/* -----------------------------------------------------------------------------317 * V4L2 subdev operations318 */319 320static struct v4l2_mbus_framefmt *321mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe,322			    struct v4l2_subdev_state *state,323			    unsigned int pad)324{325	return v4l2_subdev_state_get_format(state, pad);326}327 328static struct v4l2_rect *329mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe,330			  struct v4l2_subdev_state *state,331			  unsigned int pad)332{333	return v4l2_subdev_state_get_crop(state, pad);334}335 336static struct v4l2_rect *337mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe,338			     struct v4l2_subdev_state *state,339			     unsigned int pad)340{341	return v4l2_subdev_state_get_compose(state, pad);342}343 344static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd,345				   struct v4l2_subdev_state *state)346{347	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);348	struct v4l2_mbus_framefmt *fmt_source;349	struct v4l2_mbus_framefmt *fmt_sink;350	struct v4l2_rect *compose;351	struct v4l2_rect *crop;352 353	fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state,354					       MXC_ISI_PIPE_PAD_SINK);355	fmt_source = mxc_isi_pipe_get_pad_format(pipe, state,356						 MXC_ISI_PIPE_PAD_SOURCE);357 358	fmt_sink->width = MXC_ISI_DEF_WIDTH;359	fmt_sink->height = MXC_ISI_DEF_HEIGHT;360	fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK;361	fmt_sink->field = V4L2_FIELD_NONE;362	fmt_sink->colorspace = V4L2_COLORSPACE_JPEG;363	fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);364	fmt_sink->quantization =365		V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,366					      fmt_sink->ycbcr_enc);367	fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);368 369	*fmt_source = *fmt_sink;370	fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE;371 372	compose = mxc_isi_pipe_get_pad_compose(pipe, state,373					       MXC_ISI_PIPE_PAD_SINK);374	crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE);375 376	compose->left = 0;377	compose->top = 0;378	compose->width = MXC_ISI_DEF_WIDTH;379	compose->height = MXC_ISI_DEF_HEIGHT;380 381	*crop = *compose;382 383	return 0;384}385 386static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd,387				       struct v4l2_subdev_state *state,388				       struct v4l2_subdev_mbus_code_enum *code)389{390	static const u32 output_codes[] = {391		MEDIA_BUS_FMT_YUV8_1X24,392		MEDIA_BUS_FMT_RGB888_1X24,393	};394	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);395	const struct mxc_isi_bus_format_info *info;396	unsigned int index;397	unsigned int i;398 399	if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) {400		const struct v4l2_mbus_framefmt *format;401 402		format = mxc_isi_pipe_get_pad_format(pipe, state,403						     MXC_ISI_PIPE_PAD_SINK);404		info = mxc_isi_bus_format_by_code(format->code,405						  MXC_ISI_PIPE_PAD_SINK);406 407		if (info->encoding == MXC_ISI_ENC_RAW) {408			/*409			 * For RAW formats, the sink and source media bus codes410			 * must match.411			 */412			if (code->index)413				return -EINVAL;414 415			code->code = info->output;416		} else {417			/*418			 * For RGB or YUV formats, the ISI supports format419			 * conversion. Either of the two output formats can be420			 * used regardless of the input.421			 */422			if (code->index > 1)423				return -EINVAL;424 425			code->code = output_codes[code->index];426		}427 428		return 0;429	}430 431	index = code->index;432 433	for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) {434		info = &mxc_isi_bus_formats[i];435 436		if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK)))437			continue;438 439		if (index == 0) {440			code->code = info->mbus_code;441			return 0;442		}443 444		index--;445	}446 447	return -EINVAL;448}449 450static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd,451				struct v4l2_subdev_state *state,452				struct v4l2_subdev_format *fmt)453{454	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);455	struct v4l2_mbus_framefmt *mf = &fmt->format;456	const struct mxc_isi_bus_format_info *info;457	struct v4l2_mbus_framefmt *format;458	struct v4l2_rect *rect;459 460	if (vb2_is_busy(&pipe->video.vb2_q))461		return -EBUSY;462 463	if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) {464		unsigned int max_width;465 466		info = mxc_isi_bus_format_by_code(mf->code,467						  MXC_ISI_PIPE_PAD_SINK);468		if (!info)469			info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK,470							  MXC_ISI_PIPE_PAD_SINK);471 472		/*473		 * Limit the max line length if there's no adjacent pipe to474		 * chain with.475		 */476		max_width = pipe->id == pipe->isi->pdata->num_channels - 1477			  ? MXC_ISI_MAX_WIDTH_UNCHAINED478			  : MXC_ISI_MAX_WIDTH_CHAINED;479 480		mf->code = info->mbus_code;481		mf->width = clamp(mf->width, MXC_ISI_MIN_WIDTH, max_width);482		mf->height = clamp(mf->height, MXC_ISI_MIN_HEIGHT,483				   MXC_ISI_MAX_HEIGHT);484 485		/* Propagate the format to the source pad. */486		rect = mxc_isi_pipe_get_pad_compose(pipe, state,487						    MXC_ISI_PIPE_PAD_SINK);488		rect->width = mf->width;489		rect->height = mf->height;490 491		rect = mxc_isi_pipe_get_pad_crop(pipe, state,492						 MXC_ISI_PIPE_PAD_SOURCE);493		rect->left = 0;494		rect->top = 0;495		rect->width = mf->width;496		rect->height = mf->height;497 498		format = mxc_isi_pipe_get_pad_format(pipe, state,499						     MXC_ISI_PIPE_PAD_SOURCE);500		format->code = info->output;501		format->width = mf->width;502		format->height = mf->height;503	} else {504		/*505		 * For RGB or YUV formats, the ISI supports RGB <-> YUV format506		 * conversion. For RAW formats, the sink and source media bus507		 * codes must match.508		 */509		format = mxc_isi_pipe_get_pad_format(pipe, state,510						     MXC_ISI_PIPE_PAD_SINK);511		info = mxc_isi_bus_format_by_code(format->code,512						  MXC_ISI_PIPE_PAD_SINK);513 514		if (info->encoding != MXC_ISI_ENC_RAW) {515			if (mf->code != MEDIA_BUS_FMT_YUV8_1X24 &&516			    mf->code != MEDIA_BUS_FMT_RGB888_1X24)517				mf->code = info->output;518 519			info = mxc_isi_bus_format_by_code(mf->code,520							  MXC_ISI_PIPE_PAD_SOURCE);521		}522 523		mf->code = info->output;524 525		/*526		 * The width and height on the source can't be changed, they527		 * must match the crop rectangle size.528		 */529		rect = mxc_isi_pipe_get_pad_crop(pipe, state,530						 MXC_ISI_PIPE_PAD_SOURCE);531 532		mf->width = rect->width;533		mf->height = rect->height;534	}535 536	format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad);537	*format = *mf;538 539	dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u",540		fmt->pad, mf->code, mf->width, mf->height);541 542	return 0;543}544 545static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd,546				      struct v4l2_subdev_state *state,547				      struct v4l2_subdev_selection *sel)548{549	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);550	const struct v4l2_mbus_framefmt *format;551	const struct v4l2_rect *rect;552 553	switch (sel->target) {554	case V4L2_SEL_TGT_COMPOSE_BOUNDS:555		if (sel->pad != MXC_ISI_PIPE_PAD_SINK)556			/* No compose rectangle on source pad. */557			return -EINVAL;558 559		/* The sink compose is bound by the sink format. */560		format = mxc_isi_pipe_get_pad_format(pipe, state,561						     MXC_ISI_PIPE_PAD_SINK);562		sel->r.left = 0;563		sel->r.top = 0;564		sel->r.width = format->width;565		sel->r.height = format->height;566		break;567 568	case V4L2_SEL_TGT_CROP_BOUNDS:569		if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)570			/* No crop rectangle on sink pad. */571			return -EINVAL;572 573		/* The source crop is bound by the sink compose. */574		rect = mxc_isi_pipe_get_pad_compose(pipe, state,575						    MXC_ISI_PIPE_PAD_SINK);576		sel->r = *rect;577		break;578 579	case V4L2_SEL_TGT_CROP:580		if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)581			/* No crop rectangle on sink pad. */582			return -EINVAL;583 584		rect = mxc_isi_pipe_get_pad_crop(pipe, state, sel->pad);585		sel->r = *rect;586		break;587 588	case V4L2_SEL_TGT_COMPOSE:589		if (sel->pad != MXC_ISI_PIPE_PAD_SINK)590			/* No compose rectangle on source pad. */591			return -EINVAL;592 593		rect = mxc_isi_pipe_get_pad_compose(pipe, state, sel->pad);594		sel->r = *rect;595		break;596 597	default:598		return -EINVAL;599	}600 601	return 0;602}603 604static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd,605				      struct v4l2_subdev_state *state,606				      struct v4l2_subdev_selection *sel)607{608	struct mxc_isi_pipe *pipe = to_isi_pipe(sd);609	struct v4l2_mbus_framefmt *format;610	struct v4l2_rect *rect;611 612	switch (sel->target) {613	case V4L2_SEL_TGT_CROP:614		if (sel->pad != MXC_ISI_PIPE_PAD_SOURCE)615			/* The pipeline support cropping on the source only. */616			return -EINVAL;617 618		/* The source crop is bound by the sink compose. */619		rect = mxc_isi_pipe_get_pad_compose(pipe, state,620						    MXC_ISI_PIPE_PAD_SINK);621		sel->r.left = clamp_t(s32, sel->r.left, 0, rect->width - 1);622		sel->r.top = clamp_t(s32, sel->r.top, 0, rect->height - 1);623		sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,624				     rect->width - sel->r.left);625		sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,626				      rect->height - sel->r.top);627 628		rect = mxc_isi_pipe_get_pad_crop(pipe, state,629						 MXC_ISI_PIPE_PAD_SOURCE);630		*rect = sel->r;631 632		/* Propagate the crop rectangle to the source pad. */633		format = mxc_isi_pipe_get_pad_format(pipe, state,634						     MXC_ISI_PIPE_PAD_SOURCE);635		format->width = sel->r.width;636		format->height = sel->r.height;637		break;638 639	case V4L2_SEL_TGT_COMPOSE:640		if (sel->pad != MXC_ISI_PIPE_PAD_SINK)641			/* Composing is supported on the sink only. */642			return -EINVAL;643 644		/* The sink crop is bound by the sink format downscaling only). */645		format = mxc_isi_pipe_get_pad_format(pipe, state,646						     MXC_ISI_PIPE_PAD_SINK);647 648		sel->r.left = 0;649		sel->r.top = 0;650		sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,651				     format->width);652		sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,653				      format->height);654 655		rect = mxc_isi_pipe_get_pad_compose(pipe, state,656						    MXC_ISI_PIPE_PAD_SINK);657		*rect = sel->r;658 659		/* Propagate the compose rectangle to the source pad. */660		rect = mxc_isi_pipe_get_pad_crop(pipe, state,661						 MXC_ISI_PIPE_PAD_SOURCE);662		rect->left = 0;663		rect->top = 0;664		rect->width = sel->r.width;665		rect->height = sel->r.height;666 667		format = mxc_isi_pipe_get_pad_format(pipe, state,668						     MXC_ISI_PIPE_PAD_SOURCE);669		format->width = sel->r.width;670		format->height = sel->r.height;671		break;672 673	default:674		return -EINVAL;675	}676 677	dev_dbg(pipe->isi->dev, "%s, target %#x: (%d,%d)/%dx%d", __func__,678		sel->target, sel->r.left, sel->r.top, sel->r.width,679		sel->r.height);680 681	return 0;682}683 684static const struct v4l2_subdev_pad_ops mxc_isi_pipe_subdev_pad_ops = {685	.enum_mbus_code = mxc_isi_pipe_enum_mbus_code,686	.get_fmt = v4l2_subdev_get_fmt,687	.set_fmt = mxc_isi_pipe_set_fmt,688	.get_selection = mxc_isi_pipe_get_selection,689	.set_selection = mxc_isi_pipe_set_selection,690};691 692static const struct v4l2_subdev_ops mxc_isi_pipe_subdev_ops = {693	.pad = &mxc_isi_pipe_subdev_pad_ops,694};695 696static const struct v4l2_subdev_internal_ops mxc_isi_pipe_internal_ops = {697	.init_state = mxc_isi_pipe_init_state,698};699 700/* -----------------------------------------------------------------------------701 * IRQ handling702 */703 704static irqreturn_t mxc_isi_pipe_irq_handler(int irq, void *priv)705{706	struct mxc_isi_pipe *pipe = priv;707	const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;708	u32 status;709 710	status = mxc_isi_channel_irq_status(pipe, true);711 712	if (status & CHNL_STS_FRM_STRD) {713		if (!WARN_ON(!pipe->irq_handler))714			pipe->irq_handler(pipe, status);715	}716 717	if (status & (CHNL_STS_AXI_WR_ERR_Y |718		      CHNL_STS_AXI_WR_ERR_U |719		      CHNL_STS_AXI_WR_ERR_V))720		dev_dbg(pipe->isi->dev, "%s: IRQ AXI Error stat=0x%X\n",721			__func__, status);722 723	if (status & (ier_reg->panic_y_buf_en.mask |724		      ier_reg->panic_u_buf_en.mask |725		      ier_reg->panic_v_buf_en.mask))726		dev_dbg(pipe->isi->dev, "%s: IRQ Panic OFLW Error stat=0x%X\n",727			__func__, status);728 729	if (status & (ier_reg->oflw_y_buf_en.mask |730		      ier_reg->oflw_u_buf_en.mask |731		      ier_reg->oflw_v_buf_en.mask))732		dev_dbg(pipe->isi->dev, "%s: IRQ OFLW Error stat=0x%X\n",733			__func__, status);734 735	if (status & (ier_reg->excs_oflw_y_buf_en.mask |736		      ier_reg->excs_oflw_u_buf_en.mask |737		      ier_reg->excs_oflw_v_buf_en.mask))738		dev_dbg(pipe->isi->dev, "%s: IRQ EXCS OFLW Error stat=0x%X\n",739			__func__, status);740 741	return IRQ_HANDLED;742}743 744/* -----------------------------------------------------------------------------745 * Init & cleanup746 */747 748static const struct media_entity_operations mxc_isi_pipe_entity_ops = {749	.link_validate	= v4l2_subdev_link_validate,750};751 752int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)753{754	struct mxc_isi_pipe *pipe = &isi->pipes[id];755	struct v4l2_subdev *sd;756	int irq;757	int ret;758 759	pipe->id = id;760	pipe->isi = isi;761	pipe->regs = isi->regs + id * isi->pdata->reg_offset;762 763	mutex_init(&pipe->lock);764 765	pipe->available_res = MXC_ISI_CHANNEL_RES_LINE_BUF766			    | MXC_ISI_CHANNEL_RES_OUTPUT_BUF;767	pipe->acquired_res = 0;768	pipe->chained_res = 0;769	pipe->chained = false;770 771	sd = &pipe->sd;772	v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops);773	sd->internal_ops = &mxc_isi_pipe_internal_ops;774	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;775	snprintf(sd->name, sizeof(sd->name), "mxc_isi.%d", pipe->id);776	sd->dev = isi->dev;777 778	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;779	sd->entity.ops = &mxc_isi_pipe_entity_ops;780 781	pipe->pads[MXC_ISI_PIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;782	pipe->pads[MXC_ISI_PIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;783 784	ret = media_entity_pads_init(&sd->entity, MXC_ISI_PIPE_PADS_NUM,785				     pipe->pads);786	if (ret)787		goto error;788 789	ret = v4l2_subdev_init_finalize(sd);790	if (ret < 0)791		goto error;792 793	/* Register IRQ handler. */794	mxc_isi_channel_irq_clear(pipe);795 796	irq = platform_get_irq(to_platform_device(isi->dev), id);797	if (irq < 0) {798		ret = irq;799		goto error;800	}801 802	ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,803			       0, dev_name(isi->dev), pipe);804	if (ret < 0) {805		dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);806		goto error;807	}808 809	return 0;810 811error:812	media_entity_cleanup(&sd->entity);813	mutex_destroy(&pipe->lock);814 815	return ret;816}817 818void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe)819{820	struct v4l2_subdev *sd = &pipe->sd;821 822	media_entity_cleanup(&sd->entity);823	mutex_destroy(&pipe->lock);824}825 826int mxc_isi_pipe_acquire(struct mxc_isi_pipe *pipe,827			 mxc_isi_pipe_irq_t irq_handler)828{829	const struct mxc_isi_bus_format_info *sink_info;830	const struct mxc_isi_bus_format_info *src_info;831	struct v4l2_mbus_framefmt *sink_fmt;832	const struct v4l2_mbus_framefmt *src_fmt;833	struct v4l2_subdev *sd = &pipe->sd;834	struct v4l2_subdev_state *state;835	bool bypass;836	int ret;837 838	state = v4l2_subdev_lock_and_get_active_state(sd);839	sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);840	src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);841	v4l2_subdev_unlock_state(state);842 843	sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,844					       MXC_ISI_PIPE_PAD_SINK);845	src_info = mxc_isi_bus_format_by_code(src_fmt->code,846					      MXC_ISI_PIPE_PAD_SOURCE);847 848	bypass = sink_fmt->width == src_fmt->width &&849		 sink_fmt->height == src_fmt->height &&850		 sink_info->encoding == src_info->encoding;851 852	ret = mxc_isi_channel_acquire(pipe, irq_handler, bypass);853	if (ret)854		return ret;855 856	/* Chain the channel if needed for wide resolutions. */857	if (sink_fmt->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {858		ret = mxc_isi_channel_chain(pipe, bypass);859		if (ret)860			mxc_isi_channel_release(pipe);861	}862 863	return ret;864}865 866void mxc_isi_pipe_release(struct mxc_isi_pipe *pipe)867{868	mxc_isi_channel_release(pipe);869	mxc_isi_channel_unchain(pipe);870}871