xref: /minix/minix/drivers/video/fb/arch/earm/fb_arch.c (revision 83133719)
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