xref: /linux/drivers/staging/sm750fb/sm750_accel.c (revision a5f22b9b139762685810aa5a41fd0181488aea13)
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 
18 #include "sm750.h"
19 #include "sm750_accel.h"
write_dpr(struct lynx_accel * accel,int offset,u32 reg_value)20 static inline void write_dpr(struct lynx_accel *accel, int offset, u32 reg_value)
21 {
22 	writel(reg_value, accel->dpr_base + offset);
23 }
24 
read_dpr(struct lynx_accel * accel,int offset)25 static inline u32 read_dpr(struct lynx_accel *accel, int offset)
26 {
27 	return readl(accel->dpr_base + offset);
28 }
29 
write_dp_port(struct lynx_accel * accel,u32 data)30 static inline void write_dp_port(struct lynx_accel *accel, u32 data)
31 {
32 	writel(data, accel->dp_port_base);
33 }
34 
sm750_hw_de_init(struct lynx_accel * accel)35 void sm750_hw_de_init(struct lynx_accel *accel)
36 {
37 	/* setup 2d engine registers */
38 	u32 reg, clr;
39 
40 	write_dpr(accel, DE_MASKS, 0xFFFFFFFF);
41 
42 	/* dpr1c */
43 	reg =  0x3;
44 
45 	clr = DE_STRETCH_FORMAT_PATTERN_XY |
46 	      DE_STRETCH_FORMAT_PATTERN_Y_MASK |
47 	      DE_STRETCH_FORMAT_PATTERN_X_MASK |
48 	      DE_STRETCH_FORMAT_ADDRESSING_MASK |
49 	      DE_STRETCH_FORMAT_SOURCE_HEIGHT_MASK;
50 
51 	/* DE_STRETCH bpp format need be initialized in setMode routine */
52 	write_dpr(accel, DE_STRETCH_FORMAT,
53 		  (read_dpr(accel, DE_STRETCH_FORMAT) & ~clr) | reg);
54 
55 	/* disable clipping and transparent */
56 	write_dpr(accel, DE_CLIP_TL, 0); /* dpr2c */
57 	write_dpr(accel, DE_CLIP_BR, 0); /* dpr30 */
58 
59 	write_dpr(accel, DE_COLOR_COMPARE_MASK, 0); /* dpr24 */
60 	write_dpr(accel, DE_COLOR_COMPARE, 0);
61 
62 	clr = DE_CONTROL_TRANSPARENCY | DE_CONTROL_TRANSPARENCY_MATCH |
63 		DE_CONTROL_TRANSPARENCY_SELECT;
64 
65 	/* dpr0c */
66 	write_dpr(accel, DE_CONTROL, read_dpr(accel, DE_CONTROL) & ~clr);
67 }
68 
69 /*
70  * set2dformat only be called from setmode functions
71  * but if you need dual framebuffer driver,need call set2dformat
72  * every time you use 2d function
73  */
74 
sm750_hw_set2dformat(struct lynx_accel * accel,int fmt)75 void sm750_hw_set2dformat(struct lynx_accel *accel, int fmt)
76 {
77 	u32 reg;
78 
79 	/* fmt=0,1,2 for 8,16,32,bpp on sm718/750/502 */
80 	reg = read_dpr(accel, DE_STRETCH_FORMAT);
81 	reg &= ~DE_STRETCH_FORMAT_PIXEL_FORMAT_MASK;
82 	reg |= ((fmt << DE_STRETCH_FORMAT_PIXEL_FORMAT_SHIFT) &
83 		DE_STRETCH_FORMAT_PIXEL_FORMAT_MASK);
84 	write_dpr(accel, DE_STRETCH_FORMAT, reg);
85 }
86 
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)87 int sm750_hw_fillrect(struct lynx_accel *accel,
88 		      u32 base, u32 pitch, u32 Bpp,
89 		      u32 x, u32 y, u32 width, u32 height,
90 		      u32 color, u32 rop)
91 {
92 	u32 de_ctrl;
93 
94 	if (accel->de_wait() != 0) {
95 		/*
96 		 * int time wait and always busy,seems hardware
97 		 * got something error
98 		 */
99 		pr_debug("De engine always busy\n");
100 		return -1;
101 	}
102 
103 	write_dpr(accel, DE_WINDOW_DESTINATION_BASE, base); /* dpr40 */
104 	write_dpr(accel, DE_PITCH,
105 		  ((pitch / Bpp << DE_PITCH_DESTINATION_SHIFT) &
106 		   DE_PITCH_DESTINATION_MASK) |
107 		  (pitch / Bpp & DE_PITCH_SOURCE_MASK)); /* dpr10 */
108 
109 	write_dpr(accel, DE_WINDOW_WIDTH,
110 		  ((pitch / Bpp << DE_WINDOW_WIDTH_DST_SHIFT) &
111 		   DE_WINDOW_WIDTH_DST_MASK) |
112 		   (pitch / Bpp & DE_WINDOW_WIDTH_SRC_MASK)); /* dpr44 */
113 
114 	write_dpr(accel, DE_FOREGROUND, color); /* DPR14 */
115 
116 	write_dpr(accel, DE_DESTINATION,
117 		  ((x << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) |
118 		  (y & DE_DESTINATION_Y_MASK)); /* dpr4 */
119 
120 	write_dpr(accel, DE_DIMENSION,
121 		  ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) |
122 		  (height & DE_DIMENSION_Y_ET_MASK)); /* dpr8 */
123 
124 	de_ctrl = DE_CONTROL_STATUS | DE_CONTROL_LAST_PIXEL |
125 		DE_CONTROL_COMMAND_RECTANGLE_FILL | DE_CONTROL_ROP_SELECT |
126 		(rop & DE_CONTROL_ROP_MASK); /* dpr0xc */
127 
128 	write_dpr(accel, DE_CONTROL, de_ctrl);
129 	return 0;
130 }
131 
132 /**
133  * sm750_hw_copyarea
134  * @accel: Acceleration device data
135  * @source_base: Address of source: offset in frame buffer
136  * @source_pitch: Pitch value of source surface in BYTE
137  * @sx: Starting x coordinate of source surface
138  * @sy: Starting y coordinate of source surface
139  * @dest_base: Address of destination: offset in frame buffer
140  * @dest_pitch: 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  */
sm750_hw_copyarea(struct lynx_accel * accel,unsigned int source_base,unsigned int source_pitch,unsigned int sx,unsigned int sy,unsigned int dest_base,unsigned int dest_pitch,unsigned int Bpp,unsigned int dx,unsigned int dy,unsigned int width,unsigned int height,unsigned int rop2)148 int sm750_hw_copyarea(struct lynx_accel *accel,
149 		      unsigned int source_base, unsigned int source_pitch,
150 		      unsigned int sx, unsigned int sy,
151 		      unsigned int dest_base, unsigned int dest_pitch,
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 direction, de_ctrl;
157 
158 	direction = 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 (source_base == dest_base && source_pitch == dest_pitch) {
164 		/* Determine direction of operation */
165 		if (sy < dy) {
166 			/*  +----------+
167 			 *  |S         |
168 			 *  |   +----------+
169 			 *  |   |      |   |
170 			 *  |   |      |   |
171 			 *  +---|------+   |
172 			 *	|         D|
173 			 *	+----------+
174 			 */
175 
176 			direction = BOTTOM_TO_TOP;
177 		} else if (sy > dy) {
178 			/*  +----------+
179 			 *  |D         |
180 			 *  |   +----------+
181 			 *  |   |      |   |
182 			 *  |   |      |   |
183 			 *  +---|------+   |
184 			 *	|         S|
185 			 *	+----------+
186 			 */
187 
188 			direction = TOP_TO_BOTTOM;
189 		} else {
190 			/* sy == dy */
191 
192 			if (sx <= dx) {
193 				/* +------+---+------+
194 				 * |S     |   |     D|
195 				 * |      |   |      |
196 				 * |      |   |      |
197 				 * |      |   |      |
198 				 * +------+---+------+
199 				 */
200 
201 				direction = RIGHT_TO_LEFT;
202 			} else {
203 			/* sx > dx */
204 
205 				/* +------+---+------+
206 				 * |D     |   |     S|
207 				 * |      |   |      |
208 				 * |      |   |      |
209 				 * |      |   |      |
210 				 * +------+---+------+
211 				 */
212 
213 				direction = LEFT_TO_RIGHT;
214 			}
215 		}
216 	}
217 
218 	if ((direction == BOTTOM_TO_TOP) || (direction == 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, source_base); /* 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, dest_base); /* 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 		  ((dest_pitch / Bpp << DE_PITCH_DESTINATION_SHIFT) &
253 		   DE_PITCH_DESTINATION_MASK) |
254 		  (source_pitch / 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 		  ((dest_pitch / Bpp << DE_WINDOW_WIDTH_DST_SHIFT) &
263 		   DE_WINDOW_WIDTH_DST_MASK) |
264 		  (source_pitch / 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 		((direction == 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 
de_get_transparency(struct lynx_accel * accel)287 static unsigned int de_get_transparency(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  * @accel: Acceleration device data
302  * @src_buf: pointer to start of source buffer in system memory
303  * @src_delta: Pitch value (in bytes) of the source buffer, +ive means top down
304  *	      and -ive mean button up
305  * @start_bit: Mono data can start at any bit in a byte, this value should be
306  *	      0 to 7
307  * @dest_base: Address of destination: offset in frame buffer
308  * @dest_pitch: Pitch value of destination surface in BYTE
309  * @byte_per_pixel: Color depth of destination surface
310  * @dx: Starting x coordinate of destination surface
311  * @dy: Starting y coordinate of destination surface
312  * @width: width of rectangle in pixel value
313  * @height: height of rectangle in pixel value
314  * @fg_color: Foreground color (corresponding to a 1 in the monochrome data
315  * @bg_color: Background color (corresponding to a 0 in the monochrome data
316  * @rop2: ROP value
317  */
sm750_hw_imageblit(struct lynx_accel * accel,const char * src_buf,u32 src_delta,u32 start_bit,u32 dest_base,u32 dest_pitch,u32 byte_per_pixel,u32 dx,u32 dy,u32 width,u32 height,u32 fg_color,u32 bg_color,u32 rop2)318 int sm750_hw_imageblit(struct lynx_accel *accel, const char *src_buf,
319 		       u32 src_delta, u32 start_bit, u32 dest_base, u32 dest_pitch,
320 		       u32 byte_per_pixel, u32 dx, u32 dy, u32 width,
321 		       u32 height, u32 fg_color, u32 bg_color, u32 rop2)
322 {
323 	unsigned int bytes_per_scan;
324 	unsigned int words_per_scan;
325 	unsigned int bytes_remain;
326 	unsigned int de_ctrl = 0;
327 	unsigned char remain[4];
328 	int i, j;
329 
330 	start_bit &= 7; /* Just make sure the start bit is within legal range */
331 	bytes_per_scan = (width + start_bit + 7) / 8;
332 	words_per_scan = bytes_per_scan & ~3;
333 	bytes_remain = bytes_per_scan & 3;
334 
335 	if (accel->de_wait() != 0)
336 		return -1;
337 
338 	/*
339 	 * 2D Source Base.
340 	 * Use 0 for HOST Blt.
341 	 */
342 	write_dpr(accel, DE_WINDOW_SOURCE_BASE, 0);
343 
344 	/* 2D Destination Base.
345 	 * It is an address offset (128 bit aligned)
346 	 * from the beginning of frame buffer.
347 	 */
348 	write_dpr(accel, DE_WINDOW_DESTINATION_BASE, dest_base);
349 
350 	/*
351 	 * Program pitch (distance between the 1st points of two adjacent
352 	 * lines). Note that input pitch is BYTE value, but the 2D Pitch
353 	 * register uses pixel values. Need Byte to pixel conversion.
354 	 */
355 	write_dpr(accel, DE_PITCH,
356 		  ((dest_pitch / byte_per_pixel << DE_PITCH_DESTINATION_SHIFT) &
357 		   DE_PITCH_DESTINATION_MASK) |
358 		  (dest_pitch / byte_per_pixel & DE_PITCH_SOURCE_MASK)); /* dpr10 */
359 
360 	/*
361 	 * Screen Window width in Pixels.
362 	 * 2D engine uses this value to calculate the linear address
363 	 * in frame buffer for a given point.
364 	 */
365 	write_dpr(accel, DE_WINDOW_WIDTH,
366 		  ((dest_pitch / byte_per_pixel << DE_WINDOW_WIDTH_DST_SHIFT) &
367 		   DE_WINDOW_WIDTH_DST_MASK) |
368 		  (dest_pitch / byte_per_pixel & DE_WINDOW_WIDTH_SRC_MASK));
369 
370 	 /*
371 	  * Note: For 2D Source in Host Write, only X_K1_MONO field is needed,
372 	  * and Y_K2 field is not used.
373 	  * For mono bitmap, use start_bit for X_K1.
374 	  */
375 	write_dpr(accel, DE_SOURCE,
376 		  (start_bit << DE_SOURCE_X_K1_SHIFT) &
377 		  DE_SOURCE_X_K1_MONO_MASK); /* dpr00 */
378 
379 	write_dpr(accel, DE_DESTINATION,
380 		  ((dx << DE_DESTINATION_X_SHIFT) & DE_DESTINATION_X_MASK) |
381 		  (dy & DE_DESTINATION_Y_MASK)); /* dpr04 */
382 
383 	write_dpr(accel, DE_DIMENSION,
384 		  ((width << DE_DIMENSION_X_SHIFT) & DE_DIMENSION_X_MASK) |
385 		  (height & DE_DIMENSION_Y_ET_MASK)); /* dpr08 */
386 
387 	write_dpr(accel, DE_FOREGROUND, fg_color);
388 	write_dpr(accel, DE_BACKGROUND, bg_color);
389 
390 	de_ctrl = (rop2 & DE_CONTROL_ROP_MASK) |
391 		DE_CONTROL_ROP_SELECT | DE_CONTROL_COMMAND_HOST_WRITE |
392 		DE_CONTROL_HOST | DE_CONTROL_STATUS;
393 
394 	write_dpr(accel, DE_CONTROL, de_ctrl | de_get_transparency(accel));
395 
396 	/* Write MONO data (line by line) to 2D Engine data port */
397 	for (i = 0; i < height; i++) {
398 		/* For each line, send the data in chunks of 4 bytes */
399 		for (j = 0; j < (words_per_scan / 4); j++)
400 			write_dp_port(accel, *(unsigned int *)(src_buf + (j * 4)));
401 
402 		if (bytes_remain) {
403 			memcpy(remain, src_buf + words_per_scan,
404 			       bytes_remain);
405 			write_dp_port(accel, *(unsigned int *)remain);
406 		}
407 
408 		src_buf += src_delta;
409 	}
410 
411 	return 0;
412 }
413 
414