2 * Copyright (C) 2007 Juergen Beisert <juergen@kreuzholzen.de>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
18 * Activate the VGA feature in a Geode GX1 based system with one of five
19 * possible VESA modes: VGA, SVGA, XGA, 4:3 SXGA and 5:4 SXGA. Also it is
20 * prepared to display a splash screen.
23 #include <device/device.h>
24 #include <device/pci.h>
25 #include <device/pci_ops.h>
26 #include <device/pci_ids.h>
27 #include <console/console.h>
28 #include <cpu/amd/gx1def.h>
31 #if CONFIG_GX1_VIDEO == 1
33 * Some register descriptions that are no listed in cpu/amd/gx1def.h
35 #define CS5530_DOT_CLK_CONFIG 0x0024
36 #define CS5530_DISPLAY_CONFIG 0x0004
38 #define DC_FB_ST_OFFSET 0x8310 /* framebuffer start offset */
39 #define DC_CB_ST_OFFSET 0x8314 /* compression start offset */
40 #define DC_CURS_ST_OFFSET 0x8318 /* cursor start offset */
41 #define DC_VID_ST_OFFSET 0x8320 /* video start offset */
42 #define DC_LINE_DELTA 0x8324 /* fb and cb skip counts */
43 #define DC_BUF_SIZE 0x8328 /* fb and cb line size */
44 #define DC_H_TIMING_1 0x8330 /* horizontal timing... */
45 #define DC_H_TIMING_2 0x8334
46 #define DC_H_TIMING_3 0x8338
47 #define DC_FP_H_TIMING 0x833C
48 #define DC_V_TIMING_1 0x8340 /* vertical timing... */
49 #define DC_V_TIMING_2 0x8344
50 #define DC_V_TIMING_3 0x8348
51 #define DC_FP_V_TIMING 0x834C
52 #define DC_TIMING_CFG 0x8308
53 #define DC_OUTPUT_CFG 0x830C
56 * what colour depth should be used as default (in bpp)
57 * Note: Currently no other value than 16 is supported
59 #define COLOUR_DEPTH 16
62 * Support for a few basic video modes
63 * Note: all modes only for CRT. The flatpanel feature is
64 * not supported here (due to the lack of hardware to test)
67 int pixel_clock; /*<< pixel clock in Hz */
68 unsigned long pll_value; /*<< pll register value for this clock */
80 int sync_pol; /*<< 0: low, 1: high, bit 0 hsync, bit 1 vsync */
84 * values for .sync_pol
86 #define HSYNC_HIGH_POL 0
87 #define HSYNC_LOW_POL 1
88 #define VSYNC_HIGH_POL 0
89 #define VSYNC_LOW_POL 2
91 /* ModeLine "640x480" 31.5 640 664 704 832 480 489 491 520 -hsync -vsync */
92 static const struct video_mode mode_640x480 = {
94 * 640x480 @ 72Hz hsync: 37.9kHz
95 * VESA standard mode for classic 4:3 monitors
97 .pixel_clock = 31500000,
98 .pll_value = 0x33915801,
100 .visible_pixel = 640,
102 .hsync_end = 704, /* 1.27 us sync length */
103 .line_length = 832, /* 26.39us */
105 .visible_lines = 480,
108 .picture_length = 520, /* 13.89ms */
110 .sync_pol = HSYNC_LOW_POL | VSYNC_LOW_POL
113 /* ModeLine "800x600" 50.0 800 856 976 1040 600 637 643 666 +hsync +vsync */
114 static const struct video_mode mode_800x600 = {
116 * 800x600 @ 72Hz hsync: 48.1kHz
117 * VESA standard mode for classic 4:3 monitors
119 .pixel_clock = 50000000,
120 .pll_value = 0x23088801,
122 .visible_pixel = 800,
125 .line_length = 1040, /* 20.8us */
127 .visible_lines = 600,
130 .picture_length = 666, /* 13.89ms */
132 .sync_pol = HSYNC_HIGH_POL | VSYNC_HIGH_POL
135 /* ModeLine "1024x768" 75.0 1024 1048 1184 1328 768 771 777 806 -hsync -vsync */
136 static const struct video_mode mode_1024x768 = {
138 * 1024x768 @ 70Hz (VESA) hsync: 56.5kHz
139 * Standard mode for classic 4:3 monitors
141 .pixel_clock = 75000000,
142 .pll_value = 0x37E22801,
144 .visible_pixel = 1024,
147 .line_length = 1328, /* 17.7us */
149 .visible_lines = 768,
152 .picture_length = 806, /* 14.3us */
154 .sync_pol = HSYNC_LOW_POL | VSYNC_LOW_POL
157 /* ModeLine "1280x960" 108.0 1280 1376 1488 1800 960 961 964 1000 +hsync +vsync */
158 static const struct video_mode mode_1280x960 = {
160 * 1280x960 @ 60Hz (VESA) hsync: 60.0kHz
161 * Mode for classic 4:3 monitors
163 .pixel_clock = 108000000,
164 .pll_value = 0x2710C805,
166 .visible_pixel = 1280,
169 .line_length = 1800, /* 16.67us */
171 .visible_lines = 960,
174 .picture_length = 1000, /* 16.67ms */
176 .sync_pol = HSYNC_HIGH_POL | VSYNC_HIGH_POL
179 /* ModeLine "1280x1024" 108.0 1280 1328 1440 1688 1024 1025 1028 1066 +hsync +vsync */
180 static const struct video_mode mode_1280x1024 = {
182 * 1280x1024 @ 60Hz (VESA) hsync: 64.0kHz
183 * Mode for modern 5:4 flat screens
185 .pixel_clock = 108000000,
186 .pll_value = 0x2710C805,
188 .visible_pixel = 1280,
191 .line_length = 1688, /* 15.6us */
193 .visible_lines = 1024,
196 .picture_length = 1066,
198 .sync_pol = HSYNC_HIGH_POL | VSYNC_HIGH_POL
202 * a few supported common modes
204 static const struct video_mode *modes[] = {
205 &mode_640x480, /* CONFIG_GX1_VIDEOMODE = 0 */
206 &mode_800x600, /* CONFIG_GX1_VIDEOMODE = 1 */
207 &mode_1024x768, /* CONFIG_GX1_VIDEOMODE = 2 */
208 &mode_1280x960, /* CONFIG_GX1_VIDEOMODE = 3 */
209 &mode_1280x1024 /* CONFIG_GX1_VIDEOMODE = 4 */
212 /* make a sanity check at buildtime */
213 #if CONFIG_GX1_VIDEOMODE > 4
214 # error Requested video mode is unknown!
218 * Setup the pixel PLL in the companion chip
219 * base: register's base address
220 * pll_val: pll register value to be set
222 static void cs5530_set_clock_frequency(void *io_base,unsigned long pll_val)
226 /* disable the PLL first, reset and power it down */
227 reg = readl(io_base+CS5530_DOT_CLK_CONFIG) & ~0x20;
229 writel(reg, io_base+CS5530_DOT_CLK_CONFIG);
231 /* write the new PLL setting */
232 reg |= (pll_val & ~0x80000920);
233 writel(reg, io_base+CS5530_DOT_CLK_CONFIG);
235 mdelay(1); /* wait for control voltage to be 0V */
239 writel(reg, io_base+CS5530_DOT_CLK_CONFIG);
243 writel(reg, io_base+CS5530_DOT_CLK_CONFIG);
247 writel(reg, io_base+CS5530_DOT_CLK_CONFIG);
251 * Setup memory layout
252 * gx_base: GX register area
253 * mode: Data about the video mode to setup
255 * This routine assumes unlocked DC registers. Using compressed buffer
256 * is not supported! (makes more sense later, but not while booting)
258 static void dc_setup_layout(void *gx_base,const struct video_mode *mode)
260 unsigned long base = 0x00000000;
262 writel(base, gx_base + DC_FB_ST_OFFSET);
264 base += (COLOUR_DEPTH>>3) * mode->visible_pixel * mode->visible_lines;
266 writel(base, gx_base + DC_CB_ST_OFFSET);
267 writel(base, gx_base + DC_CURS_ST_OFFSET);
268 writel(base, gx_base + DC_VID_ST_OFFSET);
269 writel(((COLOUR_DEPTH>>3) * mode->visible_pixel) >> 2, gx_base + DC_LINE_DELTA);
270 writel(((COLOUR_DEPTH>>3) * mode->visible_pixel) >> 3, gx_base + DC_BUF_SIZE);
274 * Setup the HSYNC/VSYNC, active video timing
275 * gx_base: GX register area
276 * mode: Data about the video mode to setup
278 * This routine assumes unlocked DC registers
280 * |<------------------------- htotal ----------------------------->|
281 * |<------------ hactive -------------->| |
282 * | hblankstart-->| |
286 * |#####################################___________________________| RGB data
287 * |______________________________________________---------_________| HSYNC
289 * |<------------------------- vtotal ----------------------------->|
290 * |<------------ vactive -------------->| |
291 * | vblankstart-->| |
295 * |#####################################___________________________| line data
296 * |______________________________________________---------_________| YSYNC
298 static void dc_setup_timing(void *gx_base,const struct video_mode *mode)
300 unsigned long hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal;
301 unsigned long vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal;
303 hactive = mode->visible_pixel & 0x7FF;
304 hblankstart = hactive;
305 hsyncstart = mode->hsync_start & 0x7FF;
306 hsyncend = mode->hsync_end & 0x7FF;
307 hblankend = mode->line_length & 0x7FF;
310 vactive = mode->visible_lines & 0x7FF;
311 vblankstart = vactive;
312 vsyncstart = mode->vsync_start & 0x7FF;
313 vsyncend = mode->vsync_end & 0x7FF;
314 vblankend = mode->picture_length & 0x7FF;
317 /* row description */
318 writel((hactive - 1) | ((htotal - 1) << 16), gx_base + DC_H_TIMING_1);
319 /* horizontal blank description */
320 writel((hblankstart - 1) | ((hblankend - 1) << 16), gx_base + DC_H_TIMING_2);
321 /* horizontal sync description */
322 writel((hsyncstart - 1) | ((hsyncend - 1) << 16), gx_base + DC_H_TIMING_3);
323 writel((hsyncstart - 1) | ((hsyncend - 1) << 16), gx_base + DC_FP_H_TIMING);
325 /* line description */
326 writel((vactive - 1) | ((vtotal - 1) << 16), gx_base + DC_V_TIMING_1);
327 /* vertical blank description */
328 writel((vblankstart - 1) | ((vblankend - 1) << 16), gx_base + DC_V_TIMING_2);
329 /* vertical sync description */
330 writel((vsyncstart - 1) | ((vsyncend - 1) << 16), gx_base + DC_V_TIMING_3);
331 writel((vsyncstart - 2) | ((vsyncend - 2) << 16), gx_base + DC_FP_V_TIMING);
335 * Setup required internals to bring the mode up and running
336 * gx_base: GX register area
337 * mode: Data about the video mode to setup
339 static void cs5530_activate_mode(void *gx_base, const struct video_mode *mode)
341 writel(0x00000080, gx_base + DC_GENERAL_CFG);
343 dc_setup_layout(gx_base,mode);
344 dc_setup_timing(gx_base,mode);
346 writel(0x2000C581, gx_base + DC_GENERAL_CFG);
347 writel(0x0000002F, gx_base + DC_TIMING_CFG);
348 writel(0x00003004, gx_base + DC_OUTPUT_CFG);
352 * Activate the current mode to be "visible" outside
353 * gx_base: GX register area
354 * mode: Data about the video mode to setup
356 static void cs5530_activate_video(void *io_base, const struct video_mode *mode)
360 val = mode->sync_pol;
363 writel(val | 0x0020002F, io_base + CS5530_DISPLAY_CONFIG);
367 * This bitmap file must provide:
368 * int width: pixel count in one line
369 * int height: line count
370 * int colours: ount of used colour
371 * unsigned long colour_map[]: RGB 565 colours to be used
372 * unsigned char bitmap[]: index per pixel into colour_map[], width*height pixels
377 * show a boot splash screen in the right lower corner of the screen
378 * swidth: screen width in pixel
379 * sheight: screen height in lines
380 * pitch: line pitch in bytes
381 * base: screen base address
383 * This routine assumes we are using a 16 bit colour depth!
385 static void show_boot_splash_16(u32 swidth,u32 sheight,u32 pitch,void *base)
389 u32 xstart,ystart,x,y;
391 * fill the screen with the colour of the
392 * left top pixel in the graphic
394 word_count = pitch*sheight;
395 printk_debug("Clear Screen at %p, %d words\n",base,word_count);
396 adr = (unsigned short *) base;
397 for (i=0; i < word_count; i++, adr++)
398 *adr = colour_map[bitmap[0]];
399 printk_debug("Ready\n");
405 ystart=sheight-height;
406 printk_debug("Start at %u,%u\n",xstart,ystart);
407 for (y=0;y<height;y++) {
408 adr=(unsigned short*)(base + pitch*(y+ystart)+2*xstart);
409 for (x=0;x<width;x++) {
410 *adr=(unsigned short)colour_map[(int)bitmap[x+y*width]];
419 static void cs5530_vga_init(device_t dev)
421 const struct video_mode *mode;
422 void *io_base, *gx_base;
424 io_base = (void*)pci_read_config32(dev,0x10);
425 gx_base = (void*)GX_BASE;
426 mode = modes[CONFIG_GX1_VIDEOMODE];
428 printk_debug("Setting up video mode %dx%d with %d Hz clock\n",
429 mode->visible_pixel, mode->visible_lines, mode->pixel_clock);
431 cs5530_set_clock_frequency(io_base,mode->pll_value);
433 writel(DC_UNLOCK_MAGIC, gx_base + DC_UNLOCK);
435 show_boot_splash_16(mode->visible_pixel, mode->visible_lines,
436 mode->visible_pixel*(COLOUR_DEPTH>>3),(void*)(GX_BASE+0x800000));
438 cs5530_activate_mode(gx_base,mode);
440 cs5530_activate_video(io_base, mode);
441 writel(0x00000000, gx_base + DC_UNLOCK);
444 static struct device_operations vga_ops = {
445 .read_resources = pci_dev_read_resources,
446 .set_resources = pci_dev_set_resources,
447 .enable_resources = pci_dev_enable_resources,
448 .init = cs5530_vga_init,
449 .enable = NULL /* not required */
452 static struct pci_driver vga_pci_driver __pci_driver = {
454 .vendor = PCI_VENDOR_ID_CYRIX,
455 .device = PCI_DEVICE_ID_CYRIX_5530_VIDEO
458 #endif /* #if CONFIG_GX1_VIDEO == 1 */