1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/module.h> |
3 | #include <linux/kernel.h> |
4 | #include <linux/errno.h> |
5 | #include <linux/string.h> |
6 | #include <linux/mm.h> |
7 | #include <linux/slab.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/fb.h> |
10 | #include <linux/ioport.h> |
11 | #include <linux/init.h> |
12 | #include <linux/pci.h> |
13 | #include <linux/vmalloc.h> |
14 | #include <linux/pagemap.h> |
15 | #include <linux/console.h> |
16 | #include <linux/platform_device.h> |
17 | |
18 | #include "sm750.h" |
19 | #include "sm750_accel.h" |
20 | static inline void write_dpr(struct lynx_accel *accel, int offset, u32 regValue) |
21 | { |
22 | writel(val: regValue, addr: accel->dprBase + offset); |
23 | } |
24 | |
25 | static inline u32 read_dpr(struct lynx_accel *accel, int offset) |
26 | { |
27 | return readl(addr: accel->dprBase + offset); |
28 | } |
29 | |
30 | static inline void write_dpPort(struct lynx_accel *accel, u32 data) |
31 | { |
32 | writel(val: data, addr: accel->dpPortBase); |
33 | } |
34 | |
35 | void sm750_hw_de_init(struct lynx_accel *accel) |
36 | { |
37 | /* setup 2d engine registers */ |
38 | u32 reg, clr; |
39 | |
40 | write_dpr(accel, DE_MASKS, regValue: 0xFFFFFFFF); |
41 | |
42 | /* dpr1c */ |
43 | reg = 0x3; |
44 | |
45 | clr = DE_STRETCH_FORMAT_PATTERN_XY | |
46 | DE_STRETCH_FORMAT_PATTERN_Y_MASK | |
47 | DE_STRETCH_FORMAT_PATTERN_X_MASK | |
48 | DE_STRETCH_FORMAT_ADDRESSING_MASK | |
49 | DE_STRETCH_FORMAT_SOURCE_HEIGHT_MASK; |
50 | |
51 | /* DE_STRETCH bpp format need be initialized in setMode routine */ |
52 | write_dpr(accel, DE_STRETCH_FORMAT, |
53 | regValue: (read_dpr(accel, DE_STRETCH_FORMAT) & ~clr) | reg); |
54 | |
55 | /* disable clipping and transparent */ |
56 | write_dpr(accel, DE_CLIP_TL, regValue: 0); /* dpr2c */ |
57 | write_dpr(accel, DE_CLIP_BR, regValue: 0); /* dpr30 */ |
58 | |
59 | write_dpr(accel, DE_COLOR_COMPARE_MASK, regValue: 0); /* dpr24 */ |
60 | write_dpr(accel, DE_COLOR_COMPARE, regValue: 0); |
61 | |
62 | clr = DE_CONTROL_TRANSPARENCY | DE_CONTROL_TRANSPARENCY_MATCH | |
63 | DE_CONTROL_TRANSPARENCY_SELECT; |
64 | |
65 | /* dpr0c */ |
66 | write_dpr(accel, DE_CONTROL, regValue: read_dpr(accel, DE_CONTROL) & ~clr); |
67 | } |
68 | |
69 | /* |
70 | * set2dformat only be called from setmode functions |
71 | * but if you need dual framebuffer driver,need call set2dformat |
72 | * every time you use 2d function |
73 | */ |
74 | |
75 | void sm750_hw_set2dformat(struct lynx_accel *accel, int fmt) |
76 | { |
77 | u32 reg; |
78 | |
79 | /* fmt=0,1,2 for 8,16,32,bpp on sm718/750/502 */ |
80 | reg = read_dpr(accel, DE_STRETCH_FORMAT); |
81 | reg &= ~DE_STRETCH_FORMAT_PIXEL_FORMAT_MASK; |
82 | reg |= ((fmt << DE_STRETCH_FORMAT_PIXEL_FORMAT_SHIFT) & |
83 | DE_STRETCH_FORMAT_PIXEL_FORMAT_MASK); |
84 | write_dpr(accel, DE_STRETCH_FORMAT, regValue: reg); |
85 | } |
86 | |
87 | int sm750_hw_fillrect(struct lynx_accel *accel, |
88 | u32 base, u32 pitch, u32 Bpp, |
89 | u32 x, u32 y, u32 width, u32 height, |
90 | u32 color, u32 rop) |
91 | { |
92 | u32 deCtrl; |
93 | |
94 | if (accel->de_wait() != 0) { |
95 | /* |
96 | * int time wait and always busy,seems hardware |
97 | * got something error |
98 | */ |
99 | pr_debug("De engine always busy\n" ); |
100 | return -1; |
101 | } |
102 | |
103 | write_dpr(accel, DE_WINDOW_DESTINATION_BASE, regValue: base); /* dpr40 */ |
104 | write_dpr(accel, DE_PITCH, |
105 | regValue: ((pitch / Bpp << DE_PITCH_DESTINATION_SHIFT) & |
106 | DE_PITCH_DESTINATION_MASK) | |
107 | (pitch / Bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */ |
108 | |
109 | write_dpr(accel, DE_WINDOW_WIDTH, |
110 | regValue: ((pitch / Bpp << DE_WINDOW_WIDTH_DST_SHIFT) & |
111 | DE_WINDOW_WIDTH_DST_MASK) | |
112 | (pitch / Bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr44 */ |
113 | |
114 | write_dpr(accel, DE_FOREGROUND, regValue: color); /* DPR14 */ |
115 | |
116 | write_dpr(accel, DE_DESTINATION, |
117 | regValue: ((x << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) | |
118 | (y & DE_DESTINATION_Y_MASK)); /* dpr4 */ |
119 | |
120 | write_dpr(accel, DE_DIMENSION, |
121 | regValue: ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) | |
122 | (height & DE_DIMENSION_Y_ET_MASK)); /* dpr8 */ |
123 | |
124 | deCtrl = DE_CONTROL_STATUS | DE_CONTROL_LAST_PIXEL | |
125 | DE_CONTROL_COMMAND_RECTANGLE_FILL | DE_CONTROL_ROP_SELECT | |
126 | (rop & DE_CONTROL_ROP_MASK); /* dpr0xc */ |
127 | |
128 | write_dpr(accel, DE_CONTROL, regValue: deCtrl); |
129 | return 0; |
130 | } |
131 | |
132 | /** |
133 | * sm750_hw_copyarea |
134 | * @accel: Acceleration device data |
135 | * @sBase: Address of source: offset in frame buffer |
136 | * @sPitch: Pitch value of source surface in BYTE |
137 | * @sx: Starting x coordinate of source surface |
138 | * @sy: Starting y coordinate of source surface |
139 | * @dBase: Address of destination: offset in frame buffer |
140 | * @dPitch: Pitch value of destination surface in BYTE |
141 | * @Bpp: Color depth of destination surface |
142 | * @dx: Starting x coordinate of destination surface |
143 | * @dy: Starting y coordinate of destination surface |
144 | * @width: width of rectangle in pixel value |
145 | * @height: height of rectangle in pixel value |
146 | * @rop2: ROP value |
147 | */ |
148 | int sm750_hw_copyarea(struct lynx_accel *accel, |
149 | unsigned int sBase, unsigned int sPitch, |
150 | unsigned int sx, unsigned int sy, |
151 | unsigned int dBase, unsigned int dPitch, |
152 | unsigned int Bpp, unsigned int dx, unsigned int dy, |
153 | unsigned int width, unsigned int height, |
154 | unsigned int rop2) |
155 | { |
156 | unsigned int nDirection, de_ctrl; |
157 | |
158 | nDirection = LEFT_TO_RIGHT; |
159 | /* Direction of ROP2 operation: 1 = Left to Right, (-1) = Right to Left */ |
160 | de_ctrl = 0; |
161 | |
162 | /* If source and destination are the same surface, need to check for overlay cases */ |
163 | if (sBase == dBase && sPitch == dPitch) { |
164 | /* Determine direction of operation */ |
165 | if (sy < dy) { |
166 | /* +----------+ |
167 | * |S | |
168 | * | +----------+ |
169 | * | | | | |
170 | * | | | | |
171 | * +---|------+ | |
172 | * | D| |
173 | * +----------+ |
174 | */ |
175 | |
176 | nDirection = BOTTOM_TO_TOP; |
177 | } else if (sy > dy) { |
178 | /* +----------+ |
179 | * |D | |
180 | * | +----------+ |
181 | * | | | | |
182 | * | | | | |
183 | * +---|------+ | |
184 | * | S| |
185 | * +----------+ |
186 | */ |
187 | |
188 | nDirection = TOP_TO_BOTTOM; |
189 | } else { |
190 | /* sy == dy */ |
191 | |
192 | if (sx <= dx) { |
193 | /* +------+---+------+ |
194 | * |S | | D| |
195 | * | | | | |
196 | * | | | | |
197 | * | | | | |
198 | * +------+---+------+ |
199 | */ |
200 | |
201 | nDirection = RIGHT_TO_LEFT; |
202 | } else { |
203 | /* sx > dx */ |
204 | |
205 | /* +------+---+------+ |
206 | * |D | | S| |
207 | * | | | | |
208 | * | | | | |
209 | * | | | | |
210 | * +------+---+------+ |
211 | */ |
212 | |
213 | nDirection = LEFT_TO_RIGHT; |
214 | } |
215 | } |
216 | } |
217 | |
218 | if ((nDirection == BOTTOM_TO_TOP) || (nDirection == RIGHT_TO_LEFT)) { |
219 | sx += width - 1; |
220 | sy += height - 1; |
221 | dx += width - 1; |
222 | dy += height - 1; |
223 | } |
224 | |
225 | /* |
226 | * Note: |
227 | * DE_FOREGROUND and DE_BACKGROUND are don't care. |
228 | * DE_COLOR_COMPARE and DE_COLOR_COMPARE_MAKS |
229 | * are set by set deSetTransparency(). |
230 | */ |
231 | |
232 | /* |
233 | * 2D Source Base. |
234 | * It is an address offset (128 bit aligned) |
235 | * from the beginning of frame buffer. |
236 | */ |
237 | write_dpr(accel, DE_WINDOW_SOURCE_BASE, regValue: sBase); /* dpr40 */ |
238 | |
239 | /* |
240 | * 2D Destination Base. |
241 | * It is an address offset (128 bit aligned) |
242 | * from the beginning of frame buffer. |
243 | */ |
244 | write_dpr(accel, DE_WINDOW_DESTINATION_BASE, regValue: dBase); /* dpr44 */ |
245 | |
246 | /* |
247 | * Program pitch (distance between the 1st points of two adjacent lines). |
248 | * Note that input pitch is BYTE value, but the 2D Pitch register uses |
249 | * pixel values. Need Byte to pixel conversion. |
250 | */ |
251 | write_dpr(accel, DE_PITCH, |
252 | regValue: ((dPitch / Bpp << DE_PITCH_DESTINATION_SHIFT) & |
253 | DE_PITCH_DESTINATION_MASK) | |
254 | (sPitch / Bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */ |
255 | |
256 | /* |
257 | * Screen Window width in Pixels. |
258 | * 2D engine uses this value to calculate the linear address in frame buffer |
259 | * for a given point. |
260 | */ |
261 | write_dpr(accel, DE_WINDOW_WIDTH, |
262 | regValue: ((dPitch / Bpp << DE_WINDOW_WIDTH_DST_SHIFT) & |
263 | DE_WINDOW_WIDTH_DST_MASK) | |
264 | (sPitch / Bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr3c */ |
265 | |
266 | if (accel->de_wait() != 0) |
267 | return -1; |
268 | |
269 | write_dpr(accel, DE_SOURCE, |
270 | regValue: ((sx << DE_SOURCE_X_K1_SHIFT) & DE_SOURCE_X_K1_MASK) | |
271 | (sy & DE_SOURCE_Y_K2_MASK)); /* dpr0 */ |
272 | write_dpr(accel, DE_DESTINATION, |
273 | regValue: ((dx << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) | |
274 | (dy & DE_DESTINATION_Y_MASK)); /* dpr04 */ |
275 | write_dpr(accel, DE_DIMENSION, |
276 | regValue: ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) | |
277 | (height & DE_DIMENSION_Y_ET_MASK)); /* dpr08 */ |
278 | |
279 | de_ctrl = (rop2 & DE_CONTROL_ROP_MASK) | DE_CONTROL_ROP_SELECT | |
280 | ((nDirection == RIGHT_TO_LEFT) ? DE_CONTROL_DIRECTION : 0) | |
281 | DE_CONTROL_COMMAND_BITBLT | DE_CONTROL_STATUS; |
282 | write_dpr(accel, DE_CONTROL, regValue: de_ctrl); /* dpr0c */ |
283 | |
284 | return 0; |
285 | } |
286 | |
287 | static unsigned int deGetTransparency(struct lynx_accel *accel) |
288 | { |
289 | unsigned int de_ctrl; |
290 | |
291 | de_ctrl = read_dpr(accel, DE_CONTROL); |
292 | |
293 | de_ctrl &= (DE_CONTROL_TRANSPARENCY_MATCH | |
294 | DE_CONTROL_TRANSPARENCY_SELECT | DE_CONTROL_TRANSPARENCY); |
295 | |
296 | return de_ctrl; |
297 | } |
298 | |
299 | /** |
300 | * sm750_hw_imageblit |
301 | * @accel: Acceleration device data |
302 | * @pSrcbuf: pointer to start of source buffer in system memory |
303 | * @srcDelta: Pitch value (in bytes) of the source buffer, +ive means top down |
304 | * and -ive mean button up |
305 | * @startBit: Mono data can start at any bit in a byte, this value should be |
306 | * 0 to 7 |
307 | * @dBase: Address of destination: offset in frame buffer |
308 | * @dPitch: Pitch value of destination surface in BYTE |
309 | * @bytePerPixel: Color depth of destination surface |
310 | * @dx: Starting x coordinate of destination surface |
311 | * @dy: Starting y coordinate of destination surface |
312 | * @width: width of rectangle in pixel value |
313 | * @height: height of rectangle in pixel value |
314 | * @fColor: Foreground color (corresponding to a 1 in the monochrome data |
315 | * @bColor: Background color (corresponding to a 0 in the monochrome data |
316 | * @rop2: ROP value |
317 | */ |
318 | int sm750_hw_imageblit(struct lynx_accel *accel, const char *pSrcbuf, |
319 | u32 srcDelta, u32 startBit, u32 dBase, u32 dPitch, |
320 | u32 bytePerPixel, u32 dx, u32 dy, u32 width, |
321 | u32 height, u32 fColor, u32 bColor, u32 rop2) |
322 | { |
323 | unsigned int ulBytesPerScan; |
324 | unsigned int ul4BytesPerScan; |
325 | unsigned int ulBytesRemain; |
326 | unsigned int de_ctrl = 0; |
327 | unsigned char ajRemain[4]; |
328 | int i, j; |
329 | |
330 | startBit &= 7; /* Just make sure the start bit is within legal range */ |
331 | ulBytesPerScan = (width + startBit + 7) / 8; |
332 | ul4BytesPerScan = ulBytesPerScan & ~3; |
333 | ulBytesRemain = ulBytesPerScan & 3; |
334 | |
335 | if (accel->de_wait() != 0) |
336 | return -1; |
337 | |
338 | /* |
339 | * 2D Source Base. |
340 | * Use 0 for HOST Blt. |
341 | */ |
342 | write_dpr(accel, DE_WINDOW_SOURCE_BASE, regValue: 0); |
343 | |
344 | /* 2D Destination Base. |
345 | * It is an address offset (128 bit aligned) |
346 | * from the beginning of frame buffer. |
347 | */ |
348 | write_dpr(accel, DE_WINDOW_DESTINATION_BASE, regValue: dBase); |
349 | |
350 | /* |
351 | * Program pitch (distance between the 1st points of two adjacent |
352 | * lines). Note that input pitch is BYTE value, but the 2D Pitch |
353 | * register uses pixel values. Need Byte to pixel conversion. |
354 | */ |
355 | write_dpr(accel, DE_PITCH, |
356 | regValue: ((dPitch / bytePerPixel << DE_PITCH_DESTINATION_SHIFT) & |
357 | DE_PITCH_DESTINATION_MASK) | |
358 | (dPitch / bytePerPixel & DE_PITCH_SOURCE_MASK)); /* dpr10 */ |
359 | |
360 | /* |
361 | * Screen Window width in Pixels. |
362 | * 2D engine uses this value to calculate the linear address |
363 | * in frame buffer for a given point. |
364 | */ |
365 | write_dpr(accel, DE_WINDOW_WIDTH, |
366 | regValue: ((dPitch / bytePerPixel << DE_WINDOW_WIDTH_DST_SHIFT) & |
367 | DE_WINDOW_WIDTH_DST_MASK) | |
368 | (dPitch / bytePerPixel & DE_WINDOW_WIDTH_SRC_MASK)); |
369 | |
370 | /* |
371 | * Note: For 2D Source in Host Write, only X_K1_MONO field is needed, |
372 | * and Y_K2 field is not used. |
373 | * For mono bitmap, use startBit for X_K1. |
374 | */ |
375 | write_dpr(accel, DE_SOURCE, |
376 | regValue: (startBit << DE_SOURCE_X_K1_SHIFT) & |
377 | DE_SOURCE_X_K1_MONO_MASK); /* dpr00 */ |
378 | |
379 | write_dpr(accel, DE_DESTINATION, |
380 | regValue: ((dx << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) | |
381 | (dy & DE_DESTINATION_Y_MASK)); /* dpr04 */ |
382 | |
383 | write_dpr(accel, DE_DIMENSION, |
384 | regValue: ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) | |
385 | (height & DE_DIMENSION_Y_ET_MASK)); /* dpr08 */ |
386 | |
387 | write_dpr(accel, DE_FOREGROUND, regValue: fColor); |
388 | write_dpr(accel, DE_BACKGROUND, regValue: bColor); |
389 | |
390 | de_ctrl = (rop2 & DE_CONTROL_ROP_MASK) | |
391 | DE_CONTROL_ROP_SELECT | DE_CONTROL_COMMAND_HOST_WRITE | |
392 | DE_CONTROL_HOST | DE_CONTROL_STATUS; |
393 | |
394 | write_dpr(accel, DE_CONTROL, regValue: de_ctrl | deGetTransparency(accel)); |
395 | |
396 | /* Write MONO data (line by line) to 2D Engine data port */ |
397 | for (i = 0; i < height; i++) { |
398 | /* For each line, send the data in chunks of 4 bytes */ |
399 | for (j = 0; j < (ul4BytesPerScan / 4); j++) |
400 | write_dpPort(accel, data: *(unsigned int *)(pSrcbuf + (j * 4))); |
401 | |
402 | if (ulBytesRemain) { |
403 | memcpy(ajRemain, pSrcbuf + ul4BytesPerScan, |
404 | ulBytesRemain); |
405 | write_dpPort(accel, data: *(unsigned int *)ajRemain); |
406 | } |
407 | |
408 | pSrcbuf += srcDelta; |
409 | } |
410 | |
411 | return 0; |
412 | } |
413 | |
414 | |