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 regValue)20 static inline void write_dpr(struct lynx_accel *accel, int offset, u32 regValue) 21 { 22 writel(regValue, accel->dprBase + 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->dprBase + offset); 28 } 29 write_dpPort(struct lynx_accel * accel,u32 data)30 static inline void write_dpPort(struct lynx_accel *accel, u32 data) 31 { 32 writel(data, accel->dpPortBase); 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 deCtrl; 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 deCtrl = 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, deCtrl); 129 return 0; 130 } 131 132 /** 133 * sm750_hw_copyarea 134 * @accel: Acceleration device data 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 */ sm750_hw_copyarea(struct lynx_accel * accel,unsigned int sBase,unsigned int sPitch,unsigned int sx,unsigned int sy,unsigned int dBase,unsigned int dPitch,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 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 deGetTransparency(struct lynx_accel * accel)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 * @accel: Acceleration device data 302 * @pSrcbuf: pointer to start of source buffer in system memory 303 * @srcDelta: Pitch value (in bytes) of the source buffer, +ive means top down 304 * and -ive mean button up 305 * @startBit: Mono data can start at any bit in a byte, this value should be 306 * 0 to 7 307 * @dBase: Address of destination: offset in frame buffer 308 * @dPitch: Pitch value of destination surface in BYTE 309 * @bytePerPixel: 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 * @fColor: Foreground color (corresponding to a 1 in the monochrome data 315 * @bColor: Background color (corresponding to a 0 in the monochrome data 316 * @rop2: ROP value 317 */ sm750_hw_imageblit(struct lynx_accel * accel,const char * pSrcbuf,u32 srcDelta,u32 startBit,u32 dBase,u32 dPitch,u32 bytePerPixel,u32 dx,u32 dy,u32 width,u32 height,u32 fColor,u32 bColor,u32 rop2)318 int sm750_hw_imageblit(struct lynx_accel *accel, const char *pSrcbuf, 319 u32 srcDelta, u32 startBit, u32 dBase, u32 dPitch, 320 u32 bytePerPixel, u32 dx, u32 dy, u32 width, 321 u32 height, u32 fColor, u32 bColor, u32 rop2) 322 { 323 unsigned int ulBytesPerScan; 324 unsigned int ul4BytesPerScan; 325 unsigned int ulBytesRemain; 326 unsigned int de_ctrl = 0; 327 unsigned char ajRemain[4]; 328 int i, j; 329 330 startBit &= 7; /* Just make sure the start bit is within legal range */ 331 ulBytesPerScan = (width + startBit + 7) / 8; 332 ul4BytesPerScan = ulBytesPerScan & ~3; 333 ulBytesRemain = ulBytesPerScan & 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, dBase); 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 ((dPitch / bytePerPixel << DE_PITCH_DESTINATION_SHIFT) & 357 DE_PITCH_DESTINATION_MASK) | 358 (dPitch / bytePerPixel & 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 ((dPitch / bytePerPixel << DE_WINDOW_WIDTH_DST_SHIFT) & 367 DE_WINDOW_WIDTH_DST_MASK) | 368 (dPitch / bytePerPixel & 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 startBit for X_K1. 374 */ 375 write_dpr(accel, DE_SOURCE, 376 (startBit << 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, fColor); 388 write_dpr(accel, DE_BACKGROUND, bColor); 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 | deGetTransparency(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 < (ul4BytesPerScan / 4); j++) 400 write_dpPort(accel, *(unsigned int *)(pSrcbuf + (j * 4))); 401 402 if (ulBytesRemain) { 403 memcpy(ajRemain, pSrcbuf + ul4BytesPerScan, 404 ulBytesRemain); 405 write_dpPort(accel, *(unsigned int *)ajRemain); 406 } 407 408 pSrcbuf += srcDelta; 409 } 410 411 return 0; 412 } 413 414