brintos

brintos / linux-shallow public Read only

0
0
Text · 16.7 KiB · 5623914 Raw
650 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2019-2020 NXP4 */5 6#include <linux/delay.h>7#include <linux/device.h>8#include <linux/io.h>9#include <linux/types.h>10 11#include "imx8-isi-core.h"12#include "imx8-isi-regs.h"13 14#define	ISI_DOWNSCALE_THRESHOLD		0x400015 16static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg)17{18	return readl(pipe->regs + reg);19}20 21static inline void mxc_isi_write(struct mxc_isi_pipe *pipe, u32 reg, u32 val)22{23	writel(val, pipe->regs + reg);24}25 26/* -----------------------------------------------------------------------------27 * Buffers & M2M operation28 */29 30void mxc_isi_channel_set_inbuf(struct mxc_isi_pipe *pipe, dma_addr_t dma_addr)31{32	mxc_isi_write(pipe, CHNL_IN_BUF_ADDR, lower_32_bits(dma_addr));33	if (pipe->isi->pdata->has_36bit_dma)34		mxc_isi_write(pipe, CHNL_IN_BUF_XTND_ADDR,35			      upper_32_bits(dma_addr));36}37 38void mxc_isi_channel_set_outbuf(struct mxc_isi_pipe *pipe,39				const dma_addr_t dma_addrs[3],40				enum mxc_isi_buf_id buf_id)41{42	int val;43 44	val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);45 46	if (buf_id == MXC_ISI_BUF1) {47		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_Y,48			      lower_32_bits(dma_addrs[0]));49		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_U,50			      lower_32_bits(dma_addrs[1]));51		mxc_isi_write(pipe, CHNL_OUT_BUF1_ADDR_V,52			      lower_32_bits(dma_addrs[2]));53		if (pipe->isi->pdata->has_36bit_dma) {54			mxc_isi_write(pipe, CHNL_Y_BUF1_XTND_ADDR,55				      upper_32_bits(dma_addrs[0]));56			mxc_isi_write(pipe, CHNL_U_BUF1_XTND_ADDR,57				      upper_32_bits(dma_addrs[1]));58			mxc_isi_write(pipe, CHNL_V_BUF1_XTND_ADDR,59				      upper_32_bits(dma_addrs[2]));60		}61		val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR;62	} else  {63		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_Y,64			      lower_32_bits(dma_addrs[0]));65		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_U,66			      lower_32_bits(dma_addrs[1]));67		mxc_isi_write(pipe, CHNL_OUT_BUF2_ADDR_V,68			      lower_32_bits(dma_addrs[2]));69		if (pipe->isi->pdata->has_36bit_dma) {70			mxc_isi_write(pipe, CHNL_Y_BUF2_XTND_ADDR,71				      upper_32_bits(dma_addrs[0]));72			mxc_isi_write(pipe, CHNL_U_BUF2_XTND_ADDR,73				      upper_32_bits(dma_addrs[1]));74			mxc_isi_write(pipe, CHNL_V_BUF2_XTND_ADDR,75				      upper_32_bits(dma_addrs[2]));76		}77		val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR;78	}79 80	mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);81}82 83void mxc_isi_channel_m2m_start(struct mxc_isi_pipe *pipe)84{85	u32 val;86 87	val = mxc_isi_read(pipe, CHNL_MEM_RD_CTRL);88	val &= ~CHNL_MEM_RD_CTRL_READ_MEM;89	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val);90 91	fsleep(300);92 93	val |= CHNL_MEM_RD_CTRL_READ_MEM;94	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL, val);95}96 97/* -----------------------------------------------------------------------------98 * Pipeline configuration99 */100 101static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to,102					 u32 *dec)103{104	unsigned int ratio = from / to;105 106	if (ratio < 2)107		*dec = 1;108	else if (ratio < 4)109		*dec = 2;110	else if (ratio < 8)111		*dec = 4;112	else113		*dec = 8;114 115	return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);116}117 118static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe,119					enum mxc_isi_encoding encoding,120					const struct v4l2_area *in_size,121					const struct v4l2_area *out_size,122					bool *bypass)123{124	u32 xscale, yscale;125	u32 decx, decy;126	u32 val;127 128	dev_dbg(pipe->isi->dev, "input %ux%u, output %ux%u\n",129		in_size->width, in_size->height,130		out_size->width, out_size->height);131 132	xscale = mxc_isi_channel_scaling_ratio(in_size->width, out_size->width,133					       &decx);134	yscale = mxc_isi_channel_scaling_ratio(in_size->height, out_size->height,135					       &decy);136 137	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);138	val &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK |139		 CHNL_IMG_CTRL_YCBCR_MODE);140 141	val |= CHNL_IMG_CTRL_DEC_X(ilog2(decx))142	    |  CHNL_IMG_CTRL_DEC_Y(ilog2(decy));143 144	/*145	 * Contrary to what the documentation states, YCBCR_MODE does not146	 * control conversion between YCbCr and RGB, but whether the scaler147	 * operates in YUV mode or in RGB mode. It must be set when the scaler148	 * input is YUV.149	 */150	if (encoding == MXC_ISI_ENC_YUV)151		val |= CHNL_IMG_CTRL_YCBCR_MODE;152 153	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);154 155	mxc_isi_write(pipe, CHNL_SCALE_FACTOR,156		      CHNL_SCALE_FACTOR_Y_SCALE(yscale) |157		      CHNL_SCALE_FACTOR_X_SCALE(xscale));158 159	mxc_isi_write(pipe, CHNL_SCALE_OFFSET, 0);160 161	mxc_isi_write(pipe, CHNL_SCL_IMG_CFG,162		      CHNL_SCL_IMG_CFG_HEIGHT(out_size->height) |163		      CHNL_SCL_IMG_CFG_WIDTH(out_size->width));164 165	*bypass = in_size->height == out_size->height &&166		  in_size->width == out_size->width;167}168 169static void mxc_isi_channel_set_crop(struct mxc_isi_pipe *pipe,170				     const struct v4l2_area *src,171				     const struct v4l2_rect *dst)172{173	u32 val, val0, val1;174 175	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);176	val &= ~CHNL_IMG_CTRL_CROP_EN;177 178	if (src->height == dst->height && src->width == dst->width) {179		mxc_isi_write(pipe, CHNL_IMG_CTRL, val);180		return;181	}182 183	val |= CHNL_IMG_CTRL_CROP_EN;184	val0 = CHNL_CROP_ULC_X(dst->left) | CHNL_CROP_ULC_Y(dst->top);185	val1 = CHNL_CROP_LRC_X(dst->width) | CHNL_CROP_LRC_Y(dst->height);186 187	mxc_isi_write(pipe, CHNL_CROP_ULC, val0);188	mxc_isi_write(pipe, CHNL_CROP_LRC, val1 + val0);189	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);190}191 192/*193 * A2,A1,      B1, A3,     B3, B2,194 * C2, C1,     D1, C3,     D3, D2195 */196static const u32 mxc_isi_yuv2rgb_coeffs[6] = {197	/* YUV -> RGB */198	0x0000012a, 0x012a0198, 0x0730079c,199	0x0204012a, 0x01f00000, 0x01800180200};201 202static const u32 mxc_isi_rgb2yuv_coeffs[6] = {203	/* RGB->YUV */204	0x00810041, 0x07db0019, 0x007007b6,205	0x07a20070, 0x001007ee, 0x00800080206};207 208static void mxc_isi_channel_set_csc(struct mxc_isi_pipe *pipe,209				    enum mxc_isi_encoding in_encoding,210				    enum mxc_isi_encoding out_encoding,211				    bool *bypass)212{213	static const char * const encodings[] = {214		[MXC_ISI_ENC_RAW] = "RAW",215		[MXC_ISI_ENC_RGB] = "RGB",216		[MXC_ISI_ENC_YUV] = "YUV",217	};218	const u32 *coeffs = NULL;219	u32 val;220 221	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);222	val &= ~(CHNL_IMG_CTRL_CSC_BYPASS | CHNL_IMG_CTRL_CSC_MODE_MASK);223 224	if (in_encoding == MXC_ISI_ENC_YUV &&225	    out_encoding == MXC_ISI_ENC_RGB) {226		/* YUV2RGB */227		coeffs = mxc_isi_yuv2rgb_coeffs;228		/* YCbCr enable???  */229		val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB);230	} else if (in_encoding == MXC_ISI_ENC_RGB &&231		   out_encoding == MXC_ISI_ENC_YUV) {232		/* RGB2YUV */233		coeffs = mxc_isi_rgb2yuv_coeffs;234		val |= CHNL_IMG_CTRL_CSC_MODE(CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR);235	} else {236		/* Bypass CSC */237		val |= CHNL_IMG_CTRL_CSC_BYPASS;238	}239 240	dev_dbg(pipe->isi->dev, "CSC: %s -> %s\n",241		encodings[in_encoding], encodings[out_encoding]);242 243	if (coeffs) {244		mxc_isi_write(pipe, CHNL_CSC_COEFF0, coeffs[0]);245		mxc_isi_write(pipe, CHNL_CSC_COEFF1, coeffs[1]);246		mxc_isi_write(pipe, CHNL_CSC_COEFF2, coeffs[2]);247		mxc_isi_write(pipe, CHNL_CSC_COEFF3, coeffs[3]);248		mxc_isi_write(pipe, CHNL_CSC_COEFF4, coeffs[4]);249		mxc_isi_write(pipe, CHNL_CSC_COEFF5, coeffs[5]);250	}251 252	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);253 254	*bypass = !coeffs;255}256 257void mxc_isi_channel_set_alpha(struct mxc_isi_pipe *pipe, u8 alpha)258{259	u32 val;260 261	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);262	val &= ~CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK;263	val |= CHNL_IMG_CTRL_GBL_ALPHA_VAL(alpha) |264	       CHNL_IMG_CTRL_GBL_ALPHA_EN;265	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);266}267 268void mxc_isi_channel_set_flip(struct mxc_isi_pipe *pipe, bool hflip, bool vflip)269{270	u32 val;271 272	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);273	val &= ~(CHNL_IMG_CTRL_VFLIP_EN | CHNL_IMG_CTRL_HFLIP_EN);274 275	if (vflip)276		val |= CHNL_IMG_CTRL_VFLIP_EN;277	if (hflip)278		val |= CHNL_IMG_CTRL_HFLIP_EN;279 280	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);281}282 283static void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe)284{285	const struct mxc_isi_set_thd *set_thd = pipe->isi->pdata->set_thd;286	u32 val;287 288	val = mxc_isi_read(pipe, CHNL_OUT_BUF_CTRL);289 290	val &= ~(set_thd->panic_set_thd_y.mask);291	val |= set_thd->panic_set_thd_y.threshold << set_thd->panic_set_thd_y.offset;292 293	val &= ~(set_thd->panic_set_thd_u.mask);294	val |= set_thd->panic_set_thd_u.threshold << set_thd->panic_set_thd_u.offset;295 296	val &= ~(set_thd->panic_set_thd_v.mask);297	val |= set_thd->panic_set_thd_v.threshold << set_thd->panic_set_thd_v.offset;298 299	mxc_isi_write(pipe, CHNL_OUT_BUF_CTRL, val);300}301 302static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,303					enum mxc_isi_input_id input,304					bool bypass)305{306	u32 val;307 308	mutex_lock(&pipe->lock);309 310	val = mxc_isi_read(pipe, CHNL_CTRL);311	val &= ~(CHNL_CTRL_CHNL_BYPASS | CHNL_CTRL_CHAIN_BUF_MASK |312		 CHNL_CTRL_BLANK_PXL_MASK | CHNL_CTRL_SRC_TYPE_MASK |313		 CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK);314 315	/*316	 * If no scaling or color space conversion is needed, bypass the317	 * channel.318	 */319	if (bypass)320		val |= CHNL_CTRL_CHNL_BYPASS;321 322	/* Chain line buffers if needed. */323	if (pipe->chained)324		val |= CHNL_CTRL_CHAIN_BUF(CHNL_CTRL_CHAIN_BUF_2_CHAIN);325 326	val |= CHNL_CTRL_BLANK_PXL(0xff);327 328	/* Input source (including VC configuration for CSI-2) */329	if (input == MXC_ISI_INPUT_MEM) {330		/*331		 * The memory input is connected to the last port of the332		 * crossbar switch, after all pixel link inputs. The SRC_INPUT333		 * field controls the input selection and must be set334		 * accordingly, despite being documented as ignored when using335		 * the memory input in the i.MX8MP reference manual, and336		 * reserved in the i.MX8MN reference manual.337		 */338		val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_MEMORY);339		val |= CHNL_CTRL_SRC_INPUT(pipe->isi->pdata->num_ports);340	} else {341		val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_DEVICE);342		val |= CHNL_CTRL_SRC_INPUT(input);343		val |= CHNL_CTRL_MIPI_VC_ID(0); /* FIXME: For CSI-2 only */344	}345 346	mxc_isi_write(pipe, CHNL_CTRL, val);347 348	mutex_unlock(&pipe->lock);349}350 351void mxc_isi_channel_config(struct mxc_isi_pipe *pipe,352			    enum mxc_isi_input_id input,353			    const struct v4l2_area *in_size,354			    const struct v4l2_area *scale,355			    const struct v4l2_rect *crop,356			    enum mxc_isi_encoding in_encoding,357			    enum mxc_isi_encoding out_encoding)358{359	bool csc_bypass;360	bool scaler_bypass;361 362	/* Input frame size */363	mxc_isi_write(pipe, CHNL_IMG_CFG,364		      CHNL_IMG_CFG_HEIGHT(in_size->height) |365		      CHNL_IMG_CFG_WIDTH(in_size->width));366 367	/* Scaling */368	mxc_isi_channel_set_scaling(pipe, in_encoding, in_size, scale,369				    &scaler_bypass);370	mxc_isi_channel_set_crop(pipe, scale, crop);371 372	/* CSC */373	mxc_isi_channel_set_csc(pipe, in_encoding, out_encoding, &csc_bypass);374 375	/* Output buffer management */376	mxc_isi_channel_set_panic_threshold(pipe);377 378	/* Channel control */379	mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass);380}381 382void mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe,383				      const struct mxc_isi_format_info *info,384				      const struct v4l2_pix_format_mplane *format)385{386	unsigned int bpl = format->plane_fmt[0].bytesperline;387 388	mxc_isi_write(pipe, CHNL_MEM_RD_CTRL,389		      CHNL_MEM_RD_CTRL_IMG_TYPE(info->isi_in_format));390	mxc_isi_write(pipe, CHNL_IN_BUF_PITCH,391		      CHNL_IN_BUF_PITCH_LINE_PITCH(bpl));392}393 394void mxc_isi_channel_set_output_format(struct mxc_isi_pipe *pipe,395				       const struct mxc_isi_format_info *info,396				       struct v4l2_pix_format_mplane *format)397{398	u32 val;399 400	/* set outbuf format */401	dev_dbg(pipe->isi->dev, "output format %p4cc", &format->pixelformat);402 403	val = mxc_isi_read(pipe, CHNL_IMG_CTRL);404	val &= ~CHNL_IMG_CTRL_FORMAT_MASK;405	val |= CHNL_IMG_CTRL_FORMAT(info->isi_out_format);406	mxc_isi_write(pipe, CHNL_IMG_CTRL, val);407 408	/* line pitch */409	mxc_isi_write(pipe, CHNL_OUT_BUF_PITCH,410		      format->plane_fmt[0].bytesperline);411}412 413/* -----------------------------------------------------------------------------414 * IRQ415 */416 417u32 mxc_isi_channel_irq_status(struct mxc_isi_pipe *pipe, bool clear)418{419	u32 status;420 421	status = mxc_isi_read(pipe, CHNL_STS);422	if (clear)423		mxc_isi_write(pipe, CHNL_STS, status);424 425	return status;426}427 428void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe)429{430	mxc_isi_write(pipe, CHNL_STS, 0xffffffff);431}432 433static void mxc_isi_channel_irq_enable(struct mxc_isi_pipe *pipe)434{435	const struct mxc_isi_ier_reg *ier_reg = pipe->isi->pdata->ier_reg;436	u32 val;437 438	val = CHNL_IER_FRM_RCVD_EN |439		CHNL_IER_AXI_WR_ERR_U_EN |440		CHNL_IER_AXI_WR_ERR_V_EN |441		CHNL_IER_AXI_WR_ERR_Y_EN;442 443	/* Y/U/V overflow enable */444	val |= ier_reg->oflw_y_buf_en.mask |445	       ier_reg->oflw_u_buf_en.mask |446	       ier_reg->oflw_v_buf_en.mask;447 448	/* Y/U/V excess overflow enable */449	val |= ier_reg->excs_oflw_y_buf_en.mask |450	       ier_reg->excs_oflw_u_buf_en.mask |451	       ier_reg->excs_oflw_v_buf_en.mask;452 453	/* Y/U/V panic enable */454	val |= ier_reg->panic_y_buf_en.mask |455	       ier_reg->panic_u_buf_en.mask |456	       ier_reg->panic_v_buf_en.mask;457 458	mxc_isi_channel_irq_clear(pipe);459	mxc_isi_write(pipe, CHNL_IER, val);460}461 462static void mxc_isi_channel_irq_disable(struct mxc_isi_pipe *pipe)463{464	mxc_isi_write(pipe, CHNL_IER, 0);465}466 467/* -----------------------------------------------------------------------------468 * Init, deinit, enable, disable469 */470 471static void mxc_isi_channel_sw_reset(struct mxc_isi_pipe *pipe, bool enable_clk)472{473	mxc_isi_write(pipe, CHNL_CTRL, CHNL_CTRL_SW_RST);474	mdelay(5);475	mxc_isi_write(pipe, CHNL_CTRL, enable_clk ? CHNL_CTRL_CLK_EN : 0);476}477 478static void __mxc_isi_channel_get(struct mxc_isi_pipe *pipe)479{480	if (!pipe->use_count++)481		mxc_isi_channel_sw_reset(pipe, true);482}483 484void mxc_isi_channel_get(struct mxc_isi_pipe *pipe)485{486	mutex_lock(&pipe->lock);487	__mxc_isi_channel_get(pipe);488	mutex_unlock(&pipe->lock);489}490 491static void __mxc_isi_channel_put(struct mxc_isi_pipe *pipe)492{493	if (!--pipe->use_count)494		mxc_isi_channel_sw_reset(pipe, false);495}496 497void mxc_isi_channel_put(struct mxc_isi_pipe *pipe)498{499	mutex_lock(&pipe->lock);500	__mxc_isi_channel_put(pipe);501	mutex_unlock(&pipe->lock);502}503 504void mxc_isi_channel_enable(struct mxc_isi_pipe *pipe)505{506	u32 val;507 508	mxc_isi_channel_irq_enable(pipe);509 510	mutex_lock(&pipe->lock);511 512	val = mxc_isi_read(pipe, CHNL_CTRL);513	val |= CHNL_CTRL_CHNL_EN;514	mxc_isi_write(pipe, CHNL_CTRL, val);515 516	mutex_unlock(&pipe->lock);517}518 519void mxc_isi_channel_disable(struct mxc_isi_pipe *pipe)520{521	u32 val;522 523	mxc_isi_channel_irq_disable(pipe);524 525	mutex_lock(&pipe->lock);526 527	val = mxc_isi_read(pipe, CHNL_CTRL);528	val &= ~CHNL_CTRL_CHNL_EN;529	mxc_isi_write(pipe, CHNL_CTRL, val);530 531	mutex_unlock(&pipe->lock);532}533 534/* -----------------------------------------------------------------------------535 * Resource management & chaining536 */537int mxc_isi_channel_acquire(struct mxc_isi_pipe *pipe,538			    mxc_isi_pipe_irq_t irq_handler, bool bypass)539{540	u8 resources;541	int ret = 0;542 543	mutex_lock(&pipe->lock);544 545	if (pipe->irq_handler) {546		ret = -EBUSY;547		goto unlock;548	}549 550	/*551	 * Make sure the resources we need are available. The output buffer is552	 * always needed to operate the channel, the line buffer is needed only553	 * when the channel isn't in bypass mode.554	 */555	resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF556		  | (!bypass ? MXC_ISI_CHANNEL_RES_LINE_BUF : 0);557	if ((pipe->available_res & resources) != resources) {558		ret = -EBUSY;559		goto unlock;560	}561 562	/* Acquire the channel resources. */563	pipe->acquired_res = resources;564	pipe->available_res &= ~resources;565	pipe->irq_handler = irq_handler;566 567unlock:568	mutex_unlock(&pipe->lock);569 570	return ret;571}572 573void mxc_isi_channel_release(struct mxc_isi_pipe *pipe)574{575	mutex_lock(&pipe->lock);576 577	pipe->irq_handler = NULL;578	pipe->available_res |= pipe->acquired_res;579	pipe->acquired_res = 0;580 581	mutex_unlock(&pipe->lock);582}583 584/*585 * We currently support line buffer chaining only, for handling images with a586 * width larger than 2048 pixels.587 *588 * TODO: Support secondary line buffer for downscaling YUV420 images.589 */590int mxc_isi_channel_chain(struct mxc_isi_pipe *pipe, bool bypass)591{592	/* Channel chaining requires both line and output buffer. */593	const u8 resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF594			   | MXC_ISI_CHANNEL_RES_LINE_BUF;595	struct mxc_isi_pipe *chained_pipe = pipe + 1;596	int ret = 0;597 598	/*599	 * If buffer chaining is required, make sure this channel is not the600	 * last one, otherwise there's no 'next' channel to chain with. This601	 * should be prevented by checks in the set format handlers, but let's602	 * be defensive.603	 */604	if (WARN_ON(pipe->id == pipe->isi->pdata->num_channels - 1))605		return -EINVAL;606 607	mutex_lock(&chained_pipe->lock);608 609	/* Safety checks. */610	if (WARN_ON(pipe->chained || chained_pipe->chained_res)) {611		ret = -EINVAL;612		goto unlock;613	}614 615	if ((chained_pipe->available_res & resources) != resources) {616		ret = -EBUSY;617		goto unlock;618	}619 620	pipe->chained = true;621	chained_pipe->chained_res |= resources;622	chained_pipe->available_res &= ~resources;623 624	__mxc_isi_channel_get(chained_pipe);625 626unlock:627	mutex_unlock(&chained_pipe->lock);628 629	return ret;630}631 632void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe)633{634	struct mxc_isi_pipe *chained_pipe = pipe + 1;635 636	if (!pipe->chained)637		return;638 639	pipe->chained = false;640 641	mutex_lock(&chained_pipe->lock);642 643	chained_pipe->available_res |= chained_pipe->chained_res;644	chained_pipe->chained_res = 0;645 646	__mxc_isi_channel_put(chained_pipe);647 648	mutex_unlock(&chained_pipe->lock);649}650