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