1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc. 4 * Authors: York Sun <yorksun@freescale.com> 5 * Timur Tabi <timur@freescale.com> 6 * 7 * FSL DIU Framebuffer driver 8 */ 9 10 #include <common.h> 11 #include <malloc.h> 12 #include <asm/io.h> 13 14 #include "videomodes.h" 15 #include <video_fb.h> 16 #include <fsl_diu_fb.h> 17 #include <linux/list.h> 18 #include <linux/fb.h> 19 20 /* This setting is used for the ifm pdm360ng with PRIMEVIEW PM070WL3 */ 21 static struct fb_videomode fsl_diu_mode_800_480 = { 22 .name = "800x480-60", 23 .refresh = 60, 24 .xres = 800, 25 .yres = 480, 26 .pixclock = 31250, 27 .left_margin = 86, 28 .right_margin = 42, 29 .upper_margin = 33, 30 .lower_margin = 10, 31 .hsync_len = 128, 32 .vsync_len = 2, 33 .sync = 0, 34 .vmode = FB_VMODE_NONINTERLACED 35 }; 36 37 /* For the SHARP LQ084S3LG01, used on the P1022DS board */ 38 static struct fb_videomode fsl_diu_mode_800_600 = { 39 .name = "800x600-60", 40 .refresh = 60, 41 .xres = 800, 42 .yres = 600, 43 .pixclock = 25000, 44 .left_margin = 88, 45 .right_margin = 40, 46 .upper_margin = 23, 47 .lower_margin = 1, 48 .hsync_len = 128, 49 .vsync_len = 4, 50 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 51 .vmode = FB_VMODE_NONINTERLACED 52 }; 53 54 /* 55 * These parameters give default parameters 56 * for video output 1024x768, 57 * FIXME - change timing to proper amounts 58 * hsync 31.5kHz, vsync 60Hz 59 */ 60 static struct fb_videomode fsl_diu_mode_1024_768 = { 61 .name = "1024x768-60", 62 .refresh = 60, 63 .xres = 1024, 64 .yres = 768, 65 .pixclock = 15385, 66 .left_margin = 160, 67 .right_margin = 24, 68 .upper_margin = 29, 69 .lower_margin = 3, 70 .hsync_len = 136, 71 .vsync_len = 6, 72 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 73 .vmode = FB_VMODE_NONINTERLACED 74 }; 75 76 static struct fb_videomode fsl_diu_mode_1280_1024 = { 77 .name = "1280x1024-60", 78 .refresh = 60, 79 .xres = 1280, 80 .yres = 1024, 81 .pixclock = 9375, 82 .left_margin = 38, 83 .right_margin = 128, 84 .upper_margin = 2, 85 .lower_margin = 7, 86 .hsync_len = 216, 87 .vsync_len = 37, 88 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 89 .vmode = FB_VMODE_NONINTERLACED 90 }; 91 92 static struct fb_videomode fsl_diu_mode_1280_720 = { 93 .name = "1280x720-60", 94 .refresh = 60, 95 .xres = 1280, 96 .yres = 720, 97 .pixclock = 13426, 98 .left_margin = 192, 99 .right_margin = 64, 100 .upper_margin = 22, 101 .lower_margin = 1, 102 .hsync_len = 136, 103 .vsync_len = 3, 104 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 105 .vmode = FB_VMODE_NONINTERLACED 106 }; 107 108 static struct fb_videomode fsl_diu_mode_1920_1080 = { 109 .name = "1920x1080-60", 110 .refresh = 60, 111 .xres = 1920, 112 .yres = 1080, 113 .pixclock = 5787, 114 .left_margin = 328, 115 .right_margin = 120, 116 .upper_margin = 34, 117 .lower_margin = 1, 118 .hsync_len = 208, 119 .vsync_len = 3, 120 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 121 .vmode = FB_VMODE_NONINTERLACED 122 }; 123 124 /* 125 * These are the fields of area descriptor(in DDR memory) for every plane 126 */ 127 struct diu_ad { 128 /* Word 0(32-bit) in DDR memory */ 129 __le32 pix_fmt; /* hard coding pixel format */ 130 /* Word 1(32-bit) in DDR memory */ 131 __le32 addr; 132 /* Word 2(32-bit) in DDR memory */ 133 __le32 src_size_g_alpha; 134 /* Word 3(32-bit) in DDR memory */ 135 __le32 aoi_size; 136 /* Word 4(32-bit) in DDR memory */ 137 __le32 offset_xyi; 138 /* Word 5(32-bit) in DDR memory */ 139 __le32 offset_xyd; 140 /* Word 6(32-bit) in DDR memory */ 141 __le32 ckmax_r:8; 142 __le32 ckmax_g:8; 143 __le32 ckmax_b:8; 144 __le32 res9:8; 145 /* Word 7(32-bit) in DDR memory */ 146 __le32 ckmin_r:8; 147 __le32 ckmin_g:8; 148 __le32 ckmin_b:8; 149 __le32 res10:8; 150 /* Word 8(32-bit) in DDR memory */ 151 __le32 next_ad; 152 /* Word 9(32-bit) in DDR memory, just for 64-bit aligned */ 153 __le32 res[3]; 154 } __attribute__ ((packed)); 155 156 /* 157 * DIU register map 158 */ 159 struct diu { 160 __be32 desc[3]; 161 __be32 gamma; 162 __be32 pallete; 163 __be32 cursor; 164 __be32 curs_pos; 165 __be32 diu_mode; 166 __be32 bgnd; 167 __be32 bgnd_wb; 168 __be32 disp_size; 169 __be32 wb_size; 170 __be32 wb_mem_addr; 171 __be32 hsyn_para; 172 __be32 vsyn_para; 173 __be32 syn_pol; 174 __be32 thresholds; 175 __be32 int_status; 176 __be32 int_mask; 177 __be32 colorbar[8]; 178 __be32 filling; 179 __be32 plut; 180 } __attribute__ ((packed)); 181 182 struct diu_addr { 183 void *vaddr; /* Virtual address */ 184 u32 paddr; /* 32-bit physical address */ 185 unsigned int offset; /* Alignment offset */ 186 }; 187 188 static struct fb_info info; 189 190 /* 191 * Align to 64-bit(8-byte), 32-byte, etc. 192 */ 193 static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) 194 { 195 u32 offset, ssize; 196 u32 mask; 197 198 ssize = size + bytes_align; 199 buf->vaddr = malloc(ssize); 200 if (!buf->vaddr) 201 return -1; 202 203 memset(buf->vaddr, 0, ssize); 204 mask = bytes_align - 1; 205 offset = (u32)buf->vaddr & mask; 206 if (offset) { 207 buf->offset = bytes_align - offset; 208 buf->vaddr += offset; 209 } else 210 buf->offset = 0; 211 212 buf->paddr = virt_to_phys(buf->vaddr); 213 return 0; 214 } 215 216 /* 217 * Allocate a framebuffer and an Area Descriptor that points to it. Both 218 * are created in the same memory block. The Area Descriptor is updated to 219 * point to the framebuffer memory. Memory is aligned as needed. 220 */ 221 static struct diu_ad *allocate_fb(unsigned int xres, unsigned int yres, 222 unsigned int depth, char **fb) 223 { 224 unsigned long size = xres * yres * depth; 225 struct diu_addr addr; 226 struct diu_ad *ad; 227 size_t ad_size = roundup(sizeof(struct diu_ad), 32); 228 229 /* 230 * Allocate a memory block that holds the Area Descriptor and the 231 * frame buffer right behind it. To keep the code simple, everything 232 * is aligned on a 32-byte address. 233 */ 234 if (allocate_buf(&addr, ad_size + size, 32) < 0) 235 return NULL; 236 237 ad = addr.vaddr; 238 ad->addr = cpu_to_le32(addr.paddr + ad_size); 239 ad->aoi_size = cpu_to_le32((yres << 16) | xres); 240 ad->src_size_g_alpha = cpu_to_le32((yres << 12) | xres); 241 ad->offset_xyi = 0; 242 ad->offset_xyd = 0; 243 244 if (fb) 245 *fb = addr.vaddr + ad_size; 246 247 return ad; 248 } 249 250 int fsl_diu_init(u16 xres, u16 yres, u32 pixel_format, int gamma_fix) 251 { 252 struct fb_videomode *fsl_diu_mode_db; 253 struct diu_ad *ad; 254 struct diu *hw = (struct diu *)CONFIG_SYS_DIU_ADDR; 255 u8 *gamma_table_base; 256 unsigned int i, j; 257 struct diu_addr gamma; 258 struct diu_addr cursor; 259 260 /* Convert the X,Y resolution pair into a single number */ 261 #define RESOLUTION(x, y) (((u32)(x) << 16) | (y)) 262 263 switch (RESOLUTION(xres, yres)) { 264 case RESOLUTION(800, 480): 265 fsl_diu_mode_db = &fsl_diu_mode_800_480; 266 break; 267 case RESOLUTION(800, 600): 268 fsl_diu_mode_db = &fsl_diu_mode_800_600; 269 break; 270 case RESOLUTION(1024, 768): 271 fsl_diu_mode_db = &fsl_diu_mode_1024_768; 272 break; 273 case RESOLUTION(1280, 1024): 274 fsl_diu_mode_db = &fsl_diu_mode_1280_1024; 275 break; 276 case RESOLUTION(1280, 720): 277 fsl_diu_mode_db = &fsl_diu_mode_1280_720; 278 break; 279 case RESOLUTION(1920, 1080): 280 fsl_diu_mode_db = &fsl_diu_mode_1920_1080; 281 break; 282 default: 283 printf("DIU: Unsupported resolution %ux%u\n", xres, yres); 284 return -1; 285 } 286 287 /* read mode info */ 288 info.var.xres = fsl_diu_mode_db->xres; 289 info.var.yres = fsl_diu_mode_db->yres; 290 info.var.bits_per_pixel = 32; 291 info.var.pixclock = fsl_diu_mode_db->pixclock; 292 info.var.left_margin = fsl_diu_mode_db->left_margin; 293 info.var.right_margin = fsl_diu_mode_db->right_margin; 294 info.var.upper_margin = fsl_diu_mode_db->upper_margin; 295 info.var.lower_margin = fsl_diu_mode_db->lower_margin; 296 info.var.hsync_len = fsl_diu_mode_db->hsync_len; 297 info.var.vsync_len = fsl_diu_mode_db->vsync_len; 298 info.var.sync = fsl_diu_mode_db->sync; 299 info.var.vmode = fsl_diu_mode_db->vmode; 300 info.fix.line_length = info.var.xres * info.var.bits_per_pixel / 8; 301 302 /* Memory allocation for framebuffer */ 303 info.screen_size = 304 info.var.xres * info.var.yres * (info.var.bits_per_pixel / 8); 305 ad = allocate_fb(info.var.xres, info.var.yres, 306 info.var.bits_per_pixel / 8, &info.screen_base); 307 if (!ad) { 308 printf("DIU: Out of memory\n"); 309 return -1; 310 } 311 312 ad->pix_fmt = pixel_format; 313 314 /* Disable chroma keying function */ 315 ad->ckmax_r = 0; 316 ad->ckmax_g = 0; 317 ad->ckmax_b = 0; 318 319 ad->ckmin_r = 255; 320 ad->ckmin_g = 255; 321 ad->ckmin_b = 255; 322 323 /* Initialize the gamma table */ 324 if (allocate_buf(&gamma, 256 * 3, 32) < 0) { 325 printf("DIU: Out of memory\n"); 326 return -1; 327 } 328 gamma_table_base = gamma.vaddr; 329 for (i = 0; i <= 2; i++) 330 for (j = 0; j < 256; j++) 331 *gamma_table_base++ = j; 332 333 if (gamma_fix == 1) { /* fix the gamma */ 334 gamma_table_base = gamma.vaddr; 335 for (i = 0; i < 256 * 3; i++) { 336 gamma_table_base[i] = (gamma_table_base[i] << 2) 337 | ((gamma_table_base[i] >> 6) & 0x03); 338 } 339 } 340 341 /* Initialize the cursor */ 342 if (allocate_buf(&cursor, 32 * 32 * 2, 32) < 0) { 343 printf("DIU: Can't alloc cursor data\n"); 344 return -1; 345 } 346 347 /* Program DIU registers */ 348 out_be32(&hw->diu_mode, 0); /* Temporarily disable the DIU */ 349 350 out_be32(&hw->gamma, gamma.paddr); 351 out_be32(&hw->cursor, cursor.paddr); 352 out_be32(&hw->bgnd, 0x007F7F7F); 353 out_be32(&hw->disp_size, info.var.yres << 16 | info.var.xres); 354 out_be32(&hw->hsyn_para, info.var.left_margin << 22 | 355 info.var.hsync_len << 11 | 356 info.var.right_margin); 357 358 out_be32(&hw->vsyn_para, info.var.upper_margin << 22 | 359 info.var.vsync_len << 11 | 360 info.var.lower_margin); 361 362 /* Pixel Clock configuration */ 363 diu_set_pixel_clock(info.var.pixclock); 364 365 /* Set the frame buffers */ 366 out_be32(&hw->desc[0], virt_to_phys(ad)); 367 out_be32(&hw->desc[1], 0); 368 out_be32(&hw->desc[2], 0); 369 370 /* Enable the DIU, set display to all three planes */ 371 out_be32(&hw->diu_mode, 1); 372 373 return 0; 374 } 375 376 void *video_hw_init(void) 377 { 378 static GraphicDevice ctfb; 379 const char *options; 380 unsigned int depth = 0, freq = 0; 381 382 if (!video_get_video_mode(&ctfb.winSizeX, &ctfb.winSizeY, &depth, &freq, 383 &options)) 384 return NULL; 385 386 /* Find the monitor port, which is a required option */ 387 if (!options) 388 return NULL; 389 if (strncmp(options, "monitor=", 8) != 0) 390 return NULL; 391 392 if (platform_diu_init(ctfb.winSizeX, ctfb.winSizeY, options + 8) < 0) 393 return NULL; 394 395 /* fill in Graphic device struct */ 396 sprintf(ctfb.modeIdent, "%ix%ix%i %ikHz %iHz", 397 ctfb.winSizeX, ctfb.winSizeY, depth, 64, freq); 398 399 ctfb.frameAdrs = (unsigned int)info.screen_base; 400 ctfb.plnSizeX = ctfb.winSizeX; 401 ctfb.plnSizeY = ctfb.winSizeY; 402 403 ctfb.gdfBytesPP = 4; 404 ctfb.gdfIndex = GDF_32BIT_X888RGB; 405 406 ctfb.isaBase = 0; 407 ctfb.pciBase = 0; 408 ctfb.memSize = info.screen_size; 409 410 /* Cursor Start Address */ 411 ctfb.dprBase = 0; 412 ctfb.vprBase = 0; 413 ctfb.cprBase = 0; 414 415 return &ctfb; 416 } 417