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