/* * 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 */