1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) STMicroelectronics SA 2014
4 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
5 */
6
7#include <linux/delay.h>
8
9#include "bdisp.h"
10#include "bdisp-filter.h"
11#include "bdisp-reg.h"
12
13/* Max width of the source frame in a single node */
14#define MAX_SRC_WIDTH           2048
15
16/* Reset & boot poll config */
17#define POLL_RST_MAX            500
18#define POLL_RST_DELAY_MS       2
19
20enum bdisp_target_plan {
21	BDISP_RGB,
22	BDISP_Y,
23	BDISP_CBCR
24};
25
26struct bdisp_op_cfg {
27	bool cconv;          /* RGB - YUV conversion */
28	bool hflip;          /* Horizontal flip */
29	bool vflip;          /* Vertical flip */
30	bool wide;           /* Wide (>MAX_SRC_WIDTH) */
31	bool scale;          /* Scale */
32	u16  h_inc;          /* Horizontal increment in 6.10 format */
33	u16  v_inc;          /* Vertical increment in 6.10 format */
34	bool src_interlaced; /* is the src an interlaced buffer */
35	u8   src_nbp;        /* nb of planes of the src */
36	bool src_yuv;        /* is the src a YUV color format */
37	bool src_420;        /* is the src 4:2:0 chroma subsampled */
38	u8   dst_nbp;        /* nb of planes of the dst */
39	bool dst_yuv;        /* is the dst a YUV color format */
40	bool dst_420;        /* is the dst 4:2:0 chroma subsampled */
41};
42
43struct bdisp_filter_addr {
44	u16 min;             /* Filter min scale factor (6.10 fixed point) */
45	u16 max;             /* Filter max scale factor (6.10 fixed point) */
46	void *virt;          /* Virtual address for filter table */
47	dma_addr_t paddr;    /* Physical address for filter table */
48};
49
50static const struct bdisp_filter_h_spec bdisp_h_spec[] = {
51	{
52		.min = 0,
53		.max = 921,
54		.coef = {
55			0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
56			0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00,
57			0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00,
58			0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00,
59			0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00,
60			0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00,
61			0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00,
62			0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00
63		}
64	},
65	{
66		.min = 921,
67		.max = 1024,
68		.coef = {
69			0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
70			0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
71			0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
72			0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
73			0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
74			0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
75			0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
76			0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
77		}
78	},
79	{
80		.min = 1024,
81		.max = 1126,
82		.coef = {
83			0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
84			0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
85			0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
86			0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
87			0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
88			0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
89			0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
90			0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
91		}
92	},
93	{
94		.min = 1126,
95		.max = 1228,
96		.coef = {
97			0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
98			0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
99			0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
100			0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
101			0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
102			0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
103			0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
104			0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
105		}
106	},
107	{
108		.min = 1228,
109		.max = 1331,
110		.coef = {
111			0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04,
112			0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02,
113			0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00,
114			0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff,
115			0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd,
116			0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc,
117			0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb,
118			0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc
119		}
120	},
121	{
122		.min = 1331,
123		.max = 1433,
124		.coef = {
125			0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06,
126			0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05,
127			0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04,
128			0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02,
129			0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00,
130			0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff,
131			0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe,
132			0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd
133		}
134	},
135	{
136		.min = 1433,
137		.max = 1536,
138		.coef = {
139			0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06,
140			0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06,
141			0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06,
142			0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04,
143			0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03,
144			0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01,
145			0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00,
146			0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff
147		}
148	},
149	{
150		.min = 1536,
151		.max = 2048,
152		.coef = {
153			0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd,
154			0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff,
155			0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00,
156			0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01,
157			0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02,
158			0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03,
159			0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04,
160			0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05
161		}
162	},
163	{
164		.min = 2048,
165		.max = 3072,
166		.coef = {
167			0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd,
168			0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc,
169			0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc,
170			0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb,
171			0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb,
172			0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb,
173			0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb,
174			0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc
175		}
176	},
177	{
178		.min = 3072,
179		.max = 4096,
180		.coef = {
181			0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02,
182			0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01,
183			0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00,
184			0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00,
185			0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00,
186			0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00,
187			0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff,
188			0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff
189		}
190	},
191	{
192		.min = 4096,
193		.max = 5120,
194		.coef = {
195			0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04,
196			0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04,
197			0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03,
198			0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03,
199			0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02,
200			0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02,
201			0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01,
202			0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01
203		}
204	},
205	{
206		.min = 5120,
207		.max = 65535,
208		.coef = {
209			0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06,
210			0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
211			0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
212			0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04,
213			0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04,
214			0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04,
215			0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03,
216			0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03
217		}
218	}
219};
220
221#define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec)
222
223
224static const struct bdisp_filter_v_spec bdisp_v_spec[] = {
225	{
226		.min = 0,
227		.max = 1024,
228		.coef = {
229			0x00, 0x00, 0x40, 0x00, 0x00,
230			0x00, 0x06, 0x3d, 0xfd, 0x00,
231			0xfe, 0x0f, 0x38, 0xfb, 0x00,
232			0xfd, 0x19, 0x2f, 0xfb, 0x00,
233			0xfc, 0x24, 0x24, 0xfc, 0x00,
234			0xfb, 0x2f, 0x19, 0xfd, 0x00,
235			0xfb, 0x38, 0x0f, 0xfe, 0x00,
236			0xfd, 0x3d, 0x06, 0x00, 0x00
237		}
238	},
239	{
240		.min = 1024,
241		.max = 1331,
242		.coef = {
243			0xfc, 0x05, 0x3e, 0x05, 0xfc,
244			0xf8, 0x0e, 0x3b, 0xff, 0x00,
245			0xf5, 0x18, 0x38, 0xf9, 0x02,
246			0xf4, 0x21, 0x31, 0xf5, 0x05,
247			0xf4, 0x2a, 0x27, 0xf4, 0x07,
248			0xf6, 0x30, 0x1e, 0xf4, 0x08,
249			0xf9, 0x35, 0x15, 0xf6, 0x07,
250			0xff, 0x37, 0x0b, 0xf9, 0x06
251		}
252	},
253	{
254		.min = 1331,
255		.max = 1433,
256		.coef = {
257			0xf8, 0x0a, 0x3c, 0x0a, 0xf8,
258			0xf6, 0x12, 0x3b, 0x02, 0xfb,
259			0xf4, 0x1b, 0x35, 0xfd, 0xff,
260			0xf4, 0x23, 0x30, 0xf8, 0x01,
261			0xf6, 0x29, 0x27, 0xf6, 0x04,
262			0xf9, 0x2e, 0x1e, 0xf5, 0x06,
263			0xfd, 0x31, 0x16, 0xf6, 0x06,
264			0x02, 0x32, 0x0d, 0xf8, 0x07
265		}
266	},
267	{
268		.min = 1433,
269		.max = 1536,
270		.coef = {
271			0xf6, 0x0e, 0x38, 0x0e, 0xf6,
272			0xf5, 0x15, 0x38, 0x06, 0xf8,
273			0xf5, 0x1d, 0x33, 0x00, 0xfb,
274			0xf6, 0x23, 0x2d, 0xfc, 0xfe,
275			0xf9, 0x28, 0x26, 0xf9, 0x00,
276			0xfc, 0x2c, 0x1e, 0xf7, 0x03,
277			0x00, 0x2e, 0x18, 0xf6, 0x04,
278			0x05, 0x2e, 0x11, 0xf7, 0x05
279		}
280	},
281	{
282		.min = 1536,
283		.max = 2048,
284		.coef = {
285			0xfb, 0x13, 0x24, 0x13, 0xfb,
286			0xfd, 0x17, 0x23, 0x0f, 0xfa,
287			0xff, 0x1a, 0x23, 0x0b, 0xf9,
288			0x01, 0x1d, 0x22, 0x07, 0xf9,
289			0x04, 0x20, 0x1f, 0x04, 0xf9,
290			0x07, 0x22, 0x1c, 0x01, 0xfa,
291			0x0b, 0x24, 0x17, 0xff, 0xfb,
292			0x0f, 0x24, 0x14, 0xfd, 0xfc
293		}
294	},
295	{
296		.min = 2048,
297		.max = 3072,
298		.coef = {
299			0x05, 0x10, 0x16, 0x10, 0x05,
300			0x06, 0x11, 0x16, 0x0f, 0x04,
301			0x08, 0x13, 0x15, 0x0e, 0x02,
302			0x09, 0x14, 0x16, 0x0c, 0x01,
303			0x0b, 0x15, 0x15, 0x0b, 0x00,
304			0x0d, 0x16, 0x13, 0x0a, 0x00,
305			0x0f, 0x17, 0x13, 0x08, 0xff,
306			0x11, 0x18, 0x12, 0x07, 0xfe
307		}
308	},
309	{
310		.min = 3072,
311		.max = 4096,
312		.coef = {
313			0x09, 0x0f, 0x10, 0x0f, 0x09,
314			0x09, 0x0f, 0x12, 0x0e, 0x08,
315			0x0a, 0x10, 0x11, 0x0e, 0x07,
316			0x0b, 0x11, 0x11, 0x0d, 0x06,
317			0x0c, 0x11, 0x12, 0x0c, 0x05,
318			0x0d, 0x12, 0x11, 0x0c, 0x04,
319			0x0e, 0x12, 0x11, 0x0b, 0x04,
320			0x0f, 0x13, 0x11, 0x0a, 0x03
321		}
322	},
323	{
324		.min = 4096,
325		.max = 5120,
326		.coef = {
327			0x0a, 0x0e, 0x10, 0x0e, 0x0a,
328			0x0b, 0x0e, 0x0f, 0x0e, 0x0a,
329			0x0b, 0x0f, 0x10, 0x0d, 0x09,
330			0x0c, 0x0f, 0x10, 0x0d, 0x08,
331			0x0d, 0x0f, 0x0f, 0x0d, 0x08,
332			0x0d, 0x10, 0x10, 0x0c, 0x07,
333			0x0e, 0x10, 0x0f, 0x0c, 0x07,
334			0x0f, 0x10, 0x10, 0x0b, 0x06
335		}
336	},
337	{
338		.min = 5120,
339		.max = 65535,
340		.coef = {
341			0x0b, 0x0e, 0x0e, 0x0e, 0x0b,
342			0x0b, 0x0e, 0x0f, 0x0d, 0x0b,
343			0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
344			0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
345			0x0d, 0x0f, 0x0e, 0x0d, 0x09,
346			0x0d, 0x0f, 0x0f, 0x0c, 0x09,
347			0x0e, 0x0f, 0x0e, 0x0c, 0x09,
348			0x0e, 0x0f, 0x0f, 0x0c, 0x08
349		}
350	}
351};
352
353#define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec)
354
355static struct bdisp_filter_addr bdisp_h_filter[NB_H_FILTER];
356static struct bdisp_filter_addr bdisp_v_filter[NB_V_FILTER];
357
358/**
359 * bdisp_hw_reset
360 * @bdisp:      bdisp entity
361 *
362 * Resets HW
363 *
364 * RETURNS:
365 * 0 on success.
366 */
367int bdisp_hw_reset(struct bdisp_dev *bdisp)
368{
369	unsigned int i;
370
371	dev_dbg(bdisp->dev, "%s\n", __func__);
372
373	/* Mask Interrupt */
374	writel(0, bdisp->regs + BLT_ITM0);
375
376	/* Reset */
377	writel(readl(bdisp->regs + BLT_CTL) | BLT_CTL_RESET,
378	       bdisp->regs + BLT_CTL);
379	writel(0, bdisp->regs + BLT_CTL);
380
381	/* Wait for reset done */
382	for (i = 0; i < POLL_RST_MAX; i++) {
383		if (readl(bdisp->regs + BLT_STA1) & BLT_STA1_IDLE)
384			break;
385		udelay(POLL_RST_DELAY_MS * 1000);
386	}
387	if (i == POLL_RST_MAX)
388		dev_err(bdisp->dev, "Reset timeout\n");
389
390	return (i == POLL_RST_MAX) ? -EAGAIN : 0;
391}
392
393/**
394 * bdisp_hw_get_and_clear_irq
395 * @bdisp:      bdisp entity
396 *
397 * Read then reset interrupt status
398 *
399 * RETURNS:
400 * 0 if expected interrupt was raised.
401 */
402int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp)
403{
404	u32 its;
405
406	its = readl(bdisp->regs + BLT_ITS);
407
408	/* Check for the only expected IT: LastNode of AQ1 */
409	if (!(its & BLT_ITS_AQ1_LNA)) {
410		dev_dbg(bdisp->dev, "Unexpected IT status: 0x%08X\n", its);
411		writel(its, bdisp->regs + BLT_ITS);
412		return -1;
413	}
414
415	/* Clear and mask */
416	writel(its, bdisp->regs + BLT_ITS);
417	writel(0, bdisp->regs + BLT_ITM0);
418
419	return 0;
420}
421
422/**
423 * bdisp_hw_free_nodes
424 * @ctx:        bdisp context
425 *
426 * Free node memory
427 *
428 * RETURNS:
429 * None
430 */
431void bdisp_hw_free_nodes(struct bdisp_ctx *ctx)
432{
433	if (ctx && ctx->node[0])
434		dma_free_attrs(ctx->bdisp_dev->dev,
435			       sizeof(struct bdisp_node) * MAX_NB_NODE,
436			       ctx->node[0], ctx->node_paddr[0],
437			       DMA_ATTR_WRITE_COMBINE);
438}
439
440/**
441 * bdisp_hw_alloc_nodes
442 * @ctx:        bdisp context
443 *
444 * Allocate dma memory for nodes
445 *
446 * RETURNS:
447 * 0 on success
448 */
449int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx)
450{
451	struct device *dev = ctx->bdisp_dev->dev;
452	unsigned int i, node_size = sizeof(struct bdisp_node);
453	void *base;
454	dma_addr_t paddr;
455
456	/* Allocate all the nodes within a single memory page */
457	base = dma_alloc_attrs(dev, node_size * MAX_NB_NODE, &paddr,
458			       GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
459	if (!base) {
460		dev_err(dev, "%s no mem\n", __func__);
461		return -ENOMEM;
462	}
463
464	memset(base, 0, node_size * MAX_NB_NODE);
465
466	for (i = 0; i < MAX_NB_NODE; i++) {
467		ctx->node[i] = base;
468		ctx->node_paddr[i] = paddr;
469		dev_dbg(dev, "node[%d]=0x%p (paddr=%pad)\n", i, ctx->node[i],
470			&paddr);
471		base += node_size;
472		paddr += node_size;
473	}
474
475	return 0;
476}
477
478/**
479 * bdisp_hw_free_filters
480 * @dev:        device
481 *
482 * Free filters memory
483 *
484 * RETURNS:
485 * None
486 */
487void bdisp_hw_free_filters(struct device *dev)
488{
489	int size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
490
491	if (bdisp_h_filter[0].virt)
492		dma_free_attrs(dev, size, bdisp_h_filter[0].virt,
493			       bdisp_h_filter[0].paddr, DMA_ATTR_WRITE_COMBINE);
494}
495
496/**
497 * bdisp_hw_alloc_filters
498 * @dev:        device
499 *
500 * Allocate dma memory for filters
501 *
502 * RETURNS:
503 * 0 on success
504 */
505int bdisp_hw_alloc_filters(struct device *dev)
506{
507	unsigned int i, size;
508	void *base;
509	dma_addr_t paddr;
510
511	/* Allocate all the filters within a single memory page */
512	size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
513	base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL,
514			       DMA_ATTR_WRITE_COMBINE);
515	if (!base)
516		return -ENOMEM;
517
518	/* Setup filter addresses */
519	for (i = 0; i < NB_H_FILTER; i++) {
520		bdisp_h_filter[i].min = bdisp_h_spec[i].min;
521		bdisp_h_filter[i].max = bdisp_h_spec[i].max;
522		memcpy(base, bdisp_h_spec[i].coef, BDISP_HF_NB);
523		bdisp_h_filter[i].virt = base;
524		bdisp_h_filter[i].paddr = paddr;
525		base += BDISP_HF_NB;
526		paddr += BDISP_HF_NB;
527	}
528
529	for (i = 0; i < NB_V_FILTER; i++) {
530		bdisp_v_filter[i].min = bdisp_v_spec[i].min;
531		bdisp_v_filter[i].max = bdisp_v_spec[i].max;
532		memcpy(base, bdisp_v_spec[i].coef, BDISP_VF_NB);
533		bdisp_v_filter[i].virt = base;
534		bdisp_v_filter[i].paddr = paddr;
535		base += BDISP_VF_NB;
536		paddr += BDISP_VF_NB;
537	}
538
539	return 0;
540}
541
542/**
543 * bdisp_hw_get_hf_addr
544 * @inc:        resize increment
545 *
546 * Find the horizontal filter table that fits the resize increment
547 *
548 * RETURNS:
549 * table physical address
550 */
551static dma_addr_t bdisp_hw_get_hf_addr(u16 inc)
552{
553	unsigned int i;
554
555	for (i = NB_H_FILTER - 1; i > 0; i--)
556		if ((bdisp_h_filter[i].min < inc) &&
557		    (inc <= bdisp_h_filter[i].max))
558			break;
559
560	return bdisp_h_filter[i].paddr;
561}
562
563/**
564 * bdisp_hw_get_vf_addr
565 * @inc:        resize increment
566 *
567 * Find the vertical filter table that fits the resize increment
568 *
569 * RETURNS:
570 * table physical address
571 */
572static dma_addr_t bdisp_hw_get_vf_addr(u16 inc)
573{
574	unsigned int i;
575
576	for (i = NB_V_FILTER - 1; i > 0; i--)
577		if ((bdisp_v_filter[i].min < inc) &&
578		    (inc <= bdisp_v_filter[i].max))
579			break;
580
581	return bdisp_v_filter[i].paddr;
582}
583
584/**
585 * bdisp_hw_get_inc
586 * @from:       input size
587 * @to:         output size
588 * @inc:        resize increment in 6.10 format
589 *
590 * Computes the increment (inverse of scale) in 6.10 format
591 *
592 * RETURNS:
593 * 0 on success
594 */
595static int bdisp_hw_get_inc(u32 from, u32 to, u16 *inc)
596{
597	u32 tmp;
598
599	if (!to)
600		return -EINVAL;
601
602	if (to == from) {
603		*inc = 1 << 10;
604		return 0;
605	}
606
607	tmp = (from << 10) / to;
608	if ((tmp > 0xFFFF) || (!tmp))
609		/* overflow (downscale x 63) or too small (upscale x 1024) */
610		return -EINVAL;
611
612	*inc = (u16)tmp;
613
614	return 0;
615}
616
617/**
618 * bdisp_hw_get_hv_inc
619 * @ctx:        device context
620 * @h_inc:      horizontal increment
621 * @v_inc:      vertical increment
622 *
623 * Computes the horizontal & vertical increments (inverse of scale)
624 *
625 * RETURNS:
626 * 0 on success
627 */
628static int bdisp_hw_get_hv_inc(struct bdisp_ctx *ctx, u16 *h_inc, u16 *v_inc)
629{
630	u32 src_w, src_h, dst_w, dst_h;
631
632	src_w = ctx->src.crop.width;
633	src_h = ctx->src.crop.height;
634	dst_w = ctx->dst.crop.width;
635	dst_h = ctx->dst.crop.height;
636
637	if (bdisp_hw_get_inc(src_w, dst_w, h_inc) ||
638	    bdisp_hw_get_inc(src_h, dst_h, v_inc)) {
639		dev_err(ctx->bdisp_dev->dev,
640			"scale factors failed (%dx%d)->(%dx%d)\n",
641			src_w, src_h, dst_w, dst_h);
642		return -EINVAL;
643	}
644
645	return 0;
646}
647
648/**
649 * bdisp_hw_get_op_cfg
650 * @ctx:        device context
651 * @c:          operation configuration
652 *
653 * Check which blitter operations are expected and sets the scaling increments
654 *
655 * RETURNS:
656 * 0 on success
657 */
658static int bdisp_hw_get_op_cfg(struct bdisp_ctx *ctx, struct bdisp_op_cfg *c)
659{
660	struct device *dev = ctx->bdisp_dev->dev;
661	struct bdisp_frame *src = &ctx->src;
662	struct bdisp_frame *dst = &ctx->dst;
663
664	if (src->width > MAX_SRC_WIDTH * MAX_VERTICAL_STRIDES) {
665		dev_err(dev, "Image width out of HW caps\n");
666		return -EINVAL;
667	}
668
669	c->wide = src->width > MAX_SRC_WIDTH;
670
671	c->hflip = ctx->hflip;
672	c->vflip = ctx->vflip;
673
674	c->src_interlaced = (src->field == V4L2_FIELD_INTERLACED);
675
676	c->src_nbp = src->fmt->nb_planes;
677	c->src_yuv = (src->fmt->pixelformat == V4L2_PIX_FMT_NV12) ||
678			(src->fmt->pixelformat == V4L2_PIX_FMT_YUV420);
679	c->src_420 = c->src_yuv;
680
681	c->dst_nbp = dst->fmt->nb_planes;
682	c->dst_yuv = (dst->fmt->pixelformat == V4L2_PIX_FMT_NV12) ||
683			(dst->fmt->pixelformat == V4L2_PIX_FMT_YUV420);
684	c->dst_420 = c->dst_yuv;
685
686	c->cconv = (c->src_yuv != c->dst_yuv);
687
688	if (bdisp_hw_get_hv_inc(ctx, &c->h_inc, &c->v_inc)) {
689		dev_err(dev, "Scale factor out of HW caps\n");
690		return -EINVAL;
691	}
692
693	/* Deinterlacing adjustment : stretch a field to a frame */
694	if (c->src_interlaced)
695		c->v_inc /= 2;
696
697	if ((c->h_inc != (1 << 10)) || (c->v_inc != (1 << 10)))
698		c->scale = true;
699	else
700		c->scale = false;
701
702	return 0;
703}
704
705/**
706 * bdisp_hw_color_format
707 * @pixelformat: v4l2 pixel format
708 *
709 * v4l2 to bdisp pixel format convert
710 *
711 * RETURNS:
712 * bdisp pixel format
713 */
714static u32 bdisp_hw_color_format(u32 pixelformat)
715{
716	u32 ret;
717
718	switch (pixelformat) {
719	case V4L2_PIX_FMT_YUV420:
720		ret = (BDISP_YUV_3B << BLT_TTY_COL_SHIFT);
721		break;
722	case V4L2_PIX_FMT_NV12:
723		ret = (BDISP_NV12 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END;
724		break;
725	case V4L2_PIX_FMT_RGB565:
726		ret = (BDISP_RGB565 << BLT_TTY_COL_SHIFT);
727		break;
728	case V4L2_PIX_FMT_XBGR32: /* This V4L format actually refers to xRGB */
729		ret = (BDISP_XRGB8888 << BLT_TTY_COL_SHIFT);
730		break;
731	case V4L2_PIX_FMT_RGB24:  /* RGB888 format */
732		ret = (BDISP_RGB888 << BLT_TTY_COL_SHIFT) | BLT_TTY_BIG_END;
733		break;
734	case V4L2_PIX_FMT_ABGR32: /* This V4L format actually refers to ARGB */
735
736	default:
737		ret = (BDISP_ARGB8888 << BLT_TTY_COL_SHIFT) | BLT_TTY_ALPHA_R;
738		break;
739	}
740
741	return ret;
742}
743
744/**
745 * bdisp_hw_build_node
746 * @ctx:        device context
747 * @cfg:        operation configuration
748 * @node:       node to be set
749 * @t_plan:     whether the node refers to a RGB/Y or a CbCr plane
750 * @src_x_offset: x offset in the source image
751 *
752 * Build a node
753 *
754 * RETURNS:
755 * None
756 */
757static void bdisp_hw_build_node(struct bdisp_ctx *ctx,
758				struct bdisp_op_cfg *cfg,
759				struct bdisp_node *node,
760				enum bdisp_target_plan t_plan, int src_x_offset)
761{
762	struct bdisp_frame *src = &ctx->src;
763	struct bdisp_frame *dst = &ctx->dst;
764	u16 h_inc, v_inc, yh_inc, yv_inc;
765	struct v4l2_rect src_rect = src->crop;
766	struct v4l2_rect dst_rect = dst->crop;
767	int dst_x_offset;
768	s32 dst_width = dst->crop.width;
769	u32 src_fmt, dst_fmt;
770	const u32 *ivmx;
771
772	dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__);
773
774	memset(node, 0, sizeof(*node));
775
776	/* Adjust src and dst areas wrt src_x_offset */
777	src_rect.left += src_x_offset;
778	src_rect.width -= src_x_offset;
779	src_rect.width = min_t(__s32, MAX_SRC_WIDTH, src_rect.width);
780
781	dst_x_offset = (src_x_offset * dst_width) / ctx->src.crop.width;
782	dst_rect.left += dst_x_offset;
783	dst_rect.width = (src_rect.width * dst_width) / ctx->src.crop.width;
784
785	/* General */
786	src_fmt = src->fmt->pixelformat;
787	dst_fmt = dst->fmt->pixelformat;
788
789	node->nip = 0;
790	node->cic = BLT_CIC_ALL_GRP;
791	node->ack = BLT_ACK_BYPASS_S2S3;
792
793	switch (cfg->src_nbp) {
794	case 1:
795		/* Src2 = RGB / Src1 = Src3 = off */
796		node->ins = BLT_INS_S1_OFF | BLT_INS_S2_MEM | BLT_INS_S3_OFF;
797		break;
798	case 2:
799		/* Src3 = Y
800		 * Src2 = CbCr or ColorFill if writing the Y plane
801		 * Src1 = off */
802		node->ins = BLT_INS_S1_OFF | BLT_INS_S3_MEM;
803		if (t_plan == BDISP_Y)
804			node->ins |= BLT_INS_S2_CF;
805		else
806			node->ins |= BLT_INS_S2_MEM;
807		break;
808	case 3:
809	default:
810		/* Src3 = Y
811		 * Src2 = Cb or ColorFill if writing the Y plane
812		 * Src1 = Cr or ColorFill if writing the Y plane */
813		node->ins = BLT_INS_S3_MEM;
814		if (t_plan == BDISP_Y)
815			node->ins |= BLT_INS_S2_CF | BLT_INS_S1_CF;
816		else
817			node->ins |= BLT_INS_S2_MEM | BLT_INS_S1_MEM;
818		break;
819	}
820
821	/* Color convert */
822	node->ins |= cfg->cconv ? BLT_INS_IVMX : 0;
823	/* Scale needed if scaling OR 4:2:0 up/downsampling */
824	node->ins |= (cfg->scale || cfg->src_420 || cfg->dst_420) ?
825			BLT_INS_SCALE : 0;
826
827	/* Target */
828	node->tba = (t_plan == BDISP_CBCR) ? dst->paddr[1] : dst->paddr[0];
829
830	node->tty = dst->bytesperline;
831	node->tty |= bdisp_hw_color_format(dst_fmt);
832	node->tty |= BLT_TTY_DITHER;
833	node->tty |= (t_plan == BDISP_CBCR) ? BLT_TTY_CHROMA : 0;
834	node->tty |= cfg->hflip ? BLT_TTY_HSO : 0;
835	node->tty |= cfg->vflip ? BLT_TTY_VSO : 0;
836
837	if (cfg->dst_420 && (t_plan == BDISP_CBCR)) {
838		/* 420 chroma downsampling */
839		dst_rect.height /= 2;
840		dst_rect.width /= 2;
841		dst_rect.left /= 2;
842		dst_rect.top /= 2;
843		dst_x_offset /= 2;
844		dst_width /= 2;
845	}
846
847	node->txy = cfg->vflip ? (dst_rect.height - 1) : dst_rect.top;
848	node->txy <<= 16;
849	node->txy |= cfg->hflip ? (dst_width - dst_x_offset - 1) :
850			dst_rect.left;
851
852	node->tsz = dst_rect.height << 16 | dst_rect.width;
853
854	if (cfg->src_interlaced) {
855		/* handle only the top field which is half height of a frame */
856		src_rect.top /= 2;
857		src_rect.height /= 2;
858	}
859
860	if (cfg->src_nbp == 1) {
861		/* Src 2 : RGB */
862		node->s2ba = src->paddr[0];
863
864		node->s2ty = src->bytesperline;
865		if (cfg->src_interlaced)
866			node->s2ty *= 2;
867
868		node->s2ty |= bdisp_hw_color_format(src_fmt);
869
870		node->s2xy = src_rect.top << 16 | src_rect.left;
871		node->s2sz = src_rect.height << 16 | src_rect.width;
872	} else {
873		/* Src 2 : Cb or CbCr */
874		if (cfg->src_420) {
875			/* 420 chroma upsampling */
876			src_rect.top /= 2;
877			src_rect.left /= 2;
878			src_rect.width /= 2;
879			src_rect.height /= 2;
880		}
881
882		node->s2ba = src->paddr[1];
883
884		node->s2ty = src->bytesperline;
885		if (cfg->src_nbp == 3)
886			node->s2ty /= 2;
887		if (cfg->src_interlaced)
888			node->s2ty *= 2;
889
890		node->s2ty |= bdisp_hw_color_format(src_fmt);
891
892		node->s2xy = src_rect.top << 16 | src_rect.left;
893		node->s2sz = src_rect.height << 16 | src_rect.width;
894
895		if (cfg->src_nbp == 3) {
896			/* Src 1 : Cr */
897			node->s1ba = src->paddr[2];
898
899			node->s1ty = node->s2ty;
900			node->s1xy = node->s2xy;
901		}
902
903		/* Src 3 : Y */
904		node->s3ba = src->paddr[0];
905
906		node->s3ty = src->bytesperline;
907		if (cfg->src_interlaced)
908			node->s3ty *= 2;
909		node->s3ty |= bdisp_hw_color_format(src_fmt);
910
911		if ((t_plan != BDISP_CBCR) && cfg->src_420) {
912			/* No chroma upsampling for output RGB / Y plane */
913			node->s3xy = node->s2xy * 2;
914			node->s3sz = node->s2sz * 2;
915		} else {
916			/* No need to read Y (Src3) when writing Chroma */
917			node->s3ty |= BLT_S3TY_BLANK_ACC;
918			node->s3xy = node->s2xy;
919			node->s3sz = node->s2sz;
920		}
921	}
922
923	/* Resize (scale OR 4:2:0: chroma up/downsampling) */
924	if (node->ins & BLT_INS_SCALE) {
925		/* no need to compute Y when writing CbCr from RGB input */
926		bool skip_y = (t_plan == BDISP_CBCR) && !cfg->src_yuv;
927
928		/* FCTL */
929		if (cfg->scale) {
930			node->fctl = BLT_FCTL_HV_SCALE;
931			if (!skip_y)
932				node->fctl |= BLT_FCTL_Y_HV_SCALE;
933		} else {
934			node->fctl = BLT_FCTL_HV_SAMPLE;
935			if (!skip_y)
936				node->fctl |= BLT_FCTL_Y_HV_SAMPLE;
937		}
938
939		/* RSF - Chroma may need to be up/downsampled */
940		h_inc = cfg->h_inc;
941		v_inc = cfg->v_inc;
942		if (!cfg->src_420 && cfg->dst_420 && (t_plan == BDISP_CBCR)) {
943			/* RGB to 4:2:0 for Chroma: downsample */
944			h_inc *= 2;
945			v_inc *= 2;
946		} else if (cfg->src_420 && !cfg->dst_420) {
947			/* 4:2:0: to RGB: upsample*/
948			h_inc /= 2;
949			v_inc /= 2;
950		}
951		node->rsf = v_inc << 16 | h_inc;
952
953		/* RZI */
954		node->rzi = BLT_RZI_DEFAULT;
955
956		/* Filter table physical addr */
957		node->hfp = bdisp_hw_get_hf_addr(h_inc);
958		node->vfp = bdisp_hw_get_vf_addr(v_inc);
959
960		/* Y version */
961		if (!skip_y) {
962			yh_inc = cfg->h_inc;
963			yv_inc = cfg->v_inc;
964
965			node->y_rsf = yv_inc << 16 | yh_inc;
966			node->y_rzi = BLT_RZI_DEFAULT;
967			node->y_hfp = bdisp_hw_get_hf_addr(yh_inc);
968			node->y_vfp = bdisp_hw_get_vf_addr(yv_inc);
969		}
970	}
971
972	/* Versatile matrix for RGB / YUV conversion */
973	if (cfg->cconv) {
974		ivmx = cfg->src_yuv ? bdisp_yuv_to_rgb : bdisp_rgb_to_yuv;
975
976		node->ivmx0 = ivmx[0];
977		node->ivmx1 = ivmx[1];
978		node->ivmx2 = ivmx[2];
979		node->ivmx3 = ivmx[3];
980	}
981}
982
983/**
984 * bdisp_hw_build_all_nodes
985 * @ctx:        device context
986 *
987 * Build all the nodes for the blitter operation
988 *
989 * RETURNS:
990 * 0 on success
991 */
992static int bdisp_hw_build_all_nodes(struct bdisp_ctx *ctx)
993{
994	struct bdisp_op_cfg cfg;
995	unsigned int i, nid = 0;
996	int src_x_offset = 0;
997
998	for (i = 0; i < MAX_NB_NODE; i++)
999		if (!ctx->node[i]) {
1000			dev_err(ctx->bdisp_dev->dev, "node %d is null\n", i);
1001			return -EINVAL;
1002		}
1003
1004	/* Get configuration (scale, flip, ...) */
1005	if (bdisp_hw_get_op_cfg(ctx, &cfg))
1006		return -EINVAL;
1007
1008	/* Split source in vertical strides (HW constraint) */
1009	for (i = 0; i < MAX_VERTICAL_STRIDES; i++) {
1010		/* Build RGB/Y node and link it to the previous node */
1011		bdisp_hw_build_node(ctx, &cfg, ctx->node[nid],
1012				    cfg.dst_nbp == 1 ? BDISP_RGB : BDISP_Y,
1013				    src_x_offset);
1014		if (nid)
1015			ctx->node[nid - 1]->nip = ctx->node_paddr[nid];
1016		nid++;
1017
1018		/* Build additional Cb(Cr) node, link it to the previous one */
1019		if (cfg.dst_nbp > 1) {
1020			bdisp_hw_build_node(ctx, &cfg, ctx->node[nid],
1021					    BDISP_CBCR, src_x_offset);
1022			ctx->node[nid - 1]->nip = ctx->node_paddr[nid];
1023			nid++;
1024		}
1025
1026		/* Next stride until full width covered */
1027		src_x_offset += MAX_SRC_WIDTH;
1028		if (src_x_offset >= ctx->src.crop.width)
1029			break;
1030	}
1031
1032	/* Mark last node as the last */
1033	ctx->node[nid - 1]->nip = 0;
1034
1035	return 0;
1036}
1037
1038/**
1039 * bdisp_hw_save_request
1040 * @ctx:        device context
1041 *
1042 * Save a copy of the request and of the built nodes
1043 *
1044 * RETURNS:
1045 * None
1046 */
1047static void bdisp_hw_save_request(struct bdisp_ctx *ctx)
1048{
1049	struct bdisp_node **copy_node = ctx->bdisp_dev->dbg.copy_node;
1050	struct bdisp_request *request = &ctx->bdisp_dev->dbg.copy_request;
1051	struct bdisp_node **node = ctx->node;
1052	int i;
1053
1054	/* Request copy */
1055	request->src = ctx->src;
1056	request->dst = ctx->dst;
1057	request->hflip = ctx->hflip;
1058	request->vflip = ctx->vflip;
1059	request->nb_req++;
1060
1061	/* Nodes copy */
1062	for (i = 0; i < MAX_NB_NODE; i++) {
1063		/* Allocate memory if not done yet */
1064		if (!copy_node[i]) {
1065			copy_node[i] = devm_kzalloc(ctx->bdisp_dev->dev,
1066						    sizeof(*copy_node[i]),
1067						    GFP_ATOMIC);
1068			if (!copy_node[i])
1069				return;
1070		}
1071		*copy_node[i] = *node[i];
1072	}
1073}
1074
1075/**
1076 * bdisp_hw_update
1077 * @ctx:        device context
1078 *
1079 * Send the request to the HW
1080 *
1081 * RETURNS:
1082 * 0 on success
1083 */
1084int bdisp_hw_update(struct bdisp_ctx *ctx)
1085{
1086	int ret;
1087	struct bdisp_dev *bdisp = ctx->bdisp_dev;
1088	struct device *dev = bdisp->dev;
1089	unsigned int node_id;
1090
1091	dev_dbg(dev, "%s\n", __func__);
1092
1093	/* build nodes */
1094	ret = bdisp_hw_build_all_nodes(ctx);
1095	if (ret) {
1096		dev_err(dev, "cannot build nodes (%d)\n", ret);
1097		return ret;
1098	}
1099
1100	/* Save a copy of the request */
1101	bdisp_hw_save_request(ctx);
1102
1103	/* Configure interrupt to 'Last Node Reached for AQ1' */
1104	writel(BLT_AQ1_CTL_CFG, bdisp->regs + BLT_AQ1_CTL);
1105	writel(BLT_ITS_AQ1_LNA, bdisp->regs + BLT_ITM0);
1106
1107	/* Write first node addr */
1108	writel(ctx->node_paddr[0], bdisp->regs + BLT_AQ1_IP);
1109
1110	/* Find and write last node addr : this starts the HW processing */
1111	for (node_id = 0; node_id < MAX_NB_NODE - 1; node_id++) {
1112		if (!ctx->node[node_id]->nip)
1113			break;
1114	}
1115	writel(ctx->node_paddr[node_id], bdisp->regs + BLT_AQ1_LNA);
1116
1117	return 0;
1118}
1119