diff options
Diffstat (limited to 'src/southbridge/amd/cs5530/vga.c')
-rw-r--r-- | src/southbridge/amd/cs5530/vga.c | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/src/southbridge/amd/cs5530/vga.c b/src/southbridge/amd/cs5530/vga.c new file mode 100644 index 0000000000..4a26251084 --- /dev/null +++ b/src/southbridge/amd/cs5530/vga.c @@ -0,0 +1,495 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007 Juergen Beisert <juergen@kreuzholzen.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +/** + * @brief Activate the VGA feature in a Geode GX1 based system with one + * of five possible VESA modes: VGA, SVGA, XGA, 4:3 SXGA and 5:4 SXGA. + * Also it is prepared to display a splash screen. + * + * In a Geode GX1 environment the companion CS5530 is the VGA + * interface only. It contains a PLL for pixel clock generation, + * DACs to generate the analogue RGB signals, drivers for HSYNC + * and VSYNC and drivers for a digital flatpanel. + * The graphic feature itself (framebuffer, acceleration unit) + * is not part of this device. It is part of the CPU device. + * But both depend on each other, we cannot divide them into + * different drivers. So this driver is not only a CS5530 driver, + * it is also a Geode GX1 chipset graphic driver. + */ +#include <arch/io.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ops.h> +#include <device/pci_ids.h> +#include <console/console.h> +#include <cpu/amd/gx1def.h> +#include <delay.h> + +#if CONFIG_GX1_VIDEO == 1 +/* + * Some register descriptions that are no listed in cpu/amd/gx1def.h + */ +#define CS5530_DOT_CLK_CONFIG 0x0024 +#define CS5530_DISPLAY_CONFIG 0x0004 + +#define DC_FB_ST_OFFSET 0x8310 /* framebuffer start offset */ +#define DC_CB_ST_OFFSET 0x8314 /* compression start offset */ +#define DC_CURS_ST_OFFSET 0x8318 /* cursor start offset */ +#define DC_VID_ST_OFFSET 0x8320 /* video start offset */ +#define DC_LINE_DELTA 0x8324 /* fb and cb skip counts */ +#define DC_BUF_SIZE 0x8328 /* fb and cb line size */ +#define DC_H_TIMING_1 0x8330 /* horizontal timing... */ +#define DC_H_TIMING_2 0x8334 +#define DC_H_TIMING_3 0x8338 +#define DC_FP_H_TIMING 0x833C +#define DC_V_TIMING_1 0x8340 /* vertical timing... */ +#define DC_V_TIMING_2 0x8344 +#define DC_V_TIMING_3 0x8348 +#define DC_FP_V_TIMING 0x834C +#define DC_TIMING_CFG 0x8308 +#define DC_OUTPUT_CFG 0x830C + +/** + * what colour depth should be used as default (in bpp) + * Note: Currently no other value than 16 is supported + */ +#define COLOUR_DEPTH 16 + +/** + * Support for a few basic video modes + * Note: all modes only for CRT. The flatpanel feature is + * not supported here (due to the lack of hardware to test) + */ +struct video_mode { + int pixel_clock; /*<< pixel clock in Hz */ + unsigned long pll_value; /*<< pll register value for this clock */ + + int visible_pixel; /*<< visible pixels in one line */ + int hsync_start; /*<< start of hsync behind visible pixels */ + int hsync_end; /*<< end of hsync behind its start */ + int line_length; /*<< whole line length */ + + int visible_lines; /*<< visible lines on screen */ + int vsync_start; /*<< vsync start behind last visible line */ + int vsync_end; /*<< end of vsync behind its start */ + int picture_length; /*<< whole screen length */ + + int sync_pol; /*<< 0: low, 1: high, bit 0 hsync, bit 1 vsync */ +}; + +/* + * values for .sync_pol in struct video_mode + */ +#define HSYNC_HIGH_POL 0 +#define HSYNC_LOW_POL 1 +#define VSYNC_HIGH_POL 0 +#define VSYNC_LOW_POL 2 + +/** + * 640x480 @ 72Hz hsync: 37.9kHz + * VESA standard mode for classic 4:3 monitors + * Copied from X11: + * ModeLine "640x480" 31.5 640 664 704 832 480 489 491 520 -hsync -vsync + */ +static const struct video_mode mode_640x480 = { + .pixel_clock = 31500000, + .pll_value = 0x33915801, + + .visible_pixel = 640, + .hsync_start = 664, + .hsync_end = 704, /* 1.27 us sync length */ + .line_length = 832, /* 26.39us */ + + .visible_lines = 480, + .vsync_start = 489, + .vsync_end = 491, + .picture_length = 520, /* 13.89ms */ + + .sync_pol = HSYNC_LOW_POL | VSYNC_LOW_POL, +}; + +/** + * 800x600 @ 72Hz hsync: 48.1kHz + * VESA standard mode for classic 4:3 monitors + * Copied from X11: + * ModeLine "800x600" 50.0 800 856 976 1040 600 637 643 666 +hsync +vsync + */ +static const struct video_mode mode_800x600 = { + .pixel_clock = 50000000, + .pll_value = 0x23088801, + + .visible_pixel = 800, + .hsync_start = 856, + .hsync_end = 976, + .line_length = 1040, /* 20.8us */ + + .visible_lines = 600, + .vsync_start = 637, + .vsync_end = 643, + .picture_length = 666, /* 13.89ms */ + + .sync_pol = HSYNC_HIGH_POL | VSYNC_HIGH_POL, +}; + +/** + * 1024x768 @ 70Hz (VESA) hsync: 56.5kHz + * Standard mode for classic 4:3 monitors + * Copied from X11: + * ModeLine "1024x768" 75.0 1024 1048 1184 1328 768 771 777 806 -hsync -vsync + */ +static const struct video_mode mode_1024x768 = { + .pixel_clock = 75000000, + .pll_value = 0x37E22801, + + .visible_pixel = 1024, + .hsync_start = 1048, + .hsync_end = 1184, + .line_length = 1328, /* 17.7us */ + + .visible_lines = 768, + .vsync_start = 771, + .vsync_end = 777, + .picture_length = 806, /* 14.3us */ + + .sync_pol = HSYNC_LOW_POL | VSYNC_LOW_POL, +}; + +/** + * 1280x960 @ 60Hz (VESA) hsync: 60.0kHz + * Mode for classic 4:3 monitors + * Copied from X11: + * ModeLine "1280x960" 108.0 1280 1376 1488 1800 960 961 964 1000 +hsync +vsync + */ +static const struct video_mode mode_1280x960 = { + .pixel_clock = 108000000, + .pll_value = 0x2710C805, + + .visible_pixel = 1280, + .hsync_start = 1376, + .hsync_end = 1488, + .line_length = 1800, /* 16.67us */ + + .visible_lines = 960, + .vsync_start = 961, + .vsync_end = 964, + .picture_length = 1000, /* 16.67ms */ + + .sync_pol = HSYNC_HIGH_POL | VSYNC_HIGH_POL, +}; + +/** + * 1280x1024 @ 60Hz (VESA) hsync: 64.0kHz + * Mode for modern 5:4 flat screens + * Copied from X11: + * ModeLine "1280x1024" 108.0 1280 1328 1440 1688 1024 1025 1028 1066 +hsync +vsync + */ +static const struct video_mode mode_1280x1024 = { + .pixel_clock = 108000000, + .pll_value = 0x2710C805, + + .visible_pixel = 1280, + .hsync_start = 1328, + .hsync_end = 1440, + .line_length = 1688, /* 15.6us */ + + .visible_lines = 1024, + .vsync_start = 1025, + .vsync_end = 1028, + .picture_length = 1066, + + .sync_pol = HSYNC_HIGH_POL | VSYNC_HIGH_POL, +}; + +/** + * List of supported common modes + */ +static const struct video_mode *modes[] = { + &mode_640x480, /* CONFIG_GX1_VIDEOMODE = 0 */ + &mode_800x600, /* CONFIG_GX1_VIDEOMODE = 1 */ + &mode_1024x768, /* CONFIG_GX1_VIDEOMODE = 2 */ + &mode_1280x960, /* CONFIG_GX1_VIDEOMODE = 3 */ + &mode_1280x1024 /* CONFIG_GX1_VIDEOMODE = 4 */ +}; + +/* make a sanity check at buildtime */ +#if CONFIG_GX1_VIDEOMODE > 4 +# error Requested video mode is unknown! +#endif + +/** + * Setup the pixel PLL in the companion chip + * @param[in] base register's base address + * @param[in] pll_val pll register value to be set + * + * The PLL to program here is located in the CS5530 + */ +static void cs5530_set_clock_frequency(u32 io_base, unsigned long pll_val) +{ + unsigned long reg; + + /* disable the PLL first, reset and power it down */ + reg = read32(io_base+CS5530_DOT_CLK_CONFIG) & ~0x20; + reg |= 0x80000100; + write32(io_base+CS5530_DOT_CLK_CONFIG, reg); + + /* write the new PLL setting */ + reg |= (pll_val & ~0x80000920); + write32(io_base+CS5530_DOT_CLK_CONFIG, reg); + + mdelay(1); /* wait for control voltage to be 0V */ + + /* enable the PLL */ + reg |= 0x00000800; + write32(io_base+CS5530_DOT_CLK_CONFIG, reg); + + /* clear reset */ + reg &= ~0x80000000; + write32(io_base+CS5530_DOT_CLK_CONFIG, reg); + + /* clear bypass */ + reg &= ~0x00000100; + write32(io_base+CS5530_DOT_CLK_CONFIG, reg); +} + +/** + * Setup memory layout + * @param[in] gx_base GX register area + * @param[in] mode Data about the video mode to setup + * + * Memory layout must be setup in Geode GX1's chipset. + * Note: This routine assumes unlocked DC registers. + * Note: Using compressed buffer is not supported yet! + * (makes more sense later, but not while booting) + * + * At this point a check is missed if the requested video + * mode is possible with the provided video memory. + * Check if symbol CONFIG_VIDEO_MB is at least: + * - 1 (=1MiB) for VGA and SVGA + * - 2 (=2MiB) for XGA + * - 4 (=4MiB) for SXGA + */ +static void dc_setup_layout(u32 gx_base, const struct video_mode *mode) +{ + u32 base = 0x00000000; + + write32(gx_base + DC_FB_ST_OFFSET, base); + + base += (COLOUR_DEPTH>>3) * mode->visible_pixel * mode->visible_lines; + + write32(gx_base + DC_CB_ST_OFFSET, base); + write32(gx_base + DC_CURS_ST_OFFSET, base); + write32(gx_base + DC_VID_ST_OFFSET, base); + write32(gx_base + DC_LINE_DELTA, ((COLOUR_DEPTH>>3) * mode->visible_pixel) >> 2); + write32(gx_base + DC_BUF_SIZE, ((COLOUR_DEPTH>>3) * mode->visible_pixel) >> 3); +} + +/** + * Setup the HSYNC/VSYNC, active video timing + * @param[in] gx_base GX register area + * @param[in] mode Data about the video mode to setup + * + * Sync signal generation is done in Geode GX1's chipset. + * Note: This routine assumes unlocked DC registers + * + * |<------------------------- htotal ----------------------------->| + * |<------------ hactive -------------->| | + * | hblankstart-->| | + * | hblankend-->| + * | hsyncstart-->| | + * | hsyncend-->| | + * |#####################################___________________________| RGB data + * |______________________________________________---------_________| HSYNC + * + * |<------------------------- vtotal ----------------------------->| + * |<------------ vactive -------------->| | + * | vblankstart-->| | + * | vblankend-->| + * | vsyncstart-->| | + * | vsyncend-->| | + * |#####################################___________________________| line data + * |______________________________________________---------_________| YSYNC + */ +static void dc_setup_timing(u32 gx_base, const struct video_mode *mode) +{ + u32 hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal; + u32 vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal; + + hactive = mode->visible_pixel & 0x7FF; + hblankstart = hactive; + hsyncstart = mode->hsync_start & 0x7FF; + hsyncend = mode->hsync_end & 0x7FF; + hblankend = mode->line_length & 0x7FF; + htotal = hblankend; + + vactive = mode->visible_lines & 0x7FF; + vblankstart = vactive; + vsyncstart = mode->vsync_start & 0x7FF; + vsyncend = mode->vsync_end & 0x7FF; + vblankend = mode->picture_length & 0x7FF; + vtotal = vblankend; + + /* row description */ + write32(gx_base + DC_H_TIMING_1, (hactive - 1) | ((htotal - 1) << 16)); + /* horizontal blank description */ + write32(gx_base + DC_H_TIMING_2, (hblankstart - 1) | ((hblankend - 1) << 16)); + /* horizontal sync description */ + write32(gx_base + DC_H_TIMING_3, (hsyncstart - 1) | ((hsyncend - 1) << 16)); + write32(gx_base + DC_FP_H_TIMING, (hsyncstart - 1) | ((hsyncend - 1) << 16)); + + /* line description */ + write32(gx_base + DC_V_TIMING_1, (vactive - 1) | ((vtotal - 1) << 16)); + /* vertical blank description */ + write32(gx_base + DC_V_TIMING_2, (vblankstart - 1) | ((vblankend - 1) << 16)); + /* vertical sync description */ + write32(gx_base + DC_V_TIMING_3, (vsyncstart - 1) | ((vsyncend - 1) << 16)); + write32(gx_base + DC_FP_V_TIMING, (vsyncstart - 2) | ((vsyncend - 2) << 16)); +} + +/** + * Setup required internals to bring the mode up and running + * @param[in] gx_base GX register area + * @param[in] mode Data about the video mode to setup + * + * Must be setup in Geode GX1's chipset. + * Note: This routine assumes unlocked DC registers. + */ +static void cs5530_activate_mode(u32 gx_base, const struct video_mode *mode) +{ + write32(gx_base + DC_GENERAL_CFG, 0x00000080); + mdelay(1); + dc_setup_layout(gx_base,mode); + dc_setup_timing(gx_base,mode); + + write32(gx_base + DC_GENERAL_CFG, 0x2000C581); + write32(gx_base + DC_TIMING_CFG, 0x0000002F); + write32(gx_base + DC_OUTPUT_CFG, 0x00003004); +} + +/** + * Activate the current mode to be "visible" outside + * @param[in] gx_base GX register area + * @param[in] mode Data about the video mode to setup + * + * As we now activate the interface this must be done + * in the CS5530 + */ +static void cs5530_activate_video(u32 io_base, const struct video_mode *mode) +{ + u32 val; + + val = (u32)mode->sync_pol << 8; + write32(io_base + CS5530_DISPLAY_CONFIG, val | 0x0020002F); +} + +#if CONFIG_SPLASH_GRAPHIC == 1 + +/* + * This bitmap file must provide: + * int width: pixel count in one line + * int height: line count + * int colours: ount of used colour + * unsigned long colour_map[]: RGB 565 colours to be used + * unsigned char bitmap[]: index per pixel into colour_map[], width*height pixels + */ +#include "bitmap.c" + +/* + * show a boot splash screen in the right lower corner of the screen + * swidth: screen width in pixel + * sheight: screen height in lines + * pitch: line pitch in bytes + * base: screen base address + * + * This routine assumes we are using a 16 bit colour depth! + */ +static void show_boot_splash_16(u32 swidth, u32 sheight, u32 pitch,void *base) +{ + int word_count,i; + unsigned short *adr; + u32 xstart,ystart,x,y; + /* + * fill the screen with the colour of the + * left top pixel in the graphic + */ + word_count = pitch * sheight; + adr = (unsigned short*)base; + for (i = 0; i < word_count; i++, adr++) + *adr = colour_map[bitmap[0]]; + + /* + * paint the splash + */ + xstart = swidth-width; + ystart = sheight-height; + for (y = 0; y < height; y++) { + adr=(unsigned short*)(base + pitch*(y+ystart) + 2 * xstart); + for (x = 0; x < width; x++) { + *adr=(unsigned short)colour_map[(int)bitmap[x + y * width]]; + adr++; + } + } +} +#else +# define show_boot_splash_16(w, x, y , z) +#endif + +/** + * coreboot management part + * @param[in] dev Info about the PCI device to initialise + */ +static void cs5530_vga_init(device_t dev) +{ + const struct video_mode *mode; + u32 io_base, gx_base; + + io_base = pci_read_config32(dev, 0x10); + gx_base = GX_BASE; + mode = modes[CONFIG_GX1_VIDEOMODE]; + + printk(BIOS_DEBUG, "Setting up video mode %dx%d with %d Hz clock\n", + mode->visible_pixel, mode->visible_lines, mode->pixel_clock); + + cs5530_set_clock_frequency(io_base, mode->pll_value); + + write32(gx_base + DC_UNLOCK, DC_UNLOCK_MAGIC); + + show_boot_splash_16(mode->visible_pixel, mode->visible_lines, + mode->visible_pixel * (COLOUR_DEPTH>>3), (void*)(GX_BASE + 0x800000)); + + cs5530_activate_mode(gx_base, mode); + + cs5530_activate_video(io_base, mode); + write32(gx_base + DC_UNLOCK, 0x00000000); +} + +static struct device_operations vga_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = cs5530_vga_init, + .enable = NULL, /* not required */ +}; + +static const struct pci_driver vga_pci_driver __pci_driver = { + .ops = &vga_ops, + .vendor = PCI_VENDOR_ID_CYRIX, + .device = PCI_DEVICE_ID_CYRIX_5530_VIDEO, +}; + +#endif /* #if CONFIG_GX1_VIDEO == 1 */ |