1 /* Architecture dependent part for the framebuffer on the OMAP3. 2 * There's obvious room for improvement. 3 */ 4 5 #include <minix/chardriver.h> 6 #include <minix/drivers.h> 7 #include <minix/fb.h> 8 #include <minix/type.h> 9 #include <minix/vm.h> 10 #include <minix/log.h> 11 #include <assert.h> 12 #include <sys/mman.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <stdint.h> 16 #include <dev/videomode/videomode.h> 17 #include <dev/videomode/edidvar.h> 18 #include <dev/videomode/edidreg.h> 19 #include "dss.h" 20 #include "fb.h" 21 22 /* default / fallback resolution if EDID reading fails */ 23 #define SCREEN_WIDTH 1024 24 #define SCREEN_HEIGHT 600 25 #define PAGES_NR 2 26 27 #define NSUPPORTED_MODES (4) 28 29 /* List of valid modes from TRM 7.1 30 * Other modes might work (like the default 1024x600), but no guarantees. 31 */ 32 struct supported_modes { 33 int hdisplay; 34 int vdisplay; 35 } omap_supported_modes[NSUPPORTED_MODES] = { 36 { .hdisplay = 1024, .vdisplay = 768 }, /* XGA */ 37 { .hdisplay = 1280, .vdisplay = 800 }, /* WXGA */ 38 { .hdisplay = 1400, .vdisplay = 1050 }, /* SXGA+ */ 39 { .hdisplay = 1280, .vdisplay = 720 } /* HD 720p */ 40 }; 41 42 /* local function prototypes */ 43 static struct videomode *choose_mode(struct edid_info *info); 44 static void configure_with_defaults(int minor); 45 static int configure_with_edid(int minor, struct edid_info *info); 46 47 /* globals */ 48 static vir_bytes dss_phys_base; /* Address of dss phys memory map */ 49 static vir_bytes dispc_phys_base; /* Address of dispc phys memory map */ 50 static vir_bytes fb_vir; 51 static phys_bytes fb_phys; 52 static size_t fb_size; 53 static int initialized = 0; 54 55 struct panel_config { 56 u32_t timing_h; 57 u32_t timing_v; 58 u32_t pol_freq; 59 u32_t divisor; 60 u32_t lcd_size; 61 u32_t panel_type; 62 u32_t data_lines; 63 u32_t load_mode; 64 u32_t panel_color; 65 }; 66 67 static const struct panel_config default_cfg = { 68 /* See OMAP TRM section 15.7 for the register values/encoding */ 69 .timing_h = 0x1a4024c9, /* Horizontal timing */ 70 .timing_v = 0x02c00509, /* Vertical timing */ 71 .pol_freq = 0x00007028, /* Pol Freq */ 72 .divisor = 0x00010001, /* 96MHz Pixel Clock */ 73 .lcd_size = ((SCREEN_HEIGHT - 1) << 16 | (SCREEN_WIDTH - 1)), 74 .panel_type = 0x01, /* TFT */ 75 .data_lines = 0x03, /* 24 Bit RGB */ 76 .load_mode = 0x02, /* Frame Mode */ 77 .panel_color = 0xFFFFFF /* WHITE */ 78 }; 79 80 static struct panel_config omap_cfg[FB_DEV_NR]; 81 82 static const struct fb_fix_screeninfo default_fbfs = { 83 .xpanstep = 0, 84 .ypanstep = 0, 85 .ywrapstep = 0, 86 .line_length = SCREEN_WIDTH * 4, 87 .mmio_start = 0, /* Not implemented for char. special, so */ 88 .mmio_len = 0 /* these are set to 0 */ 89 }; 90 91 static struct fb_fix_screeninfo omap_fbfs[FB_DEV_NR]; 92 93 static const struct fb_var_screeninfo default_fbvs = { 94 .xres = SCREEN_WIDTH, 95 .yres = SCREEN_HEIGHT, 96 .xres_virtual = SCREEN_WIDTH, 97 .yres_virtual = SCREEN_HEIGHT*2, 98 .xoffset = 0, 99 .yoffset = 0, 100 .bits_per_pixel = 32, 101 .red = { 102 .offset = 16, 103 .length = 8, 104 .msb_right = 0 105 }, 106 .green = { 107 .offset = 8, 108 .length = 8, 109 .msb_right = 0 110 }, 111 .blue = { 112 .offset = 0, 113 .length = 8, 114 .msb_right = 0 115 }, 116 .transp = { 117 .offset = 24, 118 .length = 8, 119 .msb_right = 0 120 } 121 }; 122 123 static struct fb_var_screeninfo omap_fbvs[FB_DEV_NR]; 124 125 /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */ 126 static struct log log = { 127 .name = "fb", 128 .log_level = LEVEL_INFO, 129 .log_func = default_log 130 }; 131 132 static inline u32_t 133 readw(vir_bytes addr) 134 { 135 return *((volatile u32_t *) addr); 136 } 137 138 static inline void 139 writew(vir_bytes addr, u32_t val) 140 { 141 *((volatile u32_t *) addr) = val; 142 } 143 144 static struct videomode * 145 choose_mode(struct edid_info *info) 146 { 147 int i, j; 148 149 /* choose the highest resolution supported by both the SoC and screen */ 150 for (i = info->edid_nmodes - 1; i >= 0; i--) { 151 for (j = NSUPPORTED_MODES - 1; j >= 0; j--) { 152 153 if (info->edid_modes[i].hdisplay == 154 omap_supported_modes[j].hdisplay && 155 info->edid_modes[i].vdisplay == 156 omap_supported_modes[j].vdisplay) { 157 158 return &(info->edid_modes[i]); 159 } 160 } 161 } 162 163 return NULL; 164 } 165 166 static int 167 configure_with_edid(int minor, struct edid_info *info) 168 { 169 struct videomode *mode; 170 171 if (info == NULL || minor < 0 || minor >= FB_DEV_NR) { 172 log_warn(&log, "Invalid minor #%d or info == NULL\n", minor); 173 return -1; 174 } 175 176 /* If debugging or tracing, print the contents of info */ 177 if (log.log_level >= LEVEL_DEBUG) { 178 log_debug(&log, "--- EDID - START ---\n"); 179 edid_print(info); 180 log_debug(&log, "--- EDID - END ---\n"); 181 } 182 183 /* Choose the preferred mode. */ 184 mode = choose_mode(info); 185 if (mode == NULL) { 186 log_warn(&log, "Couldn't find a supported resolution.\n"); 187 return -1; 188 } 189 190 /* 191 * apply the default settings since we don't overwrite every field 192 */ 193 configure_with_defaults(minor); 194 195 /* 196 * apply the settings corresponding to the given EDID 197 */ 198 199 /* panel_config */ 200 omap_cfg[minor].lcd_size = ((mode->vdisplay - 1) << 16 | (mode->hdisplay - 1)); 201 202 if (EDID_FEATURES_DISP_TYPE(info->edid_features) == 203 EDID_FEATURES_DISP_TYPE_MONO) { 204 omap_cfg[minor].panel_type = 0x00; /* Mono */ 205 } else { 206 omap_cfg[minor].panel_type = 0x01; /* RGB/Color */ 207 } 208 209 /* fb_fix_screeninfo */ 210 omap_fbfs[minor].line_length = mode->hdisplay * 4; 211 212 /* fb_var_screeninfo */ 213 omap_fbvs[minor].xres = mode->hdisplay; 214 omap_fbvs[minor].yres = mode->vdisplay; 215 omap_fbvs[minor].xres_virtual = mode->hdisplay; 216 omap_fbvs[minor].yres_virtual = mode->vdisplay*2; 217 218 return OK; 219 } 220 221 static void 222 configure_with_defaults(int minor) 223 { 224 if (minor < 0 || minor >= FB_DEV_NR) { 225 log_warn(&log, "Invalid minor #%d\n", minor); 226 return; 227 } 228 229 /* copy the default values into this minor's configuration */ 230 memcpy(&omap_cfg[minor], &default_cfg, sizeof(struct panel_config)); 231 memcpy(&omap_fbfs[minor], &default_fbfs, sizeof(struct fb_fix_screeninfo)); 232 memcpy(&omap_fbvs[minor], &default_fbvs, sizeof(struct fb_var_screeninfo)); 233 } 234 235 static void 236 arch_configure_display(int minor) 237 { 238 /* Tell hardware where frame buffer is and turn display on */ 239 u32_t off, rdispc; 240 241 if (!initialized) return; 242 if (minor != 0) return; 243 244 off = omap_fbvs[minor].yoffset * omap_fbvs[minor].xres_virtual * (omap_fbvs[minor].bits_per_pixel/8); 245 246 writew((vir_bytes) OMAP3_DISPC_GFX_BA0(dispc_phys_base), 247 fb_phys + (phys_bytes) off); 248 rdispc = readw((vir_bytes) OMAP3_DISPC_CONTROL(dispc_phys_base)); 249 rdispc |= DISPC_LCDENABLE | DISPC_DIGITALENABLE | DISPC_GOLCD | 250 DISPC_GODIGITAL | DISPC_GPOUT0 | DISPC_GPOUT1; 251 writew((vir_bytes) OMAP3_DISPC_CONTROL(dispc_phys_base), rdispc); 252 } 253 254 int 255 arch_get_device(int minor, struct device *dev) 256 { 257 if (!initialized) return ENXIO; 258 if (minor != 0) return ENXIO; 259 dev->dv_base = fb_vir; 260 dev->dv_size = fb_size; 261 return OK; 262 } 263 264 int 265 arch_get_varscreeninfo(int minor, struct fb_var_screeninfo *fbvsp) 266 { 267 if (!initialized) return ENXIO; 268 if (minor != 0) return ENXIO; 269 270 *fbvsp = omap_fbvs[minor]; 271 return OK; 272 } 273 274 int 275 arch_put_varscreeninfo(int minor, struct fb_var_screeninfo *fbvsp) 276 { 277 int r = OK; 278 279 assert(fbvsp != NULL); 280 281 if (!initialized) return ENXIO; 282 if (minor != 0) return ENXIO; 283 284 /* For now we only allow to play with the yoffset setting */ 285 if (fbvsp->yoffset != omap_fbvs[minor].yoffset) { 286 if (fbvsp->yoffset < 0 || fbvsp->yoffset > omap_fbvs[minor].yres) { 287 return EINVAL; 288 } 289 290 omap_fbvs[minor].yoffset = fbvsp->yoffset; 291 } 292 293 /* Now update hardware with new settings */ 294 arch_configure_display(minor); 295 return OK; 296 } 297 298 int 299 arch_get_fixscreeninfo(int minor, struct fb_fix_screeninfo *fbfsp) 300 { 301 if (!initialized) return ENXIO; 302 if (minor != 0) return ENXIO; 303 304 *fbfsp = omap_fbfs[minor]; 305 return OK; 306 } 307 308 int 309 arch_pan_display(int minor, struct fb_var_screeninfo *fbvsp) 310 { 311 return arch_put_varscreeninfo(minor, fbvsp); 312 } 313 314 int 315 arch_fb_init(int minor, struct edid_info *info) 316 { 317 int r; 318 u32_t rdispc; 319 struct minix_mem_range mr; 320 321 const struct panel_config *panel_cfg = &omap_cfg[minor]; 322 323 if (minor != 0) return ENXIO; /* We support only one minor */ 324 325 if (initialized) { 326 return OK; 327 } else if (info != NULL) { 328 log_debug(&log, "Configuring Settings based on EDID...\n"); 329 r = configure_with_edid(minor, info); 330 if (r != OK) { 331 log_warn(&log, "EDID config failed. Using defaults.\n"); 332 configure_with_defaults(minor); 333 } 334 } else { 335 log_debug(&log, "Loading Default Settings...\n"); 336 configure_with_defaults(minor); 337 } 338 339 initialized = 1; 340 341 /* Configure DSS memory access */ 342 mr.mr_base = OMAP3_DSS_BASE; 343 mr.mr_limit = mr.mr_base + 0x60; 344 if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) { 345 panic("Unable to request access to DSS(1) memory"); 346 } 347 348 dss_phys_base = (vir_bytes) vm_map_phys(SELF, (void *) OMAP3_DSS_BASE, 349 0x60); 350 351 if (dss_phys_base == (vir_bytes) MAP_FAILED) { 352 panic("Unable to request access to DSS(2) memory"); 353 } 354 355 /* Configure DISPC memory access */ 356 mr.mr_base = OMAP3_DISPC_BASE; 357 mr.mr_limit = mr.mr_base + 0x430; 358 if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) { 359 panic("Unable to request access to DISPC(1) memory"); 360 } 361 dispc_phys_base = (vir_bytes) vm_map_phys(SELF, 362 (void *) OMAP3_DISPC_BASE, 363 0x430); 364 365 if (dispc_phys_base == (vir_bytes) MAP_FAILED) { 366 panic("Unable to request access to DISPC(2) memory"); 367 } 368 369 /* Set timings, screen mode, screen size, etc. */ 370 writew(OMAP3_DISPC_TIMINGH(dispc_phys_base), panel_cfg->timing_h); 371 writew(OMAP3_DISPC_TIMINGV(dispc_phys_base), panel_cfg->timing_v); 372 writew(OMAP3_DISPC_POL_FREQ(dispc_phys_base), panel_cfg->pol_freq); 373 writew(OMAP3_DISPC_DIVISOR(dispc_phys_base), panel_cfg->divisor); 374 writew(OMAP3_DISPC_CONFIG(dispc_phys_base), 375 panel_cfg->load_mode << LOADMODE_SHIFT); 376 writew(OMAP3_DISPC_CONTROL(dispc_phys_base), 377 panel_cfg->panel_type << TFTSTN_SHIFT | 378 panel_cfg->data_lines << DATALINES_SHIFT); 379 380 writew((vir_bytes) OMAP3_DISPC_SIZE_LCD(dispc_phys_base), 381 panel_cfg->lcd_size); 382 writew((vir_bytes) OMAP3_DISPC_GFX_SIZE(dispc_phys_base), 383 panel_cfg->lcd_size); 384 writew(OMAP3_DISPC_DEFAULT_COLOR0(dispc_phys_base), 385 panel_cfg->panel_color); 386 387 /* Enable gfx engine */ 388 writew(OMAP3_DISPC_GFX_ATTRIBUTES(dispc_phys_base), 389 (DISPC_GFXBURSTSIZE_16 << GFXBURSTSIZE_SHIFT) | 390 (DISPC_GFXFORMAT_RGB24 << GFXFORMAT_SHIFT) | 391 (DISPC_GFXENABLE)); 392 writew(OMAP3_DISPC_GFX_ROW_INC(dispc_phys_base), 1); 393 writew(OMAP3_DISPC_GFX_PIXEL_INC(dispc_phys_base), 1); 394 395 /* Allocate contiguous physical memory for the display buffer */ 396 fb_size = omap_fbvs[minor].yres_virtual * omap_fbvs[minor].xres_virtual * 397 (omap_fbvs[minor].bits_per_pixel / 8); 398 fb_vir = (vir_bytes) alloc_contig(fb_size, 0, &fb_phys); 399 if (fb_vir == (vir_bytes) MAP_FAILED) { 400 panic("Unable to allocate contiguous memory\n"); 401 } 402 403 /* Configure buffer settings and turn on LCD/Digital */ 404 arch_configure_display(minor); 405 406 return OK; 407 } 408 409