etc/services - sync with NetBSD-8
[minix.git] / minix / drivers / video / fb / arch / earm / fb_arch.c
blob58bcc8c104905d4dc5d7918b7fba9279b2698022
1 /* Architecture dependent part for the framebuffer on the OMAP3.
2 * There's obvious room for improvement.
3 */
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"
22 /* default / fallback resolution if EDID reading fails */
23 #define SCREEN_WIDTH 1024
24 #define SCREEN_HEIGHT 600
25 #define PAGES_NR 2
27 #define NSUPPORTED_MODES (4)
29 /* List of valid modes from TRM 7.1
30 * Other modes might work (like the default 1024x600), but no guarantees.
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 */
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);
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;
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;
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 */
80 static struct panel_config omap_cfg[FB_DEV_NR];
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 */
91 static struct fb_fix_screeninfo omap_fbfs[FB_DEV_NR];
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
106 .green = {
107 .offset = 8,
108 .length = 8,
109 .msb_right = 0
111 .blue = {
112 .offset = 0,
113 .length = 8,
114 .msb_right = 0
116 .transp = {
117 .offset = 24,
118 .length = 8,
119 .msb_right = 0
123 static struct fb_var_screeninfo omap_fbvs[FB_DEV_NR];
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
132 static inline u32_t
133 readw(vir_bytes addr)
135 return *((volatile u32_t *) addr);
138 static inline void
139 writew(vir_bytes addr, u32_t val)
141 *((volatile u32_t *) addr) = val;
144 static struct videomode *
145 choose_mode(struct edid_info *info)
147 int i, j;
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--) {
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) {
158 return &(info->edid_modes[i]);
163 return NULL;
166 static int
167 configure_with_edid(int minor, struct edid_info *info)
169 struct videomode *mode;
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;
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");
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;
191 * apply the default settings since we don't overwrite every field
193 configure_with_defaults(minor);
196 * apply the settings corresponding to the given EDID
199 /* panel_config */
200 omap_cfg[minor].lcd_size = ((mode->vdisplay - 1) << 16 | (mode->hdisplay - 1));
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 */
209 /* fb_fix_screeninfo */
210 omap_fbfs[minor].line_length = mode->hdisplay * 4;
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;
218 return OK;
221 static void
222 configure_with_defaults(int minor)
224 if (minor < 0 || minor >= FB_DEV_NR) {
225 log_warn(&log, "Invalid minor #%d\n", minor);
226 return;
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));
235 static void
236 arch_configure_display(int minor)
238 /* Tell hardware where frame buffer is and turn display on */
239 u32_t off, rdispc;
241 if (!initialized) return;
242 if (minor != 0) return;
244 off = omap_fbvs[minor].yoffset * omap_fbvs[minor].xres_virtual * (omap_fbvs[minor].bits_per_pixel/8);
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);
255 arch_get_device(int minor, struct device *dev)
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;
265 arch_get_varscreeninfo(int minor, struct fb_var_screeninfo *fbvsp)
267 if (!initialized) return ENXIO;
268 if (minor != 0) return ENXIO;
270 *fbvsp = omap_fbvs[minor];
271 return OK;
275 arch_put_varscreeninfo(int minor, struct fb_var_screeninfo *fbvsp)
277 int r = OK;
279 assert(fbvsp != NULL);
281 if (!initialized) return ENXIO;
282 if (minor != 0) return ENXIO;
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;
290 omap_fbvs[minor].yoffset = fbvsp->yoffset;
293 /* Now update hardware with new settings */
294 arch_configure_display(minor);
295 return OK;
299 arch_get_fixscreeninfo(int minor, struct fb_fix_screeninfo *fbfsp)
301 if (!initialized) return ENXIO;
302 if (minor != 0) return ENXIO;
304 *fbfsp = omap_fbfs[minor];
305 return OK;
309 arch_pan_display(int minor, struct fb_var_screeninfo *fbvsp)
311 return arch_put_varscreeninfo(minor, fbvsp);
315 arch_fb_init(int minor, struct edid_info *info)
317 int r;
318 u32_t rdispc;
319 struct minix_mem_range mr;
321 const struct panel_config *panel_cfg = &omap_cfg[minor];
323 if (minor != 0) return ENXIO; /* We support only one minor */
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);
334 } else {
335 log_debug(&log, "Loading Default Settings...\n");
336 configure_with_defaults(minor);
339 initialized = 1;
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");
348 dss_phys_base = (vir_bytes) vm_map_phys(SELF, (void *) OMAP3_DSS_BASE,
349 0x60);
351 if (dss_phys_base == (vir_bytes) MAP_FAILED) {
352 panic("Unable to request access to DSS(2) memory");
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");
361 dispc_phys_base = (vir_bytes) vm_map_phys(SELF,
362 (void *) OMAP3_DISPC_BASE,
363 0x430);
365 if (dispc_phys_base == (vir_bytes) MAP_FAILED) {
366 panic("Unable to request access to DISPC(2) memory");
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);
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);
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);
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");
403 /* Configure buffer settings and turn on LCD/Digital */
404 arch_configure_display(minor);
406 return OK;