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