xref: /linux/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019-2020 NXP
4  */
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		0x4000
15 
mxc_isi_read(struct mxc_isi_pipe * pipe,u32 reg)16 static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg)
17 {
18 	return readl(pipe->regs + reg);
19 }
20 
mxc_isi_write(struct mxc_isi_pipe * pipe,u32 reg,u32 val)21 static 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 operation
28  */
29 
mxc_isi_channel_set_inbuf(struct mxc_isi_pipe * pipe,dma_addr_t dma_addr)30 void 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 
mxc_isi_channel_set_outbuf(struct mxc_isi_pipe * pipe,const dma_addr_t dma_addrs[3],enum mxc_isi_buf_id buf_id)38 void 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 
mxc_isi_channel_m2m_start(struct mxc_isi_pipe * pipe)83 void 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 configuration
99  */
100 
mxc_isi_channel_scaling_ratio(unsigned int from,unsigned int to,u32 * dec)101 static 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 	else
113 		*dec = 8;
114 
115 	return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);
116 }
117 
mxc_isi_channel_set_scaling(struct mxc_isi_pipe * pipe,enum mxc_isi_encoding encoding,const struct v4l2_area * in_size,const struct v4l2_area * out_size,bool * bypass)118 static 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 not
146 	 * control conversion between YCbCr and RGB, but whether the scaler
147 	 * operates in YUV mode or in RGB mode. It must be set when the scaler
148 	 * 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 
mxc_isi_channel_set_crop(struct mxc_isi_pipe * pipe,const struct v4l2_area * src,const struct v4l2_rect * dst)169 static 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, D2
195  */
196 static const u32 mxc_isi_yuv2rgb_coeffs[6] = {
197 	/* YUV -> RGB */
198 	0x0000012a, 0x012a0198, 0x0730079c,
199 	0x0204012a, 0x01f00000, 0x01800180
200 };
201 
202 static const u32 mxc_isi_rgb2yuv_coeffs[6] = {
203 	/* RGB->YUV */
204 	0x00810041, 0x07db0019, 0x007007b6,
205 	0x07a20070, 0x001007ee, 0x00800080
206 };
207 
mxc_isi_channel_set_csc(struct mxc_isi_pipe * pipe,enum mxc_isi_encoding in_encoding,enum mxc_isi_encoding out_encoding,bool * bypass)208 static 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 
mxc_isi_channel_set_alpha(struct mxc_isi_pipe * pipe,u8 alpha)257 void 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 
mxc_isi_channel_set_flip(struct mxc_isi_pipe * pipe,bool hflip,bool vflip)268 void 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 
mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe * pipe)283 static 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 
mxc_isi_channel_set_control(struct mxc_isi_pipe * pipe,enum mxc_isi_input_id input,bool bypass)302 static 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 the
317 	 * 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 the
332 		 * crossbar switch, after all pixel link inputs. The SRC_INPUT
333 		 * field controls the input selection and must be set
334 		 * accordingly, despite being documented as ignored when using
335 		 * the memory input in the i.MX8MP reference manual, and
336 		 * 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 
mxc_isi_channel_config(struct mxc_isi_pipe * pipe,enum mxc_isi_input_id input,const struct v4l2_area * in_size,const struct v4l2_area * scale,const struct v4l2_rect * crop,enum mxc_isi_encoding in_encoding,enum mxc_isi_encoding out_encoding)351 void 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 
mxc_isi_channel_set_input_format(struct mxc_isi_pipe * pipe,const struct mxc_isi_format_info * info,const struct v4l2_pix_format_mplane * format)382 void 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 
mxc_isi_channel_set_output_format(struct mxc_isi_pipe * pipe,const struct mxc_isi_format_info * info,struct v4l2_pix_format_mplane * format)394 void 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  * IRQ
415  */
416 
mxc_isi_channel_irq_status(struct mxc_isi_pipe * pipe,bool clear)417 u32 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 
mxc_isi_channel_irq_clear(struct mxc_isi_pipe * pipe)428 void mxc_isi_channel_irq_clear(struct mxc_isi_pipe *pipe)
429 {
430 	mxc_isi_write(pipe, CHNL_STS, 0xffffffff);
431 }
432 
mxc_isi_channel_irq_enable(struct mxc_isi_pipe * pipe)433 static 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 
mxc_isi_channel_irq_disable(struct mxc_isi_pipe * pipe)462 static 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, disable
469  */
470 
mxc_isi_channel_sw_reset(struct mxc_isi_pipe * pipe,bool enable_clk)471 static 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 
__mxc_isi_channel_get(struct mxc_isi_pipe * pipe)478 static 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 
mxc_isi_channel_get(struct mxc_isi_pipe * pipe)484 void 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 
__mxc_isi_channel_put(struct mxc_isi_pipe * pipe)491 static 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 
mxc_isi_channel_put(struct mxc_isi_pipe * pipe)497 void 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 
mxc_isi_channel_enable(struct mxc_isi_pipe * pipe)504 void 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 
mxc_isi_channel_disable(struct mxc_isi_pipe * pipe)519 void 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 & chaining
536  */
mxc_isi_channel_acquire(struct mxc_isi_pipe * pipe,mxc_isi_pipe_irq_t irq_handler,bool bypass)537 int 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 is
552 	 * always needed to operate the channel, the line buffer is needed only
553 	 * when the channel isn't in bypass mode.
554 	 */
555 	resources = MXC_ISI_CHANNEL_RES_OUTPUT_BUF
556 		  | (!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 
567 unlock:
568 	mutex_unlock(&pipe->lock);
569 
570 	return ret;
571 }
572 
mxc_isi_channel_release(struct mxc_isi_pipe * pipe)573 void 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 a
586  * width larger than 2048 pixels.
587  *
588  * TODO: Support secondary line buffer for downscaling YUV420 images.
589  */
mxc_isi_channel_chain(struct mxc_isi_pipe * pipe,bool bypass)590 int 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_BUF
594 			   | 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 the
600 	 * last one, otherwise there's no 'next' channel to chain with. This
601 	 * should be prevented by checks in the set format handlers, but let's
602 	 * 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 
626 unlock:
627 	mutex_unlock(&chained_pipe->lock);
628 
629 	return ret;
630 }
631 
mxc_isi_channel_unchain(struct mxc_isi_pipe * pipe)632 void 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