xref: /linux/drivers/staging/sm750fb/sm750_accel.c (revision 9a6b55ac)
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 /**
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)
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, 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, 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 		  ((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 		  ((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 		  ((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 		  ((dx << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) |
274 		  (dy & DE_DESTINATION_Y_MASK)); /* dpr04 */
275 	write_dpr(accel, DE_DIMENSION,
276 		  ((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, 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  * @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)
321 {
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;
333 
334 	if (accel->de_wait() != 0)
335 		return -1;
336 
337 	/*
338 	 * 2D Source Base.
339 	 * Use 0 for HOST Blt.
340 	 */
341 	write_dpr(accel, DE_WINDOW_SOURCE_BASE, 0);
342 
343 	/* 2D Destination Base.
344 	 * It is an address offset (128 bit aligned)
345 	 * from the beginning of frame buffer.
346 	 */
347 	write_dpr(accel, DE_WINDOW_DESTINATION_BASE, dBase);
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 	 */
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 */
358 
359 	/*
360 	 * Screen Window width in Pixels.
361 	 * 2D engine uses this value to calculate the linear address
362 	 * in frame buffer for a given point.
363 	 */
364 	write_dpr(accel, DE_WINDOW_WIDTH,
365 		  ((dPitch / bytePerPixel << DE_WINDOW_WIDTH_DST_SHIFT) &
366 		   DE_WINDOW_WIDTH_DST_MASK) |
367 		  (dPitch / bytePerPixel & DE_WINDOW_WIDTH_SRC_MASK));
368 
369 	 /*
370 	  * Note: For 2D Source in Host Write, only X_K1_MONO field is needed,
371 	  * and Y_K2 field is not used.
372 	  * For mono bitmap, use startBit for X_K1.
373 	  */
374 	write_dpr(accel, DE_SOURCE,
375 		  (startBit << DE_SOURCE_X_K1_SHIFT) &
376 		  DE_SOURCE_X_K1_MONO_MASK); /* dpr00 */
377 
378 	write_dpr(accel, DE_DESTINATION,
379 		  ((dx << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) |
380 		  (dy & DE_DESTINATION_Y_MASK)); /* dpr04 */
381 
382 	write_dpr(accel, DE_DIMENSION,
383 		  ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) |
384 		  (height & DE_DIMENSION_Y_ET_MASK)); /* dpr08 */
385 
386 	write_dpr(accel, DE_FOREGROUND, fColor);
387 	write_dpr(accel, DE_BACKGROUND, bColor);
388 
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;
392 
393 	write_dpr(accel, DE_CONTROL, de_ctrl | deGetTransparency(accel));
394 
395 	/* Write MONO data (line by line) to 2D Engine data port */
396 	for (i = 0; i < height; i++) {
397 		/* For each line, send the data in chunks of 4 bytes */
398 		for (j = 0; j < (ul4BytesPerScan / 4); j++)
399 			write_dpPort(accel, *(unsigned int *)(pSrcbuf + (j * 4)));
400 
401 		if (ulBytesRemain) {
402 			memcpy(ajRemain, pSrcbuf + ul4BytesPerScan,
403 			       ulBytesRemain);
404 			write_dpPort(accel, *(unsigned int *)ajRemain);
405 		}
406 
407 		pSrcbuf += srcDelta;
408 	}
409 
410 	return 0;
411 }
412 
413