xref: /linux/drivers/staging/sm750fb/sm750_accel.c (revision 0be3ff0c)
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_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  */
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 
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  */
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