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