xref: /linux/drivers/staging/sm750fb/sm750_accel.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
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"
21 static inline void write_dpr(struct lynx_accel *accel, int offset, u32 regValue)
22 {
23 	writel(regValue, accel->dprBase + offset);
24 }
25 
26 static inline u32 read_dpr(struct lynx_accel *accel, int offset)
27 {
28 	return readl(accel->dprBase + offset);
29 }
30 
31 static inline void write_dpPort(struct lynx_accel *accel, u32 data)
32 {
33 	writel(data, accel->dpPortBase);
34 }
35 
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 
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 
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 int sm750_hw_copyarea(
134 struct lynx_accel *accel,
135 unsigned int sBase,  /* Address of source: offset in frame buffer */
136 unsigned int sPitch, /* Pitch value of source surface in BYTE */
137 unsigned int sx,
138 unsigned int sy,     /* Starting coordinate of source surface */
139 unsigned int dBase,  /* Address of destination: offset in frame buffer */
140 unsigned int dPitch, /* Pitch value of destination surface in BYTE */
141 unsigned int Bpp,    /* Color depth of destination surface */
142 unsigned int dx,
143 unsigned int dy,     /* Starting coordinate of destination surface */
144 unsigned int width,
145 unsigned int height, /* width and height of rectangle in pixel value */
146 unsigned int rop2)   /* ROP value */
147 {
148 	unsigned int nDirection, de_ctrl;
149 
150 	nDirection = LEFT_TO_RIGHT;
151 	/* Direction of ROP2 operation: 1 = Left to Right, (-1) = Right to Left */
152 	de_ctrl = 0;
153 
154 	/* If source and destination are the same surface, need to check for overlay cases */
155 	if (sBase == dBase && sPitch == dPitch) {
156 		/* Determine direction of operation */
157 		if (sy < dy) {
158 			/*  +----------+
159 			 *  |S         |
160 			 *  |   +----------+
161 			 *  |   |      |   |
162 			 *  |   |      |   |
163 			 *  +---|------+   |
164 			 *	|         D|
165 			 *	+----------+
166 			 */
167 
168 			nDirection = BOTTOM_TO_TOP;
169 		} else if (sy > dy) {
170 			/*  +----------+
171 			 *  |D         |
172 			 *  |   +----------+
173 			 *  |   |      |   |
174 			 *  |   |      |   |
175 			 *  +---|------+   |
176 			 *	|         S|
177 			 *	+----------+
178 			 */
179 
180 			nDirection = TOP_TO_BOTTOM;
181 		} else {
182 			/* sy == dy */
183 
184 			if (sx <= dx) {
185 				/* +------+---+------+
186 				 * |S     |   |     D|
187 				 * |      |   |      |
188 				 * |      |   |      |
189 				 * |      |   |      |
190 				 * +------+---+------+
191 				 */
192 
193 				nDirection = RIGHT_TO_LEFT;
194 			} else {
195 			/* sx > dx */
196 
197 				/* +------+---+------+
198 				 * |D     |   |     S|
199 				 * |      |   |      |
200 				 * |      |   |      |
201 				 * |      |   |      |
202 				 * +------+---+------+
203 				 */
204 
205 				nDirection = LEFT_TO_RIGHT;
206 			}
207 		}
208 	}
209 
210 	if ((nDirection == BOTTOM_TO_TOP) || (nDirection == RIGHT_TO_LEFT)) {
211 		sx += width - 1;
212 		sy += height - 1;
213 		dx += width - 1;
214 		dy += height - 1;
215 	}
216 
217 	/*
218 	 * Note:
219 	 * DE_FOREGROUND are DE_BACKGROUND are don't care.
220 	 * DE_COLOR_COMPARE and DE_COLOR_COMPARE_MAKS
221 	 * are set by set deSetTransparency().
222 	 */
223 
224 	/*
225 	 * 2D Source Base.
226 	 * It is an address offset (128 bit aligned)
227 	 * from the beginning of frame buffer.
228 	 */
229 	write_dpr(accel, DE_WINDOW_SOURCE_BASE, sBase); /* dpr40 */
230 
231 	/*
232 	 * 2D Destination Base.
233 	 * It is an address offset (128 bit aligned)
234 	 * from the beginning of frame buffer.
235 	 */
236 	write_dpr(accel, DE_WINDOW_DESTINATION_BASE, dBase); /* dpr44 */
237 
238     /*
239      * Program pitch (distance between the 1st points of two adjacent lines).
240      * Note that input pitch is BYTE value, but the 2D Pitch register uses
241      * pixel values. Need Byte to pixel conversion.
242      */
243 	write_dpr(accel, DE_PITCH,
244 		  ((dPitch / Bpp << DE_PITCH_DESTINATION_SHIFT) &
245 		   DE_PITCH_DESTINATION_MASK) |
246 		  (sPitch / Bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */
247 
248     /*
249      * Screen Window width in Pixels.
250      * 2D engine uses this value to calculate the linear address in frame buffer
251      * for a given point.
252      */
253 	write_dpr(accel, DE_WINDOW_WIDTH,
254 		  ((dPitch / Bpp << DE_WINDOW_WIDTH_DST_SHIFT) &
255 		   DE_WINDOW_WIDTH_DST_MASK) |
256 		  (sPitch / Bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr3c */
257 
258 	if (accel->de_wait() != 0)
259 		return -1;
260 
261 	write_dpr(accel, DE_SOURCE,
262 		  ((sx << DE_SOURCE_X_K1_SHIFT) & DE_SOURCE_X_K1_MASK) |
263 		  (sy & DE_SOURCE_Y_K2_MASK)); /* dpr0 */
264 	write_dpr(accel, DE_DESTINATION,
265 		  ((dx << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) |
266 		  (dy & DE_DESTINATION_Y_MASK)); /* dpr04 */
267 	write_dpr(accel, DE_DIMENSION,
268 		  ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) |
269 		  (height & DE_DIMENSION_Y_ET_MASK)); /* dpr08 */
270 
271 	de_ctrl = (rop2 & DE_CONTROL_ROP_MASK) | DE_CONTROL_ROP_SELECT |
272 		((nDirection == RIGHT_TO_LEFT) ? DE_CONTROL_DIRECTION : 0) |
273 		DE_CONTROL_COMMAND_BITBLT | DE_CONTROL_STATUS;
274 	write_dpr(accel, DE_CONTROL, de_ctrl); /* dpr0c */
275 
276 	return 0;
277 }
278 
279 static unsigned int deGetTransparency(struct lynx_accel *accel)
280 {
281 	unsigned int de_ctrl;
282 
283 	de_ctrl = read_dpr(accel, DE_CONTROL);
284 
285 	de_ctrl &= (DE_CONTROL_TRANSPARENCY_MATCH |
286 		    DE_CONTROL_TRANSPARENCY_SELECT | DE_CONTROL_TRANSPARENCY);
287 
288 	return de_ctrl;
289 }
290 
291 int sm750_hw_imageblit(struct lynx_accel *accel,
292 		 const char *pSrcbuf, /* pointer to start of source buffer in system memory */
293 		 u32 srcDelta,          /* Pitch value (in bytes) of the source buffer, +ive means top down and -ive mean button up */
294 		 u32 startBit, /* Mono data can start at any bit in a byte, this value should be 0 to 7 */
295 		 u32 dBase,    /* Address of destination: offset in frame buffer */
296 		 u32 dPitch,   /* Pitch value of destination surface in BYTE */
297 		 u32 bytePerPixel,      /* Color depth of destination surface */
298 		 u32 dx,
299 		 u32 dy,       /* Starting coordinate of destination surface */
300 		 u32 width,
301 		 u32 height,   /* width and height of rectangle in pixel value */
302 		 u32 fColor,   /* Foreground color (corresponding to a 1 in the monochrome data */
303 		 u32 bColor,   /* Background color (corresponding to a 0 in the monochrome data */
304 		 u32 rop2)     /* ROP value */
305 {
306 	unsigned int ulBytesPerScan;
307 	unsigned int ul4BytesPerScan;
308 	unsigned int ulBytesRemain;
309 	unsigned int de_ctrl = 0;
310 	unsigned char ajRemain[4];
311 	int i, j;
312 
313 	startBit &= 7; /* Just make sure the start bit is within legal range */
314 	ulBytesPerScan = (width + startBit + 7) / 8;
315 	ul4BytesPerScan = ulBytesPerScan & ~3;
316 	ulBytesRemain = ulBytesPerScan & 3;
317 
318 	if (accel->de_wait() != 0)
319 		return -1;
320 
321 	/*
322 	 * 2D Source Base.
323 	 * Use 0 for HOST Blt.
324 	 */
325 	write_dpr(accel, DE_WINDOW_SOURCE_BASE, 0);
326 
327 	/* 2D Destination Base.
328 	 * It is an address offset (128 bit aligned)
329 	 * from the beginning of frame buffer.
330 	 */
331 	write_dpr(accel, DE_WINDOW_DESTINATION_BASE, dBase);
332 
333 	/*
334 	 * Program pitch (distance between the 1st points of two adjacent
335 	 * lines). Note that input pitch is BYTE value, but the 2D Pitch
336 	 * register uses pixel values. Need Byte to pixel conversion.
337 	 */
338 	write_dpr(accel, DE_PITCH,
339 		  ((dPitch / bytePerPixel << DE_PITCH_DESTINATION_SHIFT) &
340 		   DE_PITCH_DESTINATION_MASK) |
341 		  (dPitch / bytePerPixel & DE_PITCH_SOURCE_MASK)); /* dpr10 */
342 
343 	/*
344 	 * Screen Window width in Pixels.
345 	 * 2D engine uses this value to calculate the linear address
346 	 * in frame buffer for a given point.
347 	 */
348 	write_dpr(accel, DE_WINDOW_WIDTH,
349 		  ((dPitch / bytePerPixel << DE_WINDOW_WIDTH_DST_SHIFT) &
350 		   DE_WINDOW_WIDTH_DST_MASK) |
351 		  (dPitch / bytePerPixel & DE_WINDOW_WIDTH_SRC_MASK));
352 
353 	 /*
354 	  * Note: For 2D Source in Host Write, only X_K1_MONO field is needed,
355 	  * and Y_K2 field is not used.
356 	  * For mono bitmap, use startBit for X_K1.
357 	  */
358 	write_dpr(accel, DE_SOURCE,
359 		  (startBit << DE_SOURCE_X_K1_SHIFT) &
360 		  DE_SOURCE_X_K1_MONO_MASK); /* dpr00 */
361 
362 	write_dpr(accel, DE_DESTINATION,
363 		  ((dx << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) |
364 		  (dy & DE_DESTINATION_Y_MASK)); /* dpr04 */
365 
366 	write_dpr(accel, DE_DIMENSION,
367 		  ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) |
368 		  (height & DE_DIMENSION_Y_ET_MASK)); /* dpr08 */
369 
370 	write_dpr(accel, DE_FOREGROUND, fColor);
371 	write_dpr(accel, DE_BACKGROUND, bColor);
372 
373 	de_ctrl = (rop2 & DE_CONTROL_ROP_MASK) |
374 		DE_CONTROL_ROP_SELECT | DE_CONTROL_COMMAND_HOST_WRITE |
375 		DE_CONTROL_HOST | DE_CONTROL_STATUS;
376 
377 	write_dpr(accel, DE_CONTROL, de_ctrl | deGetTransparency(accel));
378 
379 	/* Write MONO data (line by line) to 2D Engine data port */
380 	for (i = 0; i < height; i++) {
381 		/* For each line, send the data in chunks of 4 bytes */
382 		for (j = 0; j < (ul4BytesPerScan / 4); j++)
383 			write_dpPort(accel, *(unsigned int *)(pSrcbuf + (j * 4)));
384 
385 		if (ulBytesRemain) {
386 			memcpy(ajRemain, pSrcbuf + ul4BytesPerScan,
387 			       ulBytesRemain);
388 			write_dpPort(accel, *(unsigned int *)ajRemain);
389 		}
390 
391 		pSrcbuf += srcDelta;
392 	}
393 
394 	return 0;
395 }
396 
397