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