diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/intelfb |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/video/intelfb')
-rw-r--r-- | drivers/video/intelfb/Makefile | 8 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfb.h | 280 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbdrv.c | 1570 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbdrv.h | 68 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbhw.c | 1780 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbhw.h | 570 |
6 files changed, 4276 insertions, 0 deletions
diff --git a/drivers/video/intelfb/Makefile b/drivers/video/intelfb/Makefile new file mode 100644 index 00000000000..722d21d6e5c --- /dev/null +++ b/drivers/video/intelfb/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_FB_INTEL) += intelfb.o + +intelfb-objs := intelfbdrv.o intelfbhw.o + +ifdef CONFIG_FB_INTEL_DEBUG +#EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP +EXTRA_CFLAGS += -DDEBUG -DREGDUMP +endif diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h new file mode 100644 index 00000000000..6680ec9ba69 --- /dev/null +++ b/drivers/video/intelfb/intelfb.h @@ -0,0 +1,280 @@ +#ifndef _INTELFB_H +#define _INTELFB_H + +/* $DHD: intelfb/intelfb.h,v 1.40 2003/06/27 15:06:25 dawes Exp $ */ + +#include <linux/agp_backend.h> +#include <linux/fb.h> + + +/*** Version/name ***/ +#define INTELFB_VERSION "0.9.2" +#define INTELFB_MODULE_NAME "intelfb" +#define SUPPORTED_CHIPSETS "830M/845G/852GM/855GM/865G/915G" + + +/*** Debug/feature defines ***/ + +#ifndef DEBUG +#define DEBUG 0 +#endif + +#ifndef VERBOSE +#define VERBOSE 0 +#endif + +#ifndef REGDUMP +#define REGDUMP 0 +#endif + +#ifndef DETECT_VGA_CLASS_ONLY +#define DETECT_VGA_CLASS_ONLY 1 +#endif + +#ifndef ALLOCATE_FOR_PANNING +#define ALLOCATE_FOR_PANNING 1 +#endif + +#ifndef PREFERRED_MODE +#define PREFERRED_MODE "1024x768-32@70" +#endif + +/*** hw-related values ***/ + +/* PCI ids for supported devices */ +#define PCI_DEVICE_ID_INTEL_830M 0x3577 +#define PCI_DEVICE_ID_INTEL_845G 0x2562 +#define PCI_DEVICE_ID_INTEL_85XGM 0x3582 +#define PCI_DEVICE_ID_INTEL_865G 0x2572 +#define PCI_DEVICE_ID_INTEL_915G 0x2582 + +/* Size of MMIO region */ +#define INTEL_REG_SIZE 0x80000 + +#define STRIDE_ALIGNMENT 16 + +#define PALETTE_8_ENTRIES 256 + + +/*** Macros ***/ + +/* basic arithmetic */ +#define KB(x) ((x) * 1024) +#define MB(x) ((x) * 1024 * 1024) +#define BtoKB(x) ((x) / 1024) +#define BtoMB(x) ((x) / 1024 / 1024) + +#define GTT_PAGE_SIZE KB(4) + +#define ROUND_UP_TO(x, y) (((x) + (y) - 1) / (y) * (y)) +#define ROUND_DOWN_TO(x, y) ((x) / (y) * (y)) +#define ROUND_UP_TO_PAGE(x) ROUND_UP_TO((x), GTT_PAGE_SIZE) +#define ROUND_DOWN_TO_PAGE(x) ROUND_DOWN_TO((x), GTT_PAGE_SIZE) + +/* messages */ +#define PFX INTELFB_MODULE_NAME ": " + +#define ERR_MSG(fmt, args...) printk(KERN_ERR PFX fmt, ## args) +#define WRN_MSG(fmt, args...) printk(KERN_WARNING PFX fmt, ## args) +#define NOT_MSG(fmt, args...) printk(KERN_NOTICE PFX fmt, ## args) +#define INF_MSG(fmt, args...) printk(KERN_INFO PFX fmt, ## args) +#if DEBUG +#define DBG_MSG(fmt, args...) printk(KERN_DEBUG PFX fmt, ## args) +#else +#define DBG_MSG(fmt, args...) while (0) printk(fmt, ## args) +#endif + +/* get commonly used pointers */ +#define GET_DINFO(info) (info)->par + +/* misc macros */ +#define ACCEL(d, i) \ + ((d)->accel && !(d)->ring_lockup && \ + ((i)->var.accel_flags & FB_ACCELF_TEXT)) + +/*#define NOACCEL_CHIPSET(d) \ + ((d)->chipset != INTEL_865G)*/ +#define NOACCEL_CHIPSET(d) \ + (0) + +#define FIXED_MODE(d) ((d)->fixed_mode) + +/*** Driver paramters ***/ + +#define RINGBUFFER_SIZE KB(64) +#define HW_CURSOR_SIZE KB(4) + +/* Intel agpgart driver */ +#define AGP_PHYSICAL_MEMORY 2 + +/*** Data Types ***/ + +/* supported chipsets */ +enum intel_chips { + INTEL_830M, + INTEL_845G, + INTEL_85XGM, + INTEL_852GM, + INTEL_852GME, + INTEL_855GM, + INTEL_855GME, + INTEL_865G, + INTEL_915G +}; + +struct intelfb_hwstate { + u32 vga0_divisor; + u32 vga1_divisor; + u32 vga_pd; + u32 dpll_a; + u32 dpll_b; + u32 fpa0; + u32 fpa1; + u32 fpb0; + u32 fpb1; + u32 palette_a[PALETTE_8_ENTRIES]; + u32 palette_b[PALETTE_8_ENTRIES]; + u32 htotal_a; + u32 hblank_a; + u32 hsync_a; + u32 vtotal_a; + u32 vblank_a; + u32 vsync_a; + u32 src_size_a; + u32 bclrpat_a; + u32 htotal_b; + u32 hblank_b; + u32 hsync_b; + u32 vtotal_b; + u32 vblank_b; + u32 vsync_b; + u32 src_size_b; + u32 bclrpat_b; + u32 adpa; + u32 dvoa; + u32 dvob; + u32 dvoc; + u32 dvoa_srcdim; + u32 dvob_srcdim; + u32 dvoc_srcdim; + u32 lvds; + u32 pipe_a_conf; + u32 pipe_b_conf; + u32 disp_arb; + u32 cursor_a_control; + u32 cursor_b_control; + u32 cursor_a_base; + u32 cursor_b_base; + u32 cursor_size; + u32 disp_a_ctrl; + u32 disp_b_ctrl; + u32 disp_a_base; + u32 disp_b_base; + u32 cursor_a_palette[4]; + u32 cursor_b_palette[4]; + u32 disp_a_stride; + u32 disp_b_stride; + u32 vgacntrl; + u32 add_id; + u32 swf0x[7]; + u32 swf1x[7]; + u32 swf3x[3]; + u32 fence[8]; + u32 instpm; + u32 mem_mode; + u32 fw_blc_0; + u32 fw_blc_1; +}; + +struct intelfb_heap_data { + u32 physical; + u8 __iomem *virtual; + u32 offset; // in GATT pages + u32 size; // in bytes +}; + +struct intelfb_info { + struct fb_info *info; + struct fb_ops *fbops; + struct pci_dev *pdev; + + struct intelfb_hwstate save_state; + + /* agpgart structs */ + struct agp_memory *gtt_fb_mem; // use all stolen memory or vram + struct agp_memory *gtt_ring_mem; // ring buffer + struct agp_memory *gtt_cursor_mem; // hw cursor + + /* use a gart reserved fb mem */ + u8 fbmem_gart; + + /* mtrr support */ + u32 mtrr_reg; + u32 has_mtrr; + + /* heap data */ + struct intelfb_heap_data aperture; + struct intelfb_heap_data fb; + struct intelfb_heap_data ring; + struct intelfb_heap_data cursor; + + /* mmio regs */ + u32 mmio_base_phys; + u8 __iomem *mmio_base; + + /* fb start offset (in bytes) */ + u32 fb_start; + + /* ring buffer */ + u8 __iomem *ring_head; + u32 ring_tail; + u32 ring_tail_mask; + u32 ring_space; + u32 ring_lockup; + + /* palette */ + u32 pseudo_palette[17]; + struct { u8 red, green, blue, pad; } palette[256]; + + /* chip info */ + int pci_chipset; + int chipset; + const char *name; + int mobile; + + /* current mode */ + int bpp, depth; + u32 visual; + int xres, yres, pitch; + int pixclock; + + /* current pipe */ + int pipe; + + /* some flags */ + int accel; + int hwcursor; + int fixed_mode; + int ring_active; + + /* hw cursor */ + int cursor_on; + int cursor_blanked; + u8 cursor_src[64]; + + /* initial parameters */ + int initial_vga; + struct fb_var_screeninfo initial_var; + u32 initial_fb_base; + u32 initial_video_ram; + u32 initial_pitch; + + /* driver registered */ + int registered; +}; + +/*** function prototypes ***/ + +extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var); + +#endif /* _INTELFB_H */ diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c new file mode 100644 index 00000000000..6a05b700083 --- /dev/null +++ b/drivers/video/intelfb/intelfbdrv.c @@ -0,0 +1,1570 @@ +/* + * intelfb + * + * Linux framebuffer driver for Intel(R) 830M/845G/852GM/855GM/865G/915G + * integrated graphics chips. + * + * Copyright © 2002, 2003 David Dawes <dawes@xfree86.org> + * 2004 Sylvain Meyer + * + * This driver consists of two parts. The first part (intelfbdrv.c) provides + * the basic fbdev interfaces, is derived in part from the radeonfb and + * vesafb drivers, and is covered by the GPL. The second part (intelfbhw.c) + * provides the code to program the hardware. Most of it is derived from + * the i810/i830 XFree86 driver. The HW-specific code is covered here + * under a dual license (GPL and MIT/XFree86 license). + * + * Author: David Dawes + * + */ + +/* $DHD: intelfb/intelfbdrv.c,v 1.20 2003/06/27 15:17:40 dawes Exp $ */ + +/* + * Changes: + * 01/2003 - Initial driver (0.1.0), no mode switching, no acceleration. + * This initial version is a basic core that works a lot like + * the vesafb driver. It must be built-in to the kernel, + * and the initial video mode must be set with vga=XXX at + * boot time. (David Dawes) + * + * 01/2003 - Version 0.2.0: Mode switching added, colormap support + * implemented, Y panning, and soft screen blanking implemented. + * No acceleration yet. (David Dawes) + * + * 01/2003 - Version 0.3.0: fbcon acceleration support added. Module + * option handling added. (David Dawes) + * + * 01/2003 - Version 0.4.0: fbcon HW cursor support added. (David Dawes) + * + * 01/2003 - Version 0.4.1: Add auto-generation of built-in modes. + * (David Dawes) + * + * 02/2003 - Version 0.4.2: Add check for active non-CRT devices, and + * mode validation checks. (David Dawes) + * + * 02/2003 - Version 0.4.3: Check when the VC is in graphics mode so that + * acceleration is disabled while an XFree86 server is running. + * (David Dawes) + * + * 02/2003 - Version 0.4.4: Monitor DPMS support. (David Dawes) + * + * 02/2003 - Version 0.4.5: Basic XFree86 + fbdev working. (David Dawes) + * + * 02/2003 - Version 0.5.0: Modify to work with the 2.5.32 kernel as well + * as 2.4.x kernels. (David Dawes) + * + * 02/2003 - Version 0.6.0: Split out HW-specifics into a separate file. + * (David Dawes) + * + * 02/2003 - Version 0.7.0: Test on 852GM/855GM. Acceleration and HW + * cursor are disabled on this platform. (David Dawes) + * + * 02/2003 - Version 0.7.1: Test on 845G. Acceleration is disabled + * on this platform. (David Dawes) + * + * 02/2003 - Version 0.7.2: Test on 830M. Acceleration and HW + * cursor are disabled on this platform. (David Dawes) + * + * 02/2003 - Version 0.7.3: Fix 8-bit modes for mobile platforms + * (David Dawes) + * + * 02/2003 - Version 0.7.4: Add checks for FB and FBCON_HAS_CFB* configured + * in the kernel, and add mode bpp verification and default + * bpp selection based on which FBCON_HAS_CFB* are configured. + * (David Dawes) + * + * 02/2003 - Version 0.7.5: Add basic package/install scripts based on the + * DRI packaging scripts. (David Dawes) + * + * 04/2003 - Version 0.7.6: Fix typo that affects builds with SMP-enabled + * kernels. (David Dawes, reported by Anupam). + * + * 06/2003 - Version 0.7.7: + * Fix Makefile.kernel build problem (Tsutomu Yasuda). + * Fix mis-placed #endif (2.4.21 kernel). + * + * 09/2004 - Version 0.9.0 - by Sylvain Meyer + * Port to linux 2.6 kernel fbdev + * Fix HW accel and HW cursor on i845G + * Use of agpgart for fb memory reservation + * Add mtrr support + * + * 10/2004 - Version 0.9.1 + * Use module_param instead of old MODULE_PARM + * Some cleanup + * + * 11/2004 - Version 0.9.2 + * Add vram option to reserve more memory than stolen by BIOS + * Fix intelfbhw_pan_display typo + * Add __initdata annotations + * + * TODO: + * + * + * Wish List: + * + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/console.h> +#include <linux/selection.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/vmalloc.h> +#include <linux/kd.h> +#include <linux/vt_kern.h> +#include <linux/pagemap.h> +#include <linux/version.h> + +#include <asm/io.h> + +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif + +#include "intelfb.h" +#include "intelfbdrv.h" +#include "intelfbhw.h" + +/* + * Limiting the class to PCI_CLASS_DISPLAY_VGA prevents function 1 of the + * mobile chipsets from being registered. + */ +#if DETECT_VGA_CLASS_ONLY +#define INTELFB_CLASS_MASK ~0 << 8 +#else +#define INTELFB_CLASS_MASK 0 +#endif + +static struct pci_device_id intelfb_pci_table[] __devinitdata = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_830M, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_830M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_845G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_845G }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_85XGM, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_85XGM }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_865G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_865G }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_915G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_915G }, + { 0, } +}; + +/* Global data */ +static int num_registered = 0; + +/* fb ops */ +static struct fb_ops intel_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = intelfb_check_var, + .fb_set_par = intelfb_set_par, + .fb_setcolreg = intelfb_setcolreg, + .fb_blank = intelfb_blank, + .fb_pan_display = intelfb_pan_display, + .fb_fillrect = intelfb_fillrect, + .fb_copyarea = intelfb_copyarea, + .fb_imageblit = intelfb_imageblit, + .fb_cursor = intelfb_cursor, + .fb_sync = intelfb_sync, + .fb_ioctl = intelfb_ioctl +}; + +/* PCI driver module table */ +static struct pci_driver intelfb_driver = { + .name = "Intel(R) " SUPPORTED_CHIPSETS " Framebuffer Driver", + .id_table = intelfb_pci_table, + .probe = intelfb_pci_register, + .remove = __devexit_p(intelfb_pci_unregister) +}; + +/* Module description/parameters */ +MODULE_AUTHOR("David Dawes <dawes@tungstengraphics.com>, " + "Sylvain Meyer <sylvain.meyer@worldonline.fr>"); +MODULE_DESCRIPTION( + "Framebuffer driver for Intel(R) " SUPPORTED_CHIPSETS " chipsets"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DEVICE_TABLE(pci, intelfb_pci_table); + +static int accel __initdata = 1; +static int vram __initdata = 4; +static int hwcursor __initdata = 1; +static int mtrr __initdata = 1; +static int fixed __initdata = 0; +static int noinit __initdata = 0; +static int noregister __initdata = 0; +static int probeonly __initdata = 0; +static int idonly __initdata = 0; +static int bailearly __initdata = 0; +static char *mode __initdata = NULL; + +module_param(accel, bool, S_IRUGO); +MODULE_PARM_DESC(accel, "Enable console acceleration"); +module_param(vram, int, S_IRUGO); +MODULE_PARM_DESC(vram, "System RAM to allocate to framebuffer in MiB"); +module_param(hwcursor, bool, S_IRUGO); +MODULE_PARM_DESC(hwcursor, "Enable HW cursor"); +module_param(mtrr, bool, S_IRUGO); +MODULE_PARM_DESC(mtrr, "Enable MTRR support"); +module_param(fixed, bool, S_IRUGO); +MODULE_PARM_DESC(fixed, "Disable mode switching"); +module_param(noinit, bool, 0); +MODULE_PARM_DESC(noinit, "Don't initialise graphics mode when loading"); +module_param(noregister, bool, 0); +MODULE_PARM_DESC(noregister, "Don't register, just probe and exit (debug)"); +module_param(probeonly, bool, 0); +MODULE_PARM_DESC(probeonly, "Do a minimal probe (debug)"); +module_param(idonly, bool, 0); +MODULE_PARM_DESC(idonly, "Just identify without doing anything else (debug)"); +module_param(bailearly, bool, 0); +MODULE_PARM_DESC(bailearly, "Bail out early, depending on value (debug)"); +module_param(mode, charp, S_IRUGO); +MODULE_PARM_DESC(mode, + "Initial video mode \"<xres>x<yres>[-<depth>][@<refresh>]\""); + +#ifndef MODULE +#define OPT_EQUAL(opt, name) (!strncmp(opt, name, strlen(name))) +#define OPT_INTVAL(opt, name) simple_strtoul(opt + strlen(name), NULL, 0) +#define OPT_STRVAL(opt, name) (opt + strlen(name)) + +static __inline__ char * +get_opt_string(const char *this_opt, const char *name) +{ + const char *p; + int i; + char *ret; + + p = OPT_STRVAL(this_opt, name); + i = 0; + while (p[i] && p[i] != ' ' && p[i] != ',') + i++; + ret = kmalloc(i + 1, GFP_KERNEL); + if (ret) { + strncpy(ret, p, i); + ret[i] = '\0'; + } + return ret; +} + +static __inline__ int +get_opt_int(const char *this_opt, const char *name, int *ret) +{ + if (!ret) + return 0; + + if (!OPT_EQUAL(this_opt, name)) + return 0; + + *ret = OPT_INTVAL(this_opt, name); + return 1; +} + +static __inline__ int +get_opt_bool(const char *this_opt, const char *name, int *ret) +{ + if (!ret) + return 0; + + if (OPT_EQUAL(this_opt, name)) { + if (this_opt[strlen(name)] == '=') + *ret = simple_strtoul(this_opt + strlen(name) + 1, + NULL, 0); + else + *ret = 1; + } else { + if (OPT_EQUAL(this_opt, "no") && OPT_EQUAL(this_opt + 2, name)) + *ret = 0; + else + return 0; + } + return 1; +} + +static int __init +intelfb_setup(char *options) +{ + char *this_opt; + + DBG_MSG("intelfb_setup\n"); + + if (!options || !*options) { + DBG_MSG("no options\n"); + return 0; + } else + DBG_MSG("options: %s\n", options); + + /* + * These are the built-in options analogous to the module parameters + * defined above. + * + * The syntax is: + * + * video=intelfb:[mode][,<param>=<val>] ... + * + * e.g., + * + * video=intelfb:1024x768-16@75,accel=0 + */ + + while ((this_opt = strsep(&options, ","))) { + if (!*this_opt) + continue; + if (get_opt_bool(this_opt, "accel", &accel)) + ; + else if (get_opt_int(this_opt, "vram", &vram)) + ; + else if (get_opt_bool(this_opt, "hwcursor", &hwcursor)) + ; + else if (get_opt_bool(this_opt, "mtrr", &mtrr)) + ; + else if (get_opt_bool(this_opt, "fixed", &fixed)) + ; + else if (get_opt_bool(this_opt, "init", &noinit)) + noinit = !noinit; + else if (OPT_EQUAL(this_opt, "mode=")) + mode = get_opt_string(this_opt, "mode="); + else + mode = this_opt; + } + + return 0; +} + +#endif + +static int __init +intelfb_init(void) +{ +#ifndef MODULE + char *option = NULL; +#endif + + DBG_MSG("intelfb_init\n"); + + INF_MSG("Framebuffer driver for " + "Intel(R) " SUPPORTED_CHIPSETS " chipsets\n"); + INF_MSG("Version " INTELFB_VERSION "\n"); + + if (idonly) + return -ENODEV; + +#ifndef MODULE + if (fb_get_options("intelfb", &option)) + return -ENODEV; + intelfb_setup(option); +#endif + + return pci_register_driver(&intelfb_driver); +} + +static void __exit +intelfb_exit(void) +{ + DBG_MSG("intelfb_exit\n"); + pci_unregister_driver(&intelfb_driver); +} + +module_init(intelfb_init); +module_exit(intelfb_exit); + +/*************************************************************** + * mtrr support functions * + ***************************************************************/ + +#ifdef CONFIG_MTRR +static inline void __devinit set_mtrr(struct intelfb_info *dinfo) +{ + dinfo->mtrr_reg = mtrr_add(dinfo->aperture.physical, + dinfo->aperture.size, MTRR_TYPE_WRCOMB, 1); + if (dinfo->mtrr_reg < 0) { + ERR_MSG("unable to set MTRR\n"); + return; + } + dinfo->has_mtrr = 1; +} +static inline void unset_mtrr(struct intelfb_info *dinfo) +{ + if (dinfo->has_mtrr) + mtrr_del(dinfo->mtrr_reg, dinfo->aperture.physical, + dinfo->aperture.size); +} +#else +#define set_mtrr(x) WRN_MSG("MTRR is disabled in the kernel\n") + +#define unset_mtrr(x) do { } while (0) +#endif /* CONFIG_MTRR */ + +/*************************************************************** + * driver init / cleanup * + ***************************************************************/ + +static void +cleanup(struct intelfb_info *dinfo) +{ + DBG_MSG("cleanup\n"); + + if (!dinfo) + return; + + fb_dealloc_cmap(&dinfo->info->cmap); + kfree(dinfo->info->pixmap.addr); + + if (dinfo->registered) + unregister_framebuffer(dinfo->info); + + unset_mtrr(dinfo); + + if (dinfo->fbmem_gart && dinfo->gtt_fb_mem) { + agp_unbind_memory(dinfo->gtt_fb_mem); + agp_free_memory(dinfo->gtt_fb_mem); + } + if (dinfo->gtt_cursor_mem) { + agp_unbind_memory(dinfo->gtt_cursor_mem); + agp_free_memory(dinfo->gtt_cursor_mem); + } + if (dinfo->gtt_ring_mem) { + agp_unbind_memory(dinfo->gtt_ring_mem); + agp_free_memory(dinfo->gtt_ring_mem); + } + + if (dinfo->mmio_base) + iounmap((void __iomem *)dinfo->mmio_base); + if (dinfo->aperture.virtual) + iounmap((void __iomem *)dinfo->aperture.virtual); + + if (dinfo->mmio_base_phys) + release_mem_region(dinfo->mmio_base_phys, INTEL_REG_SIZE); + if (dinfo->aperture.physical) + release_mem_region(dinfo->aperture.physical, + dinfo->aperture.size); + framebuffer_release(dinfo->info); +} + +#define bailout(dinfo) do { \ + DBG_MSG("bailout\n"); \ + cleanup(dinfo); \ + INF_MSG("Not going to register framebuffer, exiting...\n"); \ + return -ENODEV; \ +} while (0) + + +static int __devinit +intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct fb_info *info; + struct intelfb_info *dinfo; + int i, j, err, dvo; + int aperture_size, stolen_size; + struct agp_kern_info gtt_info; + int agp_memtype; + const char *s; + struct agp_bridge_data *bridge; + int aperture_bar = 0; + int mmio_bar = 1; + + DBG_MSG("intelfb_pci_register\n"); + + num_registered++; + if (num_registered != 1) { + ERR_MSG("Attempted to register %d devices " + "(should be only 1).\n", num_registered); + return -ENODEV; + } + + info = framebuffer_alloc(sizeof(struct intelfb_info), &pdev->dev); + if (!info) { + ERR_MSG("Could not allocate memory for intelfb_info.\n"); + return -ENODEV; + } + if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { + ERR_MSG("Could not allocate cmap for intelfb_info.\n"); + goto err_out_cmap; + return -ENODEV; + } + + dinfo = info->par; + dinfo->info = info; + dinfo->fbops = &intel_fb_ops; + dinfo->pdev = pdev; + + /* Reserve pixmap space. */ + info->pixmap.addr = kmalloc(64 * 1024, GFP_KERNEL); + if (info->pixmap.addr == NULL) { + ERR_MSG("Cannot reserve pixmap memory.\n"); + goto err_out_pixmap; + } + memset(info->pixmap.addr, 0, 64 * 1024); + + /* set early this option because it could be changed by tv encoder + driver */ + dinfo->fixed_mode = fixed; + + /* Enable device. */ + if ((err = pci_enable_device(pdev))) { + ERR_MSG("Cannot enable device.\n"); + cleanup(dinfo); + return -ENODEV; + } + + /* Set base addresses. */ + if (ent->device == PCI_DEVICE_ID_INTEL_915G) { + aperture_bar = 2; + mmio_bar = 0; + /* Disable HW cursor on 915G (not implemented yet) */ + hwcursor = 0; + } + dinfo->aperture.physical = pci_resource_start(pdev, aperture_bar); + dinfo->aperture.size = pci_resource_len(pdev, aperture_bar); + dinfo->mmio_base_phys = pci_resource_start(pdev, mmio_bar); + DBG_MSG("fb aperture: 0x%llx/0x%llx, MMIO region: 0x%llx/0x%llx\n", + (unsigned long long)pci_resource_start(pdev, aperture_bar), + (unsigned long long)pci_resource_len(pdev, aperture_bar), + (unsigned long long)pci_resource_start(pdev, mmio_bar), + (unsigned long long)pci_resource_len(pdev, mmio_bar)); + + /* Reserve the fb and MMIO regions */ + if (!request_mem_region(dinfo->aperture.physical, dinfo->aperture.size, + INTELFB_MODULE_NAME)) { + ERR_MSG("Cannot reserve FB region.\n"); + cleanup(dinfo); + return -ENODEV; + } + if (!request_mem_region(dinfo->mmio_base_phys, + INTEL_REG_SIZE, + INTELFB_MODULE_NAME)) { + ERR_MSG("Cannot reserve MMIO region.\n"); + cleanup(dinfo); + return -ENODEV; + } + + /* Map the fb and MMIO regions */ + dinfo->aperture.virtual = (u8 __iomem *)ioremap_nocache + (dinfo->aperture.physical, dinfo->aperture.size); + if (!dinfo->aperture.virtual) { + ERR_MSG("Cannot remap FB region.\n"); + cleanup(dinfo); + return -ENODEV; + } + dinfo->mmio_base = + (u8 __iomem *)ioremap_nocache(dinfo->mmio_base_phys, + INTEL_REG_SIZE); + if (!dinfo->mmio_base) { + ERR_MSG("Cannot remap MMIO region.\n"); + cleanup(dinfo); + return -ENODEV; + } + + /* Get the chipset info. */ + dinfo->pci_chipset = pdev->device; + + if (intelfbhw_get_chipset(pdev, &dinfo->name, &dinfo->chipset, + &dinfo->mobile)) { + cleanup(dinfo); + return -ENODEV; + } + + if (intelfbhw_get_memory(pdev, &aperture_size,&stolen_size)) { + cleanup(dinfo); + return -ENODEV; + } + + INF_MSG("%02x:%02x.%d: %s, aperture size %dMB, " + "stolen memory %dkB\n", + pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), dinfo->name, + BtoMB(aperture_size), BtoKB(stolen_size)); + + /* Set these from the options. */ + dinfo->accel = accel; + dinfo->hwcursor = hwcursor; + + if (NOACCEL_CHIPSET(dinfo) && dinfo->accel == 1) { + INF_MSG("Acceleration is not supported for the %s chipset.\n", + dinfo->name); + dinfo->accel = 0; + } + + /* Framebuffer parameters - Use all the stolen memory if >= vram */ + if (ROUND_UP_TO_PAGE(stolen_size) >= MB(vram)) { + dinfo->fb.size = ROUND_UP_TO_PAGE(stolen_size); + dinfo->fbmem_gart = 0; + } else { + dinfo->fb.size = MB(vram); + dinfo->fbmem_gart = 1; + } + + /* Allocate space for the ring buffer and HW cursor if enabled. */ + if (dinfo->accel) { + dinfo->ring.size = RINGBUFFER_SIZE; + dinfo->ring_tail_mask = dinfo->ring.size - 1; + } + if (dinfo->hwcursor) { + dinfo->cursor.size = HW_CURSOR_SIZE; + } + + /* Use agpgart to manage the GATT */ + if (!(bridge = agp_backend_acquire(pdev))) { + ERR_MSG("cannot acquire agp\n"); + cleanup(dinfo); + return -ENODEV; + } + + /* get the current gatt info */ + if (agp_copy_info(bridge, >t_info)) { + ERR_MSG("cannot get agp info\n"); + agp_backend_release(bridge); + cleanup(dinfo); + return -ENODEV; + } + + /* set the mem offsets - set them after the already used pages */ + if (dinfo->accel) { + dinfo->ring.offset = (stolen_size >> 12) + + gtt_info.current_memory; + } + if (dinfo->hwcursor) { + dinfo->cursor.offset = (stolen_size >> 12) + + + gtt_info.current_memory + (dinfo->ring.size >> 12); + } + if (dinfo->fbmem_gart) { + dinfo->fb.offset = (stolen_size >> 12) + + + gtt_info.current_memory + (dinfo->ring.size >> 12) + + (dinfo->cursor.size >> 12); + } + + /* Allocate memories (which aren't stolen) */ + if (dinfo->accel) { + if (!(dinfo->gtt_ring_mem = + agp_allocate_memory(bridge, dinfo->ring.size >> 12, + AGP_NORMAL_MEMORY))) { + ERR_MSG("cannot allocate ring buffer memory\n"); + agp_backend_release(bridge); + cleanup(dinfo); + return -ENOMEM; + } + if (agp_bind_memory(dinfo->gtt_ring_mem, + dinfo->ring.offset)) { + ERR_MSG("cannot bind ring buffer memory\n"); + agp_backend_release(bridge); + cleanup(dinfo); + return -EBUSY; + } + dinfo->ring.physical = dinfo->aperture.physical + + (dinfo->ring.offset << 12); + dinfo->ring.virtual = dinfo->aperture.virtual + + (dinfo->ring.offset << 12); + dinfo->ring_head = dinfo->ring.virtual; + } + if (dinfo->hwcursor) { + agp_memtype = dinfo->mobile ? AGP_PHYSICAL_MEMORY + : AGP_NORMAL_MEMORY; + if (!(dinfo->gtt_cursor_mem = + agp_allocate_memory(bridge, dinfo->cursor.size >> 12, + agp_memtype))) { + ERR_MSG("cannot allocate cursor memory\n"); + agp_backend_release(bridge); + cleanup(dinfo); + return -ENOMEM; + } + if (agp_bind_memory(dinfo->gtt_cursor_mem, + dinfo->cursor.offset)) { + ERR_MSG("cannot bind cursor memory\n"); + agp_backend_release(bridge); + cleanup(dinfo); + return -EBUSY; + } + if (dinfo->mobile) + dinfo->cursor.physical + = dinfo->gtt_cursor_mem->physical; + else + dinfo->cursor.physical = dinfo->aperture.physical + + (dinfo->cursor.offset << 12); + dinfo->cursor.virtual = dinfo->aperture.virtual + + (dinfo->cursor.offset << 12); + } + if (dinfo->fbmem_gart) { + if (!(dinfo->gtt_fb_mem = + agp_allocate_memory(bridge, dinfo->fb.size >> 12, + AGP_NORMAL_MEMORY))) { + WRN_MSG("cannot allocate framebuffer memory - use " + "the stolen one\n"); + dinfo->fbmem_gart = 0; + } + if (agp_bind_memory(dinfo->gtt_fb_mem, + dinfo->fb.offset)) { + WRN_MSG("cannot bind framebuffer memory - use " + "the stolen one\n"); + dinfo->fbmem_gart = 0; + } + } + + /* update framebuffer memory parameters */ + if (!dinfo->fbmem_gart) + dinfo->fb.offset = 0; /* starts at offset 0 */ + dinfo->fb.physical = dinfo->aperture.physical + + (dinfo->fb.offset << 12); + dinfo->fb.virtual = dinfo->aperture.virtual + (dinfo->fb.offset << 12); + dinfo->fb_start = dinfo->fb.offset << 12; + + /* release agpgart */ + agp_backend_release(bridge); + + if (mtrr) + set_mtrr(dinfo); + + DBG_MSG("fb: 0x%x(+ 0x%x)/0x%x (0x%x)\n", + dinfo->fb.physical, dinfo->fb.offset, dinfo->fb.size, + (u32 __iomem ) dinfo->fb.virtual); + DBG_MSG("MMIO: 0x%x/0x%x (0x%x)\n", + dinfo->mmio_base_phys, INTEL_REG_SIZE, + (u32 __iomem) dinfo->mmio_base); + DBG_MSG("ring buffer: 0x%x/0x%x (0x%x)\n", + dinfo->ring.physical, dinfo->ring.size, + (u32 __iomem ) dinfo->ring.virtual); + DBG_MSG("HW cursor: 0x%x/0x%x (0x%x) (offset 0x%x) (phys 0x%x)\n", + dinfo->cursor.physical, dinfo->cursor.size, + (u32 __iomem ) dinfo->cursor.virtual, dinfo->cursor.offset, + dinfo->cursor.physical); + + DBG_MSG("options: vram = %d, accel = %d, hwcursor = %d, fixed = %d, " + "noinit = %d\n", vram, accel, hwcursor, fixed, noinit); + DBG_MSG("options: mode = \"%s\"\n", mode ? mode : ""); + + if (probeonly) + bailout(dinfo); + + /* + * Check if the LVDS port or any DVO ports are enabled. If so, + * don't allow mode switching + */ + dvo = intelfbhw_check_non_crt(dinfo); + if (dvo) { + dinfo->fixed_mode = 1; + WRN_MSG("Non-CRT device is enabled ( "); + i = 0; + while (dvo) { + if (dvo & 1) { + s = intelfbhw_dvo_to_string(1 << i); + if (s) + printk("%s ", s); + } + dvo >>= 1; + ++i; + } + printk("). Disabling mode switching.\n"); + } + + if (bailearly == 1) + bailout(dinfo); + + if (FIXED_MODE(dinfo) && ORIG_VIDEO_ISVGA != VIDEO_TYPE_VLFB) { + ERR_MSG("Video mode must be programmed at boot time.\n"); + cleanup(dinfo); + return -ENODEV; + } + + if (bailearly == 2) + bailout(dinfo); + + /* Initialise dinfo and related data. */ + /* If an initial mode was programmed at boot time, get its details. */ + if (ORIG_VIDEO_ISVGA == VIDEO_TYPE_VLFB) + get_initial_mode(dinfo); + + if (bailearly == 3) + bailout(dinfo); + + if (FIXED_MODE(dinfo)) { + /* remap fb address */ + update_dinfo(dinfo, &dinfo->initial_var); + } + + if (bailearly == 4) + bailout(dinfo); + + + if (intelfb_set_fbinfo(dinfo)) { + cleanup(dinfo); + return -ENODEV; + } + + if (bailearly == 5) + bailout(dinfo); + + for (i = 0; i < 16; i++) { + j = color_table[i]; + dinfo->palette[i].red = default_red[j]; + dinfo->palette[i].green = default_grn[j]; + dinfo->palette[i].blue = default_blu[j]; + } + + if (bailearly == 6) + bailout(dinfo); + + pci_set_drvdata(pdev, dinfo); + + /* Save the initial register state. */ + i = intelfbhw_read_hw_state(dinfo, &dinfo->save_state, + bailearly > 6 ? bailearly - 6 : 0); + if (i != 0) { + DBG_MSG("intelfbhw_read_hw_state returned %d\n", i); + bailout(dinfo); + } + + intelfbhw_print_hw_state(dinfo, &dinfo->save_state); + + if (bailearly == 18) + bailout(dinfo); + + /* Cursor initialisation */ + if (dinfo->hwcursor) { + intelfbhw_cursor_init(dinfo); + intelfbhw_cursor_reset(dinfo); + } + + if (bailearly == 19) + bailout(dinfo); + + /* 2d acceleration init */ + if (dinfo->accel) + intelfbhw_2d_start(dinfo); + + if (bailearly == 20) + bailout(dinfo); + + if (noregister) + bailout(dinfo); + + if (register_framebuffer(dinfo->info) < 0) { + ERR_MSG("Cannot register framebuffer.\n"); + cleanup(dinfo); + return -ENODEV; + } + + dinfo->registered = 1; + + return 0; + +err_out_pixmap: + fb_dealloc_cmap(&info->cmap); +err_out_cmap: + framebuffer_release(info); + return -ENODEV; +} + +static void __devexit +intelfb_pci_unregister(struct pci_dev *pdev) +{ + struct intelfb_info *dinfo = pci_get_drvdata(pdev); + + DBG_MSG("intelfb_pci_unregister\n"); + + if (!dinfo) + return; + + cleanup(dinfo); + + pci_set_drvdata(pdev, NULL); +} + +/*************************************************************** + * helper functions * + ***************************************************************/ + +int __inline__ +intelfb_var_to_depth(const struct fb_var_screeninfo *var) +{ + DBG_MSG("intelfb_var_to_depth: bpp: %d, green.length is %d\n", + var->bits_per_pixel, var->green.length); + + switch (var->bits_per_pixel) { + case 16: + return (var->green.length == 6) ? 16 : 15; + case 32: + return 24; + default: + return var->bits_per_pixel; + } +} + + +static __inline__ int +var_to_refresh(const struct fb_var_screeninfo *var) +{ + int xtot = var->xres + var->left_margin + var->right_margin + + var->hsync_len; + int ytot = var->yres + var->upper_margin + var->lower_margin + + var->vsync_len; + + return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot; +} + +/*************************************************************** + * Various intialisation functions * + ***************************************************************/ + +static void __devinit +get_initial_mode(struct intelfb_info *dinfo) +{ + struct fb_var_screeninfo *var; + int xtot, ytot; + + DBG_MSG("get_initial_mode\n"); + + dinfo->initial_vga = 1; + dinfo->initial_fb_base = screen_info.lfb_base; + dinfo->initial_video_ram = screen_info.lfb_size * KB(64); + dinfo->initial_pitch = screen_info.lfb_linelength; + + var = &dinfo->initial_var; + memset(var, 0, sizeof(*var)); + var->xres = screen_info.lfb_width; + var->yres = screen_info.lfb_height; + var->bits_per_pixel = screen_info.lfb_depth; + switch (screen_info.lfb_depth) { + case 15: + var->bits_per_pixel = 16; + break; + case 24: + var->bits_per_pixel = 32; + break; + } + + DBG_MSG("Initial info: FB is 0x%x/0x%x (%d kByte)\n", + dinfo->initial_fb_base, dinfo->initial_video_ram, + BtoKB(dinfo->initial_video_ram)); + + DBG_MSG("Initial info: mode is %dx%d-%d (%d)\n", + var->xres, var->yres, var->bits_per_pixel, + dinfo->initial_pitch); + + /* Dummy timing values (assume 60Hz) */ + var->left_margin = (var->xres / 8) & 0xf8; + var->right_margin = 32; + var->upper_margin = 16; + var->lower_margin = 4; + var->hsync_len = (var->xres / 8) & 0xf8; + var->vsync_len = 4; + + xtot = var->xres + var->left_margin + + var->right_margin + var->hsync_len; + ytot = var->yres + var->upper_margin + + var->lower_margin + var->vsync_len; + var->pixclock = 10000000 / xtot * 1000 / ytot * 100 / 60; + + var->height = -1; + var->width = -1; + + if (var->bits_per_pixel > 8) { + var->red.offset = screen_info.red_pos; + var->red.length = screen_info.red_size; + var->green.offset = screen_info.green_pos; + var->green.length = screen_info.green_size; + var->blue.offset = screen_info.blue_pos; + var->blue.length = screen_info.blue_size; + var->transp.offset = screen_info.rsvd_pos; + var->transp.length = screen_info.rsvd_size; + } else { + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + } +} + +static int __devinit +intelfb_init_var(struct intelfb_info *dinfo) +{ + struct fb_var_screeninfo *var; + int msrc = 0; + + DBG_MSG("intelfb_init_var\n"); + + var = &dinfo->info->var; + if (FIXED_MODE(dinfo)) { + memcpy(var, &dinfo->initial_var, + sizeof(struct fb_var_screeninfo)); + msrc = 5; + } else { + if (mode) { + msrc = fb_find_mode(var, dinfo->info, mode, + vesa_modes, VESA_MODEDB_SIZE, + NULL, 0); + if (msrc) + msrc |= 8; + } + if (!msrc) { + msrc = fb_find_mode(var, dinfo->info, PREFERRED_MODE, + vesa_modes, VESA_MODEDB_SIZE, + NULL, 0); + } + } + + if (!msrc) { + ERR_MSG("Cannot find a suitable video mode.\n"); + return 1; + } + + INF_MSG("Initial video mode is %dx%d-%d@%d.\n", var->xres, var->yres, + var->bits_per_pixel, var_to_refresh(var)); + + DBG_MSG("Initial video mode is from %d.\n", msrc); + +#if ALLOCATE_FOR_PANNING + /* Allow use of half of the video ram for panning */ + var->xres_virtual = var->xres; + var->yres_virtual = + dinfo->fb.size / 2 / (var->bits_per_pixel * var->xres); + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; +#else + var->yres_virtual = var->yres; +#endif + + if (dinfo->accel) + var->accel_flags |= FB_ACCELF_TEXT; + else + var->accel_flags &= ~FB_ACCELF_TEXT; + + return 0; +} + +static int __devinit +intelfb_set_fbinfo(struct intelfb_info *dinfo) +{ + struct fb_info *info = dinfo->info; + + DBG_MSG("intelfb_set_fbinfo\n"); + + info->flags = FBINFO_FLAG_DEFAULT; + info->fbops = &intel_fb_ops; + info->pseudo_palette = dinfo->pseudo_palette; + + info->pixmap.size = 64*1024; + info->pixmap.buf_align = 8; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + + if (intelfb_init_var(dinfo)) + return 1; + + info->pixmap.scan_align = 1; + + update_dinfo(dinfo, &info->var); + + return 0; +} + +/* Update dinfo to match the active video mode. */ +static void +update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var) +{ + DBG_MSG("update_dinfo\n"); + + dinfo->bpp = var->bits_per_pixel; + dinfo->depth = intelfb_var_to_depth(var); + dinfo->xres = var->xres; + dinfo->yres = var->xres; + dinfo->pixclock = var->pixclock; + + intelfb_get_fix(&dinfo->info->fix, dinfo->info); + + switch (dinfo->bpp) { + case 8: + dinfo->visual = FB_VISUAL_PSEUDOCOLOR; + dinfo->pitch = var->xres_virtual; + break; + case 16: + dinfo->visual = FB_VISUAL_TRUECOLOR; + dinfo->pitch = var->xres_virtual * 2; + break; + case 32: + dinfo->visual = FB_VISUAL_TRUECOLOR; + dinfo->pitch = var->xres_virtual * 4; + break; + } + + /* Make sure the line length is a aligned correctly. */ + dinfo->pitch = ROUND_UP_TO(dinfo->pitch, STRIDE_ALIGNMENT); + + if (FIXED_MODE(dinfo)) + dinfo->pitch = dinfo->initial_pitch; + + dinfo->info->screen_base = (char __iomem *)dinfo->fb.virtual; + dinfo->info->fix.line_length = dinfo->pitch; + dinfo->info->fix.visual = dinfo->visual; +} + +/* fbops functions */ + +static int +intelfb_get_fix(struct fb_fix_screeninfo *fix, struct fb_info *info) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + + DBG_MSG("intelfb_get_fix\n"); + + memset(fix, 0, sizeof(*fix)); + strcpy(fix->id, dinfo->name); + fix->smem_start = dinfo->fb.physical; + fix->smem_len = dinfo->fb.size; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + fix->visual = dinfo->visual; + fix->xpanstep = 8; + fix->ypanstep = 1; + fix->ywrapstep = 0; + fix->line_length = dinfo->pitch; + fix->mmio_start = dinfo->mmio_base_phys; + fix->mmio_len = INTEL_REG_SIZE; + fix->accel = FB_ACCEL_I830; + return 0; +} + +/*************************************************************** + * fbdev interface * + ***************************************************************/ + +static int +intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + int change_var = 0; + struct fb_var_screeninfo v; + struct intelfb_info *dinfo; + static int first = 1; + + DBG_MSG("intelfb_check_var: accel_flags is %d\n", var->accel_flags); + + dinfo = GET_DINFO(info); + + if (intelfbhw_validate_mode(dinfo, var) != 0) + return -EINVAL; + + v = *var; + + /* Check for a supported bpp. */ + if (v.bits_per_pixel <= 8) { + v.bits_per_pixel = 8; + } else if (v.bits_per_pixel <= 16) { + if (v.bits_per_pixel == 16) + v.green.length = 6; + v.bits_per_pixel = 16; + } else if (v.bits_per_pixel <= 32) { + v.bits_per_pixel = 32; + } else + return -EINVAL; + + change_var = ((info->var.xres != var->xres) || + (info->var.yres != var->yres) || + (info->var.xres_virtual != var->xres_virtual) || + (info->var.yres_virtual != var->yres_virtual) || + (info->var.bits_per_pixel != var->bits_per_pixel) || + memcmp(&info->var.red, &var->red, sizeof(var->red)) || + memcmp(&info->var.green, &var->green, + sizeof(var->green)) || + memcmp(&info->var.blue, &var->blue, sizeof(var->blue))); + + if (FIXED_MODE(dinfo) && + (change_var || + var->yres_virtual > dinfo->initial_var.yres_virtual || + var->yres_virtual < dinfo->initial_var.yres || + var->xoffset || var->nonstd)) { + if (first) { + ERR_MSG("Changing the video mode is not supported.\n"); + first = 0; + } + return -EINVAL; + } + + switch (intelfb_var_to_depth(&v)) { + case 8: + v.red.offset = v.green.offset = v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 8; + v.transp.offset = v.transp.length = 0; + break; + case 15: + v.red.offset = 10; + v.green.offset = 5; + v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 5; + v.transp.offset = v.transp.length = 0; + break; + case 16: + v.red.offset = 11; + v.green.offset = 5; + v.blue.offset = 0; + v.red.length = 5; + v.green.length = 6; + v.blue.length = 5; + v.transp.offset = v.transp.length = 0; + break; + case 24: + v.red.offset = 16; + v.green.offset = 8; + v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 8; + v.transp.offset = v.transp.length = 0; + break; + case 32: + v.red.offset = 16; + v.green.offset = 8; + v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 8; + v.transp.offset = 24; + v.transp.length = 8; + break; + } + + if (v.xoffset < 0) + v.xoffset = 0; + if (v.yoffset < 0) + v.yoffset = 0; + + if (v.xoffset > v.xres_virtual - v.xres) + v.xoffset = v.xres_virtual - v.xres; + if (v.yoffset > v.yres_virtual - v.yres) + v.yoffset = v.yres_virtual - v.yres; + + v.red.msb_right = v.green.msb_right = v.blue.msb_right = + v.transp.msb_right = 0; + + *var = v; + + return 0; +} + +static int +intelfb_set_par(struct fb_info *info) +{ + struct intelfb_hwstate *hw; + struct intelfb_info *dinfo = GET_DINFO(info); + + if (FIXED_MODE(dinfo)) { + ERR_MSG("Changing the video mode is not supported.\n"); + return -EINVAL; + } + + hw = kmalloc(sizeof(*hw), GFP_ATOMIC); + if (!hw) + return -ENOMEM; + + DBG_MSG("intelfb_set_par (%dx%d-%d)\n", info->var.xres, + info->var.yres, info->var.bits_per_pixel); + + intelfb_blank(FB_BLANK_POWERDOWN, info); + + if (dinfo->accel) + intelfbhw_2d_stop(dinfo); + + memcpy(hw, &dinfo->save_state, sizeof(*hw)); + if (intelfbhw_mode_to_hw(dinfo, hw, &info->var)) + goto invalid_mode; + if (intelfbhw_program_mode(dinfo, hw, 0)) + goto invalid_mode; + +#if REGDUMP > 0 + intelfbhw_read_hw_state(dinfo, hw, 0); + intelfbhw_print_hw_state(dinfo, hw); +#endif + + update_dinfo(dinfo, &info->var); + + if (dinfo->accel) + intelfbhw_2d_start(dinfo); + + intelfb_pan_display(&info->var, info); + + intelfb_blank(FB_BLANK_UNBLANK, info); + + if (ACCEL(dinfo, info)) { + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN | + FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_IMAGEBLIT; + } else { + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; + } + kfree(hw); + return 0; +invalid_mode: + kfree(hw); + return -EINVAL; +} + +static int +intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + +#if VERBOSE > 0 + DBG_MSG("intelfb_setcolreg: regno %d, depth %d\n", regno, dinfo->depth); +#endif + + if (regno > 255) + return 1; + + switch (dinfo->depth) { + case 8: + { + red >>= 8; + green >>= 8; + blue >>= 8; + + dinfo->palette[regno].red = red; + dinfo->palette[regno].green = green; + dinfo->palette[regno].blue = blue; + + intelfbhw_setcolreg(dinfo, regno, red, green, blue, + transp); + } + break; + case 15: + dinfo->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + dinfo->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + dinfo->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + return 0; +} + +static int +intelfb_blank(int blank, struct fb_info *info) +{ + intelfbhw_do_blank(blank, info); + return 0; +} + +static int +intelfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + intelfbhw_pan_display(var, info); + return 0; +} + +/* When/if we have our own ioctls. */ +static int +intelfb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, struct fb_info *info) +{ + int retval = 0; + + return retval; +} + +static void +intelfb_fillrect (struct fb_info *info, const struct fb_fillrect *rect) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + u32 rop, color; + +#if VERBOSE > 0 + DBG_MSG("intelfb_fillrect\n"); +#endif + + if (!ACCEL(dinfo, info) || dinfo->depth == 4) + return cfb_fillrect(info, rect); + + if (rect->rop == ROP_COPY) + rop = PAT_ROP_GXCOPY; + else // ROP_XOR + rop = PAT_ROP_GXXOR; + + if (dinfo->depth != 8) + color = dinfo->pseudo_palette[rect->color]; + else + color = rect->color; + + intelfbhw_do_fillrect(dinfo, rect->dx, rect->dy, + rect->width, rect->height, color, + dinfo->pitch, info->var.bits_per_pixel, + rop); +} + +static void +intelfb_copyarea(struct fb_info *info, const struct fb_copyarea *region) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + +#if VERBOSE > 0 + DBG_MSG("intelfb_copyarea\n"); +#endif + + if (!ACCEL(dinfo, info) || dinfo->depth == 4) + return cfb_copyarea(info, region); + + intelfbhw_do_bitblt(dinfo, region->sx, region->sy, region->dx, + region->dy, region->width, region->height, + dinfo->pitch, info->var.bits_per_pixel); +} + +static void +intelfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + u32 fgcolor, bgcolor; + +#if VERBOSE > 0 + DBG_MSG("intelfb_imageblit\n"); +#endif + + if (!ACCEL(dinfo, info) || dinfo->depth == 4 + || image->depth != 1) + return cfb_imageblit(info, image); + + if (dinfo->depth != 8) { + fgcolor = dinfo->pseudo_palette[image->fg_color]; + bgcolor = dinfo->pseudo_palette[image->bg_color]; + } else { + fgcolor = image->fg_color; + bgcolor = image->bg_color; + } + + if (!intelfbhw_do_drawglyph(dinfo, fgcolor, bgcolor, image->width, + image->height, image->data, + image->dx, image->dy, + dinfo->pitch, info->var.bits_per_pixel)) + return cfb_imageblit(info, image); +} + +static int +intelfb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + +#if VERBOSE > 0 + DBG_MSG("intelfb_cursor\n"); +#endif + + if (!dinfo->hwcursor) + return soft_cursor(info, cursor); + + intelfbhw_cursor_hide(dinfo); + + /* If XFree killed the cursor - restore it */ + if (INREG(CURSOR_A_BASEADDR) != dinfo->cursor.offset << 12) { + u32 fg, bg; + + DBG_MSG("the cursor was killed - restore it !!\n"); + DBG_MSG("size %d, %d pos %d, %d\n", + cursor->image.width, cursor->image.height, + cursor->image.dx, cursor->image.dy); + + intelfbhw_cursor_init(dinfo); + intelfbhw_cursor_reset(dinfo); + intelfbhw_cursor_setpos(dinfo, cursor->image.dx, + cursor->image.dy); + + if (dinfo->depth != 8) { + fg =dinfo->pseudo_palette[cursor->image.fg_color]; + bg =dinfo->pseudo_palette[cursor->image.bg_color]; + } else { + fg = cursor->image.fg_color; + bg = cursor->image.bg_color; + } + intelfbhw_cursor_setcolor(dinfo, bg, fg); + intelfbhw_cursor_load(dinfo, cursor->image.width, + cursor->image.height, + dinfo->cursor_src); + + if (cursor->enable) + intelfbhw_cursor_show(dinfo); + return 0; + } + + if (cursor->set & FB_CUR_SETPOS) { + u32 dx, dy; + + dx = cursor->image.dx - info->var.xoffset; + dy = cursor->image.dy - info->var.yoffset; + + intelfbhw_cursor_setpos(dinfo, dx, dy); + } + + if (cursor->set & FB_CUR_SETSIZE) { + if (cursor->image.width > 64 || cursor->image.height > 64) + return -ENXIO; + + intelfbhw_cursor_reset(dinfo); + } + + if (cursor->set & FB_CUR_SETCMAP) { + u32 fg, bg; + + if (dinfo->depth != 8) { + fg = dinfo->pseudo_palette[cursor->image.fg_color]; + bg = dinfo->pseudo_palette[cursor->image.bg_color]; + } else { + fg = cursor->image.fg_color; + bg = cursor->image.bg_color; + } + + intelfbhw_cursor_setcolor(dinfo, bg, fg); + } + + if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) { + u32 s_pitch = (ROUND_UP_TO(cursor->image.width, 8) / 8); + u32 size = s_pitch * cursor->image.height; + u8 *dat = (u8 *) cursor->image.data; + u8 *msk = (u8 *) cursor->mask; + u8 src[64]; + u32 i; + + if (cursor->image.depth != 1) + return -ENXIO; + + switch (cursor->rop) { + case ROP_XOR: + for (i = 0; i < size; i++) + src[i] = dat[i] ^ msk[i]; + break; + case ROP_COPY: + default: + for (i = 0; i < size; i++) + src[i] = dat[i] & msk[i]; + break; + } + + /* save the bitmap to restore it when XFree will + make the cursor dirty */ + memcpy(dinfo->cursor_src, src, size); + + intelfbhw_cursor_load(dinfo, cursor->image.width, + cursor->image.height, src); + } + + if (cursor->enable) + intelfbhw_cursor_show(dinfo); + + return 0; +} + +static int +intelfb_sync(struct fb_info *info) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + +#if VERBOSE > 0 + DBG_MSG("intelfb_sync\n"); +#endif + + if (dinfo->ring_lockup) + return 0; + + intelfbhw_do_sync(dinfo); + return 0; +} + diff --git a/drivers/video/intelfb/intelfbdrv.h b/drivers/video/intelfb/intelfbdrv.h new file mode 100644 index 00000000000..cc305812888 --- /dev/null +++ b/drivers/video/intelfb/intelfbdrv.h @@ -0,0 +1,68 @@ +#ifndef _INTELFBDRV_H +#define _INTELFBDRV_H + +/* + ****************************************************************************** + * intelfb + * + * Linux framebuffer driver for Intel(R) 830M/845G/852GM/855GM/865G/915G + * integrated graphics chips. + * + * Copyright © 2004 Sylvain Meyer + * + * Author: Sylvain Meyer + * + ****************************************************************************** + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +static void __devinit get_initial_mode(struct intelfb_info *dinfo); +static void update_dinfo(struct intelfb_info *dinfo, + struct fb_var_screeninfo *var); +static int intelfb_get_fix(struct fb_fix_screeninfo *fix, + struct fb_info *info); + +static int intelfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int intelfb_set_par(struct fb_info *info); +static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info); + +static int intelfb_blank(int blank, struct fb_info *info); +static int intelfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); + +static void intelfb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect); +static void intelfb_copyarea(struct fb_info *info, + const struct fb_copyarea *region); +static void intelfb_imageblit(struct fb_info *info, + const struct fb_image *image); +static int intelfb_cursor(struct fb_info *info, + struct fb_cursor *cursor); + +static int intelfb_sync(struct fb_info *info); + +static int intelfb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, + struct fb_info *info); + +static int __devinit intelfb_pci_register(struct pci_dev *pdev, + const struct pci_device_id *ent); +static void __devexit intelfb_pci_unregister(struct pci_dev *pdev); +static int __devinit intelfb_set_fbinfo(struct intelfb_info *dinfo); + +#endif diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c new file mode 100644 index 00000000000..f5bed581dc4 --- /dev/null +++ b/drivers/video/intelfb/intelfbhw.c @@ -0,0 +1,1780 @@ +/* + * intelfb + * + * Linux framebuffer driver for Intel(R) 865G integrated graphics chips. + * + * Copyright © 2002, 2003 David Dawes <dawes@xfree86.org> + * 2004 Sylvain Meyer + * + * This driver consists of two parts. The first part (intelfbdrv.c) provides + * the basic fbdev interfaces, is derived in part from the radeonfb and + * vesafb drivers, and is covered by the GPL. The second part (intelfbhw.c) + * provides the code to program the hardware. Most of it is derived from + * the i810/i830 XFree86 driver. The HW-specific code is covered here + * under a dual license (GPL and MIT/XFree86 license). + * + * Author: David Dawes + * + */ + +/* $DHD: intelfb/intelfbhw.c,v 1.9 2003/06/27 15:06:25 dawes Exp $ */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/console.h> +#include <linux/selection.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/vmalloc.h> +#include <linux/kd.h> +#include <linux/vt_kern.h> +#include <linux/pagemap.h> +#include <linux/version.h> + +#include <asm/io.h> + +#include "intelfb.h" +#include "intelfbhw.h" + +int +intelfbhw_get_chipset(struct pci_dev *pdev, const char **name, int *chipset, + int *mobile) +{ + u32 tmp; + + if (!pdev || !name || !chipset || !mobile) + return 1; + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_830M: + *name = "Intel(R) 830M"; + *chipset = INTEL_830M; + *mobile = 1; + return 0; + case PCI_DEVICE_ID_INTEL_845G: + *name = "Intel(R) 845G"; + *chipset = INTEL_845G; + *mobile = 0; + return 0; + case PCI_DEVICE_ID_INTEL_85XGM: + tmp = 0; + *mobile = 1; + pci_read_config_dword(pdev, INTEL_85X_CAPID, &tmp); + switch ((tmp >> INTEL_85X_VARIANT_SHIFT) & + INTEL_85X_VARIANT_MASK) { + case INTEL_VAR_855GME: + *name = "Intel(R) 855GME"; + *chipset = INTEL_855GME; + return 0; + case INTEL_VAR_855GM: + *name = "Intel(R) 855GM"; + *chipset = INTEL_855GM; + return 0; + case INTEL_VAR_852GME: + *name = "Intel(R) 852GME"; + *chipset = INTEL_852GME; + return 0; + case INTEL_VAR_852GM: + *name = "Intel(R) 852GM"; + *chipset = INTEL_852GM; + return 0; + default: + *name = "Intel(R) 852GM/855GM"; + *chipset = INTEL_85XGM; + return 0; + } + break; + case PCI_DEVICE_ID_INTEL_865G: + *name = "Intel(R) 865G"; + *chipset = INTEL_865G; + *mobile = 0; + return 0; + case PCI_DEVICE_ID_INTEL_915G: + *name = "Intel(R) 915G"; + *chipset = INTEL_915G; + *mobile = 0; + return 0; + default: + return 1; + } +} + +int +intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, + int *stolen_size) +{ + struct pci_dev *bridge_dev; + u16 tmp; + + if (!pdev || !aperture_size || !stolen_size) + return 1; + + /* Find the bridge device. It is always 0:0.0 */ + if (!(bridge_dev = pci_find_slot(0, PCI_DEVFN(0, 0)))) { + ERR_MSG("cannot find bridge device\n"); + return 1; + } + + /* Get the fb aperture size and "stolen" memory amount. */ + tmp = 0; + pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp); + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_830M: + case PCI_DEVICE_ID_INTEL_845G: + if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M) + *aperture_size = MB(64); + else + *aperture_size = MB(128); + switch (tmp & INTEL_830_GMCH_GMS_MASK) { + case INTEL_830_GMCH_GMS_STOLEN_512: + *stolen_size = KB(512) - KB(132); + return 0; + case INTEL_830_GMCH_GMS_STOLEN_1024: + *stolen_size = MB(1) - KB(132); + return 0; + case INTEL_830_GMCH_GMS_STOLEN_8192: + *stolen_size = MB(8) - KB(132); + return 0; + case INTEL_830_GMCH_GMS_LOCAL: + ERR_MSG("only local memory found\n"); + return 1; + case INTEL_830_GMCH_GMS_DISABLED: + ERR_MSG("video memory is disabled\n"); + return 1; + default: + ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n", + tmp & INTEL_830_GMCH_GMS_MASK); + return 1; + } + break; + default: + *aperture_size = MB(128); + switch (tmp & INTEL_855_GMCH_GMS_MASK) { + case INTEL_855_GMCH_GMS_STOLEN_1M: + *stolen_size = MB(1) - KB(132); + return 0; + case INTEL_855_GMCH_GMS_STOLEN_4M: + *stolen_size = MB(4) - KB(132); + return 0; + case INTEL_855_GMCH_GMS_STOLEN_8M: + *stolen_size = MB(8) - KB(132); + return 0; + case INTEL_855_GMCH_GMS_STOLEN_16M: + *stolen_size = MB(16) - KB(132); + return 0; + case INTEL_855_GMCH_GMS_STOLEN_32M: + *stolen_size = MB(32) - KB(132); + return 0; + case INTEL_915G_GMCH_GMS_STOLEN_48M: + *stolen_size = MB(48) - KB(132); + return 0; + case INTEL_915G_GMCH_GMS_STOLEN_64M: + *stolen_size = MB(64) - KB(132); + return 0; + case INTEL_855_GMCH_GMS_DISABLED: + ERR_MSG("video memory is disabled\n"); + return 0; + default: + ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n", + tmp & INTEL_855_GMCH_GMS_MASK); + return 1; + } + } +} + +int +intelfbhw_check_non_crt(struct intelfb_info *dinfo) +{ + int dvo = 0; + + if (INREG(LVDS) & PORT_ENABLE) + dvo |= LVDS_PORT; + if (INREG(DVOA) & PORT_ENABLE) + dvo |= DVOA_PORT; + if (INREG(DVOB) & PORT_ENABLE) + dvo |= DVOB_PORT; + if (INREG(DVOC) & PORT_ENABLE) + dvo |= DVOC_PORT; + + return dvo; +} + +const char * +intelfbhw_dvo_to_string(int dvo) +{ + if (dvo & DVOA_PORT) + return "DVO port A"; + else if (dvo & DVOB_PORT) + return "DVO port B"; + else if (dvo & DVOC_PORT) + return "DVO port C"; + else if (dvo & LVDS_PORT) + return "LVDS port"; + else + return NULL; +} + + +int +intelfbhw_validate_mode(struct intelfb_info *dinfo, + struct fb_var_screeninfo *var) +{ + int bytes_per_pixel; + int tmp; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_validate_mode\n"); +#endif + + bytes_per_pixel = var->bits_per_pixel / 8; + if (bytes_per_pixel == 3) + bytes_per_pixel = 4; + + /* Check if enough video memory. */ + tmp = var->yres_virtual * var->xres_virtual * bytes_per_pixel; + if (tmp > dinfo->fb.size) { + WRN_MSG("Not enough video ram for mode " + "(%d KByte vs %d KByte).\n", + BtoKB(tmp), BtoKB(dinfo->fb.size)); + return 1; + } + + /* Check if x/y limits are OK. */ + if (var->xres - 1 > HACTIVE_MASK) { + WRN_MSG("X resolution too large (%d vs %d).\n", + var->xres, HACTIVE_MASK + 1); + return 1; + } + if (var->yres - 1 > VACTIVE_MASK) { + WRN_MSG("Y resolution too large (%d vs %d).\n", + var->yres, VACTIVE_MASK + 1); + return 1; + } + + /* Check for interlaced/doublescan modes. */ + if (var->vmode & FB_VMODE_INTERLACED) { + WRN_MSG("Mode is interlaced.\n"); + return 1; + } + if (var->vmode & FB_VMODE_DOUBLE) { + WRN_MSG("Mode is double-scan.\n"); + return 1; + } + + /* Check if clock is OK. */ + tmp = 1000000000 / var->pixclock; + if (tmp < MIN_CLOCK) { + WRN_MSG("Pixel clock is too low (%d MHz vs %d MHz).\n", + (tmp + 500) / 1000, MIN_CLOCK / 1000); + return 1; + } + if (tmp > MAX_CLOCK) { + WRN_MSG("Pixel clock is too high (%d MHz vs %d MHz).\n", + (tmp + 500) / 1000, MAX_CLOCK / 1000); + return 1; + } + + return 0; +} + +int +intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + u32 offset, xoffset, yoffset; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_pan_display\n"); +#endif + + xoffset = ROUND_DOWN_TO(var->xoffset, 8); + yoffset = var->yoffset; + + if ((xoffset + var->xres > var->xres_virtual) || + (yoffset + var->yres > var->yres_virtual)) + return -EINVAL; + + offset = (yoffset * dinfo->pitch) + + (xoffset * var->bits_per_pixel) / 8; + + offset += dinfo->fb.offset << 12; + + OUTREG(DSPABASE, offset); + + return 0; +} + +/* Blank the screen. */ +void +intelfbhw_do_blank(int blank, struct fb_info *info) +{ + struct intelfb_info *dinfo = GET_DINFO(info); + u32 tmp; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_do_blank: blank is %d\n", blank); +#endif + + /* Turn plane A on or off */ + tmp = INREG(DSPACNTR); + if (blank) + tmp &= ~DISPPLANE_PLANE_ENABLE; + else + tmp |= DISPPLANE_PLANE_ENABLE; + OUTREG(DSPACNTR, tmp); + /* Flush */ + tmp = INREG(DSPABASE); + OUTREG(DSPABASE, tmp); + + /* Turn off/on the HW cursor */ +#if VERBOSE > 0 + DBG_MSG("cursor_on is %d\n", dinfo->cursor_on); +#endif + if (dinfo->cursor_on) { + if (blank) { + intelfbhw_cursor_hide(dinfo); + } else { + intelfbhw_cursor_show(dinfo); + } + dinfo->cursor_on = 1; + } + dinfo->cursor_blanked = blank; + + /* Set DPMS level */ + tmp = INREG(ADPA) & ~ADPA_DPMS_CONTROL_MASK; + switch (blank) { + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: + tmp |= ADPA_DPMS_D0; + break; + case FB_BLANK_VSYNC_SUSPEND: + tmp |= ADPA_DPMS_D1; + break; + case FB_BLANK_HSYNC_SUSPEND: + tmp |= ADPA_DPMS_D2; + break; + case FB_BLANK_POWERDOWN: + tmp |= ADPA_DPMS_D3; + break; + } + OUTREG(ADPA, tmp); + + return; +} + + +void +intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp) +{ +#if VERBOSE > 0 + DBG_MSG("intelfbhw_setcolreg: %d: (%d, %d, %d)\n", + regno, red, green, blue); +#endif + + u32 palette_reg = (dinfo->pipe == PIPE_A) ? + PALETTE_A : PALETTE_B; + + OUTREG(palette_reg + (regno << 2), + (red << PALETTE_8_RED_SHIFT) | + (green << PALETTE_8_GREEN_SHIFT) | + (blue << PALETTE_8_BLUE_SHIFT)); +} + + +int +intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, + int flag) +{ + int i; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_read_hw_state\n"); +#endif + + if (!hw || !dinfo) + return -1; + + /* Read in as much of the HW state as possible. */ + hw->vga0_divisor = INREG(VGA0_DIVISOR); + hw->vga1_divisor = INREG(VGA1_DIVISOR); + hw->vga_pd = INREG(VGAPD); + hw->dpll_a = INREG(DPLL_A); + hw->dpll_b = INREG(DPLL_B); + hw->fpa0 = INREG(FPA0); + hw->fpa1 = INREG(FPA1); + hw->fpb0 = INREG(FPB0); + hw->fpb1 = INREG(FPB1); + + if (flag == 1) + return flag; + +#if 0 + /* This seems to be a problem with the 852GM/855GM */ + for (i = 0; i < PALETTE_8_ENTRIES; i++) { + hw->palette_a[i] = INREG(PALETTE_A + (i << 2)); + hw->palette_b[i] = INREG(PALETTE_B + (i << 2)); + } +#endif + + if (flag == 2) + return flag; + + hw->htotal_a = INREG(HTOTAL_A); + hw->hblank_a = INREG(HBLANK_A); + hw->hsync_a = INREG(HSYNC_A); + hw->vtotal_a = INREG(VTOTAL_A); + hw->vblank_a = INREG(VBLANK_A); + hw->vsync_a = INREG(VSYNC_A); + hw->src_size_a = INREG(SRC_SIZE_A); + hw->bclrpat_a = INREG(BCLRPAT_A); + hw->htotal_b = INREG(HTOTAL_B); + hw->hblank_b = INREG(HBLANK_B); + hw->hsync_b = INREG(HSYNC_B); + hw->vtotal_b = INREG(VTOTAL_B); + hw->vblank_b = INREG(VBLANK_B); + hw->vsync_b = INREG(VSYNC_B); + hw->src_size_b = INREG(SRC_SIZE_B); + hw->bclrpat_b = INREG(BCLRPAT_B); + + if (flag == 3) + return flag; + + hw->adpa = INREG(ADPA); + hw->dvoa = INREG(DVOA); + hw->dvob = INREG(DVOB); + hw->dvoc = INREG(DVOC); + hw->dvoa_srcdim = INREG(DVOA_SRCDIM); + hw->dvob_srcdim = INREG(DVOB_SRCDIM); + hw->dvoc_srcdim = INREG(DVOC_SRCDIM); + hw->lvds = INREG(LVDS); + + if (flag == 4) + return flag; + + hw->pipe_a_conf = INREG(PIPEACONF); + hw->pipe_b_conf = INREG(PIPEBCONF); + hw->disp_arb = INREG(DISPARB); + + if (flag == 5) + return flag; + + hw->cursor_a_control = INREG(CURSOR_A_CONTROL); + hw->cursor_b_control = INREG(CURSOR_B_CONTROL); + hw->cursor_a_base = INREG(CURSOR_A_BASEADDR); + hw->cursor_b_base = INREG(CURSOR_B_BASEADDR); + + if (flag == 6) + return flag; + + for (i = 0; i < 4; i++) { + hw->cursor_a_palette[i] = INREG(CURSOR_A_PALETTE0 + (i << 2)); + hw->cursor_b_palette[i] = INREG(CURSOR_B_PALETTE0 + (i << 2)); + } + + if (flag == 7) + return flag; + + hw->cursor_size = INREG(CURSOR_SIZE); + + if (flag == 8) + return flag; + + hw->disp_a_ctrl = INREG(DSPACNTR); + hw->disp_b_ctrl = INREG(DSPBCNTR); + hw->disp_a_base = INREG(DSPABASE); + hw->disp_b_base = INREG(DSPBBASE); + hw->disp_a_stride = INREG(DSPASTRIDE); + hw->disp_b_stride = INREG(DSPBSTRIDE); + + if (flag == 9) + return flag; + + hw->vgacntrl = INREG(VGACNTRL); + + if (flag == 10) + return flag; + + hw->add_id = INREG(ADD_ID); + + if (flag == 11) + return flag; + + for (i = 0; i < 7; i++) { + hw->swf0x[i] = INREG(SWF00 + (i << 2)); + hw->swf1x[i] = INREG(SWF10 + (i << 2)); + if (i < 3) + hw->swf3x[i] = INREG(SWF30 + (i << 2)); + } + + for (i = 0; i < 8; i++) + hw->fence[i] = INREG(FENCE + (i << 2)); + + hw->instpm = INREG(INSTPM); + hw->mem_mode = INREG(MEM_MODE); + hw->fw_blc_0 = INREG(FW_BLC_0); + hw->fw_blc_1 = INREG(FW_BLC_1); + + return 0; +} + + +void +intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) +{ +#if REGDUMP + int i, m1, m2, n, p1, p2; + + DBG_MSG("intelfbhw_print_hw_state\n"); + + if (!hw || !dinfo) + return; + /* Read in as much of the HW state as possible. */ + printk("hw state dump start\n"); + printk(" VGA0_DIVISOR: 0x%08x\n", hw->vga0_divisor); + printk(" VGA1_DIVISOR: 0x%08x\n", hw->vga1_divisor); + printk(" VGAPD: 0x%08x\n", hw->vga_pd); + n = (hw->vga0_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m1 = (hw->vga0_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m2 = (hw->vga0_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + if (hw->vga_pd & VGAPD_0_P1_FORCE_DIV2) + p1 = 0; + else + p1 = (hw->vga_pd >> VGAPD_0_P1_SHIFT) & DPLL_P1_MASK; + p2 = (hw->vga_pd >> VGAPD_0_P2_SHIFT) & DPLL_P2_MASK; + printk(" VGA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", + m1, m2, n, p1, p2); + printk(" VGA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); + + n = (hw->vga1_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m1 = (hw->vga1_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m2 = (hw->vga1_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + if (hw->vga_pd & VGAPD_1_P1_FORCE_DIV2) + p1 = 0; + else + p1 = (hw->vga_pd >> VGAPD_1_P1_SHIFT) & DPLL_P1_MASK; + p2 = (hw->vga_pd >> VGAPD_1_P2_SHIFT) & DPLL_P2_MASK; + printk(" VGA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", + m1, m2, n, p1, p2); + printk(" VGA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); + + printk(" DPLL_A: 0x%08x\n", hw->dpll_a); + printk(" DPLL_B: 0x%08x\n", hw->dpll_b); + printk(" FPA0: 0x%08x\n", hw->fpa0); + printk(" FPA1: 0x%08x\n", hw->fpa1); + printk(" FPB0: 0x%08x\n", hw->fpb0); + printk(" FPB1: 0x%08x\n", hw->fpb1); + + n = (hw->fpa0 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m1 = (hw->fpa0 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m2 = (hw->fpa0 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + if (hw->dpll_a & DPLL_P1_FORCE_DIV2) + p1 = 0; + else + p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK; + p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK; + printk(" PLLA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", + m1, m2, n, p1, p2); + printk(" PLLA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); + + n = (hw->fpa1 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m1 = (hw->fpa1 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + m2 = (hw->fpa1 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; + if (hw->dpll_a & DPLL_P1_FORCE_DIV2) + p1 = 0; + else + p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK; + p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK; + printk(" PLLA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", + m1, m2, n, p1, p2); + printk(" PLLA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); + +#if 0 + printk(" PALETTE_A:\n"); + for (i = 0; i < PALETTE_8_ENTRIES) + printk(" %3d: 0x%08x\n", i, hw->palette_a[i]; + printk(" PALETTE_B:\n"); + for (i = 0; i < PALETTE_8_ENTRIES) + printk(" %3d: 0x%08x\n", i, hw->palette_b[i]; +#endif + + printk(" HTOTAL_A: 0x%08x\n", hw->htotal_a); + printk(" HBLANK_A: 0x%08x\n", hw->hblank_a); + printk(" HSYNC_A: 0x%08x\n", hw->hsync_a); + printk(" VTOTAL_A: 0x%08x\n", hw->vtotal_a); + printk(" VBLANK_A: 0x%08x\n", hw->vblank_a); + printk(" VSYNC_A: 0x%08x\n", hw->vsync_a); + printk(" SRC_SIZE_A: 0x%08x\n", hw->src_size_a); + printk(" BCLRPAT_A: 0x%08x\n", hw->bclrpat_a); + printk(" HTOTAL_B: 0x%08x\n", hw->htotal_b); + printk(" HBLANK_B: 0x%08x\n", hw->hblank_b); + printk(" HSYNC_B: 0x%08x\n", hw->hsync_b); + printk(" VTOTAL_B: 0x%08x\n", hw->vtotal_b); + printk(" VBLANK_B: 0x%08x\n", hw->vblank_b); + printk(" VSYNC_B: 0x%08x\n", hw->vsync_b); + printk(" SRC_SIZE_B: 0x%08x\n", hw->src_size_b); + printk(" BCLRPAT_B: 0x%08x\n", hw->bclrpat_b); + + printk(" ADPA: 0x%08x\n", hw->adpa); + printk(" DVOA: 0x%08x\n", hw->dvoa); + printk(" DVOB: 0x%08x\n", hw->dvob); + printk(" DVOC: 0x%08x\n", hw->dvoc); + printk(" DVOA_SRCDIM: 0x%08x\n", hw->dvoa_srcdim); + printk(" DVOB_SRCDIM: 0x%08x\n", hw->dvob_srcdim); + printk(" DVOC_SRCDIM: 0x%08x\n", hw->dvoc_srcdim); + printk(" LVDS: 0x%08x\n", hw->lvds); + + printk(" PIPEACONF: 0x%08x\n", hw->pipe_a_conf); + printk(" PIPEBCONF: 0x%08x\n", hw->pipe_b_conf); + printk(" DISPARB: 0x%08x\n", hw->disp_arb); + + printk(" CURSOR_A_CONTROL: 0x%08x\n", hw->cursor_a_control); + printk(" CURSOR_B_CONTROL: 0x%08x\n", hw->cursor_b_control); + printk(" CURSOR_A_BASEADDR: 0x%08x\n", hw->cursor_a_base); + printk(" CURSOR_B_BASEADDR: 0x%08x\n", hw->cursor_b_base); + + printk(" CURSOR_A_PALETTE: "); + for (i = 0; i < 4; i++) { + printk("0x%08x", hw->cursor_a_palette[i]); + if (i < 3) + printk(", "); + } + printk("\n"); + printk(" CURSOR_B_PALETTE: "); + for (i = 0; i < 4; i++) { + printk("0x%08x", hw->cursor_b_palette[i]); + if (i < 3) + printk(", "); + } + printk("\n"); + + printk(" CURSOR_SIZE: 0x%08x\n", hw->cursor_size); + + printk(" DSPACNTR: 0x%08x\n", hw->disp_a_ctrl); + printk(" DSPBCNTR: 0x%08x\n", hw->disp_b_ctrl); + printk(" DSPABASE: 0x%08x\n", hw->disp_a_base); + printk(" DSPBBASE: 0x%08x\n", hw->disp_b_base); + printk(" DSPASTRIDE: 0x%08x\n", hw->disp_a_stride); + printk(" DSPBSTRIDE: 0x%08x\n", hw->disp_b_stride); + + printk(" VGACNTRL: 0x%08x\n", hw->vgacntrl); + printk(" ADD_ID: 0x%08x\n", hw->add_id); + + for (i = 0; i < 7; i++) { + printk(" SWF0%d 0x%08x\n", i, + hw->swf0x[i]); + } + for (i = 0; i < 7; i++) { + printk(" SWF1%d 0x%08x\n", i, + hw->swf1x[i]); + } + for (i = 0; i < 3; i++) { + printk(" SWF3%d 0x%08x\n", i, + hw->swf3x[i]); + } + for (i = 0; i < 8; i++) + printk(" FENCE%d 0x%08x\n", i, + hw->fence[i]); + + printk(" INSTPM 0x%08x\n", hw->instpm); + printk(" MEM_MODE 0x%08x\n", hw->mem_mode); + printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0); + printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1); + + printk("hw state dump end\n"); +#endif +} + +/* Split the M parameter into M1 and M2. */ +static int +splitm(unsigned int m, unsigned int *retm1, unsigned int *retm2) +{ + int m1, m2; + + m1 = (m - 2 - (MIN_M2 + MAX_M2) / 2) / 5 - 2; + if (m1 < MIN_M1) + m1 = MIN_M1; + if (m1 > MAX_M1) + m1 = MAX_M1; + m2 = m - 5 * (m1 + 2) - 2; + if (m2 < MIN_M2 || m2 > MAX_M2 || m2 >= m1) { + return 1; + } else { + *retm1 = (unsigned int)m1; + *retm2 = (unsigned int)m2; + return 0; + } +} + +/* Split the P parameter into P1 and P2. */ +static int +splitp(unsigned int p, unsigned int *retp1, unsigned int *retp2) +{ + int p1, p2; + + if (p % 4 == 0) + p2 = 1; + else + p2 = 0; + p1 = (p / (1 << (p2 + 1))) - 2; + if (p % 4 == 0 && p1 < MIN_P1) { + p2 = 0; + p1 = (p / (1 << (p2 + 1))) - 2; + } + if (p1 < MIN_P1 || p1 > MAX_P1 || (p1 + 2) * (1 << (p2 + 1)) != p) { + return 1; + } else { + *retp1 = (unsigned int)p1; + *retp2 = (unsigned int)p2; + return 0; + } +} + +static int +calc_pll_params(int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *retp1, + u32 *retp2, u32 *retclock) +{ + u32 m1, m2, n, p1, p2, n1; + u32 f_vco, p, p_best = 0, m, f_out; + u32 err_max, err_target, err_best = 10000000; + u32 n_best = 0, m_best = 0, f_best, f_err; + u32 p_min, p_max, p_inc, div_min, div_max; + + /* Accept 0.5% difference, but aim for 0.1% */ + err_max = 5 * clock / 1000; + err_target = clock / 1000; + + DBG_MSG("Clock is %d\n", clock); + + div_max = MAX_VCO_FREQ / clock; + div_min = ROUND_UP_TO(MIN_VCO_FREQ, clock) / clock; + + if (clock <= P_TRANSITION_CLOCK) + p_inc = 4; + else + p_inc = 2; + p_min = ROUND_UP_TO(div_min, p_inc); + p_max = ROUND_DOWN_TO(div_max, p_inc); + if (p_min < MIN_P) + p_min = 4; + if (p_max > MAX_P) + p_max = 128; + + DBG_MSG("p range is %d-%d (%d)\n", p_min, p_max, p_inc); + + p = p_min; + do { + if (splitp(p, &p1, &p2)) { + WRN_MSG("cannot split p = %d\n", p); + p += p_inc; + continue; + } + n = MIN_N; + f_vco = clock * p; + + do { + m = ROUND_UP_TO(f_vco * n, PLL_REFCLK) / PLL_REFCLK; + if (m < MIN_M) + m = MIN_M; + if (m > MAX_M) + m = MAX_M; + f_out = CALC_VCLOCK3(m, n, p); + if (splitm(m, &m1, &m2)) { + WRN_MSG("cannot split m = %d\n", m); + n++; + continue; + } + if (clock > f_out) + f_err = clock - f_out; + else + f_err = f_out - clock; + + if (f_err < err_best) { + m_best = m; + n_best = n; + p_best = p; + f_best = f_out; + err_best = f_err; + } + n++; + } while ((n <= MAX_N) && (f_out >= clock)); + p += p_inc; + } while ((p <= p_max)); + + if (!m_best) { + WRN_MSG("cannot find parameters for clock %d\n", clock); + return 1; + } + m = m_best; + n = n_best; + p = p_best; + splitm(m, &m1, &m2); + splitp(p, &p1, &p2); + n1 = n - 2; + + DBG_MSG("m, n, p: %d (%d,%d), %d (%d), %d (%d,%d), " + "f: %d (%d), VCO: %d\n", + m, m1, m2, n, n1, p, p1, p2, + CALC_VCLOCK3(m, n, p), CALC_VCLOCK(m1, m2, n1, p1, p2), + CALC_VCLOCK3(m, n, p) * p); + *retm1 = m1; + *retm2 = m2; + *retn = n1; + *retp1 = p1; + *retp2 = p2; + *retclock = CALC_VCLOCK(m1, m2, n1, p1, p2); + + return 0; +} + +static __inline__ int +check_overflow(u32 value, u32 limit, const char *description) +{ + if (value > limit) { + WRN_MSG("%s value %d exceeds limit %d\n", + description, value, limit); + return 1; + } + return 0; +} + +/* It is assumed that hw is filled in with the initial state information. */ +int +intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, + struct fb_var_screeninfo *var) +{ + int pipe = PIPE_A; + u32 *dpll, *fp0, *fp1; + u32 m1, m2, n, p1, p2, clock_target, clock; + u32 hsync_start, hsync_end, hblank_start, hblank_end, htotal, hactive; + u32 vsync_start, vsync_end, vblank_start, vblank_end, vtotal, vactive; + u32 vsync_pol, hsync_pol; + u32 *vs, *vb, *vt, *hs, *hb, *ht, *ss, *pipe_conf; + + DBG_MSG("intelfbhw_mode_to_hw\n"); + + /* Disable VGA */ + hw->vgacntrl |= VGA_DISABLE; + + /* Check whether pipe A or pipe B is enabled. */ + if (hw->pipe_a_conf & PIPECONF_ENABLE) + pipe = PIPE_A; + else if (hw->pipe_b_conf & PIPECONF_ENABLE) + pipe = PIPE_B; + + /* Set which pipe's registers will be set. */ + if (pipe == PIPE_B) { + dpll = &hw->dpll_b; + fp0 = &hw->fpb0; + fp1 = &hw->fpb1; + hs = &hw->hsync_b; + hb = &hw->hblank_b; + ht = &hw->htotal_b; + vs = &hw->vsync_b; + vb = &hw->vblank_b; + vt = &hw->vtotal_b; + ss = &hw->src_size_b; + pipe_conf = &hw->pipe_b_conf; + } else { + dpll = &hw->dpll_a; + fp0 = &hw->fpa0; + fp1 = &hw->fpa1; + hs = &hw->hsync_a; + hb = &hw->hblank_a; + ht = &hw->htotal_a; + vs = &hw->vsync_a; + vb = &hw->vblank_a; + vt = &hw->vtotal_a; + ss = &hw->src_size_a; + pipe_conf = &hw->pipe_a_conf; + } + + /* Use ADPA register for sync control. */ + hw->adpa &= ~ADPA_USE_VGA_HVPOLARITY; + + /* sync polarity */ + hsync_pol = (var->sync & FB_SYNC_HOR_HIGH_ACT) ? + ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW; + vsync_pol = (var->sync & FB_SYNC_VERT_HIGH_ACT) ? + ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW; + hw->adpa &= ~((ADPA_SYNC_ACTIVE_MASK << ADPA_VSYNC_ACTIVE_SHIFT) | + (ADPA_SYNC_ACTIVE_MASK << ADPA_HSYNC_ACTIVE_SHIFT)); + hw->adpa |= (hsync_pol << ADPA_HSYNC_ACTIVE_SHIFT) | + (vsync_pol << ADPA_VSYNC_ACTIVE_SHIFT); + + /* Connect correct pipe to the analog port DAC */ + hw->adpa &= ~(PIPE_MASK << ADPA_PIPE_SELECT_SHIFT); + hw->adpa |= (pipe << ADPA_PIPE_SELECT_SHIFT); + + /* Set DPMS state to D0 (on) */ + hw->adpa &= ~ADPA_DPMS_CONTROL_MASK; + hw->adpa |= ADPA_DPMS_D0; + + hw->adpa |= ADPA_DAC_ENABLE; + + *dpll |= (DPLL_VCO_ENABLE | DPLL_VGA_MODE_DISABLE); + *dpll &= ~(DPLL_RATE_SELECT_MASK | DPLL_REFERENCE_SELECT_MASK); + *dpll |= (DPLL_REFERENCE_DEFAULT | DPLL_RATE_SELECT_FP0); + + /* Desired clock in kHz */ + clock_target = 1000000000 / var->pixclock; + + if (calc_pll_params(clock_target, &m1, &m2, &n, &p1, &p2, &clock)) { + WRN_MSG("calc_pll_params failed\n"); + return 1; + } + + /* Check for overflow. */ + if (check_overflow(p1, DPLL_P1_MASK, "PLL P1 parameter")) + return 1; + if (check_overflow(p2, DPLL_P2_MASK, "PLL P2 parameter")) + return 1; + if (check_overflow(m1, FP_DIVISOR_MASK, "PLL M1 parameter")) + return 1; + if (check_overflow(m2, FP_DIVISOR_MASK, "PLL M2 parameter")) + return 1; + if (check_overflow(n, FP_DIVISOR_MASK, "PLL N parameter")) + return 1; + + *dpll &= ~DPLL_P1_FORCE_DIV2; + *dpll &= ~((DPLL_P2_MASK << DPLL_P2_SHIFT) | + (DPLL_P1_MASK << DPLL_P1_SHIFT)); + *dpll |= (p2 << DPLL_P2_SHIFT) | (p1 << DPLL_P1_SHIFT); + *fp0 = (n << FP_N_DIVISOR_SHIFT) | + (m1 << FP_M1_DIVISOR_SHIFT) | + (m2 << FP_M2_DIVISOR_SHIFT); + *fp1 = *fp0; + + hw->dvob &= ~PORT_ENABLE; + hw->dvoc &= ~PORT_ENABLE; + + /* Use display plane A. */ + hw->disp_a_ctrl |= DISPPLANE_PLANE_ENABLE; + hw->disp_a_ctrl &= ~DISPPLANE_GAMMA_ENABLE; + hw->disp_a_ctrl &= ~DISPPLANE_PIXFORMAT_MASK; + switch (intelfb_var_to_depth(var)) { + case 8: + hw->disp_a_ctrl |= DISPPLANE_8BPP | DISPPLANE_GAMMA_ENABLE; + break; + case 15: + hw->disp_a_ctrl |= DISPPLANE_15_16BPP; + break; + case 16: + hw->disp_a_ctrl |= DISPPLANE_16BPP; + break; + case 24: + hw->disp_a_ctrl |= DISPPLANE_32BPP_NO_ALPHA; + break; + } + hw->disp_a_ctrl &= ~(PIPE_MASK << DISPPLANE_SEL_PIPE_SHIFT); + hw->disp_a_ctrl |= (pipe << DISPPLANE_SEL_PIPE_SHIFT); + + /* Set CRTC registers. */ + hactive = var->xres; + hsync_start = hactive + var->right_margin; + hsync_end = hsync_start + var->hsync_len; + htotal = hsync_end + var->left_margin; + hblank_start = hactive; + hblank_end = htotal; + + DBG_MSG("H: act %d, ss %d, se %d, tot %d bs %d, be %d\n", + hactive, hsync_start, hsync_end, htotal, hblank_start, + hblank_end); + + vactive = var->yres; + vsync_start = vactive + var->lower_margin; + vsync_end = vsync_start + var->vsync_len; + vtotal = vsync_end + var->upper_margin; + vblank_start = vactive; + vblank_end = vtotal; + vblank_end = vsync_end + 1; + + DBG_MSG("V: act %d, ss %d, se %d, tot %d bs %d, be %d\n", + vactive, vsync_start, vsync_end, vtotal, vblank_start, + vblank_end); + + /* Adjust for register values, and check for overflow. */ + hactive--; + if (check_overflow(hactive, HACTIVE_MASK, "CRTC hactive")) + return 1; + hsync_start--; + if (check_overflow(hsync_start, HSYNCSTART_MASK, "CRTC hsync_start")) + return 1; + hsync_end--; + if (check_overflow(hsync_end, HSYNCEND_MASK, "CRTC hsync_end")) + return 1; + htotal--; + if (check_overflow(htotal, HTOTAL_MASK, "CRTC htotal")) + return 1; + hblank_start--; + if (check_overflow(hblank_start, HBLANKSTART_MASK, "CRTC hblank_start")) + return 1; + hblank_end--; + if (check_overflow(hblank_end, HBLANKEND_MASK, "CRTC hblank_end")) + return 1; + + vactive--; + if (check_overflow(vactive, VACTIVE_MASK, "CRTC vactive")) + return 1; + vsync_start--; + if (check_overflow(vsync_start, VSYNCSTART_MASK, "CRTC vsync_start")) + return 1; + vsync_end--; + if (check_overflow(vsync_end, VSYNCEND_MASK, "CRTC vsync_end")) + return 1; + vtotal--; + if (check_overflow(vtotal, VTOTAL_MASK, "CRTC vtotal")) + return 1; + vblank_start--; + if (check_overflow(vblank_start, VBLANKSTART_MASK, "CRTC vblank_start")) + return 1; + vblank_end--; + if (check_overflow(vblank_end, VBLANKEND_MASK, "CRTC vblank_end")) + return 1; + + *ht = (htotal << HTOTAL_SHIFT) | (hactive << HACTIVE_SHIFT); + *hb = (hblank_start << HBLANKSTART_SHIFT) | + (hblank_end << HSYNCEND_SHIFT); + *hs = (hsync_start << HSYNCSTART_SHIFT) | (hsync_end << HSYNCEND_SHIFT); + + *vt = (vtotal << VTOTAL_SHIFT) | (vactive << VACTIVE_SHIFT); + *vb = (vblank_start << VBLANKSTART_SHIFT) | + (vblank_end << VSYNCEND_SHIFT); + *vs = (vsync_start << VSYNCSTART_SHIFT) | (vsync_end << VSYNCEND_SHIFT); + *ss = (hactive << SRC_SIZE_HORIZ_SHIFT) | + (vactive << SRC_SIZE_VERT_SHIFT); + + hw->disp_a_stride = var->xres_virtual * var->bits_per_pixel / 8; + DBG_MSG("pitch is %d\n", hw->disp_a_stride); + + hw->disp_a_base = hw->disp_a_stride * var->yoffset + + var->xoffset * var->bits_per_pixel / 8; + + hw->disp_a_base += dinfo->fb.offset << 12; + + /* Check stride alignment. */ + if (hw->disp_a_stride % STRIDE_ALIGNMENT != 0) { + WRN_MSG("display stride %d has bad alignment %d\n", + hw->disp_a_stride, STRIDE_ALIGNMENT); + return 1; + } + + /* Set the palette to 8-bit mode. */ + *pipe_conf &= ~PIPECONF_GAMMA; + return 0; +} + +/* Program a (non-VGA) video mode. */ +int +intelfbhw_program_mode(struct intelfb_info *dinfo, + const struct intelfb_hwstate *hw, int blank) +{ + int pipe = PIPE_A; + u32 tmp; + const u32 *dpll, *fp0, *fp1, *pipe_conf; + const u32 *hs, *ht, *hb, *vs, *vt, *vb, *ss; + u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg; + u32 hsync_reg, htotal_reg, hblank_reg; + u32 vsync_reg, vtotal_reg, vblank_reg; + u32 src_size_reg; + + /* Assume single pipe, display plane A, analog CRT. */ + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_program_mode\n"); +#endif + + /* Disable VGA */ + tmp = INREG(VGACNTRL); + tmp |= VGA_DISABLE; + OUTREG(VGACNTRL, tmp); + + /* Check whether pipe A or pipe B is enabled. */ + if (hw->pipe_a_conf & PIPECONF_ENABLE) + pipe = PIPE_A; + else if (hw->pipe_b_conf & PIPECONF_ENABLE) + pipe = PIPE_B; + + dinfo->pipe = pipe; + + if (pipe == PIPE_B) { + dpll = &hw->dpll_b; + fp0 = &hw->fpb0; + fp1 = &hw->fpb1; + pipe_conf = &hw->pipe_b_conf; + hs = &hw->hsync_b; + hb = &hw->hblank_b; + ht = &hw->htotal_b; + vs = &hw->vsync_b; + vb = &hw->vblank_b; + vt = &hw->vtotal_b; + ss = &hw->src_size_b; + dpll_reg = DPLL_B; + fp0_reg = FPB0; + fp1_reg = FPB1; + pipe_conf_reg = PIPEBCONF; + hsync_reg = HSYNC_B; + htotal_reg = HTOTAL_B; + hblank_reg = HBLANK_B; + vsync_reg = VSYNC_B; + vtotal_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + src_size_reg = SRC_SIZE_B; + } else { + dpll = &hw->dpll_a; + fp0 = &hw->fpa0; + fp1 = &hw->fpa1; + pipe_conf = &hw->pipe_a_conf; + hs = &hw->hsync_a; + hb = &hw->hblank_a; + ht = &hw->htotal_a; + vs = &hw->vsync_a; + vb = &hw->vblank_a; + vt = &hw->vtotal_a; + ss = &hw->src_size_a; + dpll_reg = DPLL_A; + fp0_reg = FPA0; + fp1_reg = FPA1; + pipe_conf_reg = PIPEACONF; + hsync_reg = HSYNC_A; + htotal_reg = HTOTAL_A; + hblank_reg = HBLANK_A; + vsync_reg = VSYNC_A; + vtotal_reg = VTOTAL_A; + vblank_reg = VBLANK_A; + src_size_reg = SRC_SIZE_A; + } + + /* Disable planes A and B. */ + tmp = INREG(DSPACNTR); + tmp &= ~DISPPLANE_PLANE_ENABLE; + OUTREG(DSPACNTR, tmp); + tmp = INREG(DSPBCNTR); + tmp &= ~DISPPLANE_PLANE_ENABLE; + OUTREG(DSPBCNTR, tmp); + + /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ + mdelay(20); + + /* Disable Sync */ + tmp = INREG(ADPA); + tmp &= ~ADPA_DPMS_CONTROL_MASK; + tmp |= ADPA_DPMS_D3; + OUTREG(ADPA, tmp); + + /* turn off pipe */ + tmp = INREG(pipe_conf_reg); + tmp &= ~PIPECONF_ENABLE; + OUTREG(pipe_conf_reg, tmp); + + /* turn off PLL */ + tmp = INREG(dpll_reg); + dpll_reg &= ~DPLL_VCO_ENABLE; + OUTREG(dpll_reg, tmp); + + /* Set PLL parameters */ + OUTREG(dpll_reg, *dpll & ~DPLL_VCO_ENABLE); + OUTREG(fp0_reg, *fp0); + OUTREG(fp1_reg, *fp1); + + /* Set pipe parameters */ + OUTREG(hsync_reg, *hs); + OUTREG(hblank_reg, *hb); + OUTREG(htotal_reg, *ht); + OUTREG(vsync_reg, *vs); + OUTREG(vblank_reg, *vb); + OUTREG(vtotal_reg, *vt); + OUTREG(src_size_reg, *ss); + + /* Set DVOs B/C */ + OUTREG(DVOB, hw->dvob); + OUTREG(DVOC, hw->dvoc); + + /* Set ADPA */ + OUTREG(ADPA, (hw->adpa & ~(ADPA_DPMS_CONTROL_MASK)) | ADPA_DPMS_D3); + + /* Enable PLL */ + tmp = INREG(dpll_reg); + tmp |= DPLL_VCO_ENABLE; + OUTREG(dpll_reg, tmp); + + /* Enable pipe */ + OUTREG(pipe_conf_reg, *pipe_conf | PIPECONF_ENABLE); + + /* Enable sync */ + tmp = INREG(ADPA); + tmp &= ~ADPA_DPMS_CONTROL_MASK; + tmp |= ADPA_DPMS_D0; + OUTREG(ADPA, tmp); + + /* setup display plane */ + if (dinfo->pdev->device == PCI_DEVICE_ID_INTEL_830M) { + /* + * i830M errata: the display plane must be enabled + * to allow writes to the other bits in the plane + * control register. + */ + tmp = INREG(DSPACNTR); + if ((tmp & DISPPLANE_PLANE_ENABLE) != DISPPLANE_PLANE_ENABLE) { + tmp |= DISPPLANE_PLANE_ENABLE; + OUTREG(DSPACNTR, tmp); + OUTREG(DSPACNTR, + hw->disp_a_ctrl|DISPPLANE_PLANE_ENABLE); + mdelay(1); + } + } + + OUTREG(DSPACNTR, hw->disp_a_ctrl & ~DISPPLANE_PLANE_ENABLE); + OUTREG(DSPASTRIDE, hw->disp_a_stride); + OUTREG(DSPABASE, hw->disp_a_base); + + /* Enable plane */ + if (!blank) { + tmp = INREG(DSPACNTR); + tmp |= DISPPLANE_PLANE_ENABLE; + OUTREG(DSPACNTR, tmp); + OUTREG(DSPABASE, hw->disp_a_base); + } + + return 0; +} + +/* forward declarations */ +static void refresh_ring(struct intelfb_info *dinfo); +static void reset_state(struct intelfb_info *dinfo); +static void do_flush(struct intelfb_info *dinfo); + +static int +wait_ring(struct intelfb_info *dinfo, int n) +{ + int i = 0; + unsigned long end; + u32 last_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK; + +#if VERBOSE > 0 + DBG_MSG("wait_ring: %d\n", n); +#endif + + end = jiffies + (HZ * 3); + while (dinfo->ring_space < n) { + dinfo->ring_head = (u8 __iomem *)(INREG(PRI_RING_HEAD) & + RING_HEAD_MASK); + if (dinfo->ring_tail + RING_MIN_FREE < + (u32 __iomem) dinfo->ring_head) + dinfo->ring_space = (u32 __iomem) dinfo->ring_head + - (dinfo->ring_tail + RING_MIN_FREE); + else + dinfo->ring_space = (dinfo->ring.size + + (u32 __iomem) dinfo->ring_head) + - (dinfo->ring_tail + RING_MIN_FREE); + if ((u32 __iomem) dinfo->ring_head != last_head) { + end = jiffies + (HZ * 3); + last_head = (u32 __iomem) dinfo->ring_head; + } + i++; + if (time_before(end, jiffies)) { + if (!i) { + /* Try again */ + reset_state(dinfo); + refresh_ring(dinfo); + do_flush(dinfo); + end = jiffies + (HZ * 3); + i = 1; + } else { + WRN_MSG("ring buffer : space: %d wanted %d\n", + dinfo->ring_space, n); + WRN_MSG("lockup - turning off hardware " + "acceleration\n"); + dinfo->ring_lockup = 1; + break; + } + } + udelay(1); + } + return i; +} + +static void +do_flush(struct intelfb_info *dinfo) { + START_RING(2); + OUT_RING(MI_FLUSH | MI_WRITE_DIRTY_STATE | MI_INVALIDATE_MAP_CACHE); + OUT_RING(MI_NOOP); + ADVANCE_RING(); +} + +void +intelfbhw_do_sync(struct intelfb_info *dinfo) +{ +#if VERBOSE > 0 + DBG_MSG("intelfbhw_do_sync\n"); +#endif + + if (!dinfo->accel) + return; + + /* + * Send a flush, then wait until the ring is empty. This is what + * the XFree86 driver does, and actually it doesn't seem a lot worse + * than the recommended method (both have problems). + */ + do_flush(dinfo); + wait_ring(dinfo, dinfo->ring.size - RING_MIN_FREE); + dinfo->ring_space = dinfo->ring.size - RING_MIN_FREE; +} + +static void +refresh_ring(struct intelfb_info *dinfo) +{ +#if VERBOSE > 0 + DBG_MSG("refresh_ring\n"); +#endif + + dinfo->ring_head = (u8 __iomem *) (INREG(PRI_RING_HEAD) & + RING_HEAD_MASK); + dinfo->ring_tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK; + if (dinfo->ring_tail + RING_MIN_FREE < (u32 __iomem)dinfo->ring_head) + dinfo->ring_space = (u32 __iomem) dinfo->ring_head + - (dinfo->ring_tail + RING_MIN_FREE); + else + dinfo->ring_space = (dinfo->ring.size + + (u32 __iomem) dinfo->ring_head) + - (dinfo->ring_tail + RING_MIN_FREE); +} + +static void +reset_state(struct intelfb_info *dinfo) +{ + int i; + u32 tmp; + +#if VERBOSE > 0 + DBG_MSG("reset_state\n"); +#endif + + for (i = 0; i < FENCE_NUM; i++) + OUTREG(FENCE + (i << 2), 0); + + /* Flush the ring buffer if it's enabled. */ + tmp = INREG(PRI_RING_LENGTH); + if (tmp & RING_ENABLE) { +#if VERBOSE > 0 + DBG_MSG("reset_state: ring was enabled\n"); +#endif + refresh_ring(dinfo); + intelfbhw_do_sync(dinfo); + DO_RING_IDLE(); + } + + OUTREG(PRI_RING_LENGTH, 0); + OUTREG(PRI_RING_HEAD, 0); + OUTREG(PRI_RING_TAIL, 0); + OUTREG(PRI_RING_START, 0); +} + +/* Stop the 2D engine, and turn off the ring buffer. */ +void +intelfbhw_2d_stop(struct intelfb_info *dinfo) +{ +#if VERBOSE > 0 + DBG_MSG("intelfbhw_2d_stop: accel: %d, ring_active: %d\n", dinfo->accel, + dinfo->ring_active); +#endif + + if (!dinfo->accel) + return; + + dinfo->ring_active = 0; + reset_state(dinfo); +} + +/* + * Enable the ring buffer, and initialise the 2D engine. + * It is assumed that the graphics engine has been stopped by previously + * calling intelfb_2d_stop(). + */ +void +intelfbhw_2d_start(struct intelfb_info *dinfo) +{ +#if VERBOSE > 0 + DBG_MSG("intelfbhw_2d_start: accel: %d, ring_active: %d\n", + dinfo->accel, dinfo->ring_active); +#endif + + if (!dinfo->accel) + return; + + /* Initialise the primary ring buffer. */ + OUTREG(PRI_RING_LENGTH, 0); + OUTREG(PRI_RING_TAIL, 0); + OUTREG(PRI_RING_HEAD, 0); + + OUTREG(PRI_RING_START, dinfo->ring.physical & RING_START_MASK); + OUTREG(PRI_RING_LENGTH, + ((dinfo->ring.size - GTT_PAGE_SIZE) & RING_LENGTH_MASK) | + RING_NO_REPORT | RING_ENABLE); + refresh_ring(dinfo); + dinfo->ring_active = 1; +} + +/* 2D fillrect (solid fill or invert) */ +void +intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y, u32 w, u32 h, + u32 color, u32 pitch, u32 bpp, u32 rop) +{ + u32 br00, br09, br13, br14, br16; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_do_fillrect: (%d,%d) %dx%d, c 0x%06x, p %d bpp %d, " + "rop 0x%02x\n", x, y, w, h, color, pitch, bpp, rop); +#endif + + br00 = COLOR_BLT_CMD; + br09 = dinfo->fb_start + (y * pitch + x * (bpp / 8)); + br13 = (rop << ROP_SHIFT) | pitch; + br14 = (h << HEIGHT_SHIFT) | ((w * (bpp / 8)) << WIDTH_SHIFT); + br16 = color; + + switch (bpp) { + case 8: + br13 |= COLOR_DEPTH_8; + break; + case 16: + br13 |= COLOR_DEPTH_16; + break; + case 32: + br13 |= COLOR_DEPTH_32; + br00 |= WRITE_ALPHA | WRITE_RGB; + break; + } + + START_RING(6); + OUT_RING(br00); + OUT_RING(br13); + OUT_RING(br14); + OUT_RING(br09); + OUT_RING(br16); + OUT_RING(MI_NOOP); + ADVANCE_RING(); + +#if VERBOSE > 0 + DBG_MSG("ring = 0x%08x, 0x%08x (%d)\n", dinfo->ring_head, + dinfo->ring_tail, dinfo->ring_space); +#endif +} + +void +intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury, + u32 dstx, u32 dsty, u32 w, u32 h, u32 pitch, u32 bpp) +{ + u32 br00, br09, br11, br12, br13, br22, br23, br26; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_do_bitblt: (%d,%d)->(%d,%d) %dx%d, p %d bpp %d\n", + curx, cury, dstx, dsty, w, h, pitch, bpp); +#endif + + br00 = XY_SRC_COPY_BLT_CMD; + br09 = dinfo->fb_start; + br11 = (pitch << PITCH_SHIFT); + br12 = dinfo->fb_start; + br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT); + br22 = (dstx << WIDTH_SHIFT) | (dsty << HEIGHT_SHIFT); + br23 = ((dstx + w) << WIDTH_SHIFT) | + ((dsty + h) << HEIGHT_SHIFT); + br26 = (curx << WIDTH_SHIFT) | (cury << HEIGHT_SHIFT); + + switch (bpp) { + case 8: + br13 |= COLOR_DEPTH_8; + break; + case 16: + br13 |= COLOR_DEPTH_16; + break; + case 32: + br13 |= COLOR_DEPTH_32; + br00 |= WRITE_ALPHA | WRITE_RGB; + break; + } + + START_RING(8); + OUT_RING(br00); + OUT_RING(br13); + OUT_RING(br22); + OUT_RING(br23); + OUT_RING(br09); + OUT_RING(br26); + OUT_RING(br11); + OUT_RING(br12); + ADVANCE_RING(); +} + +int +intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, u32 w, + u32 h, const u8* cdat, u32 x, u32 y, u32 pitch, u32 bpp) +{ + int nbytes, ndwords, pad, tmp; + u32 br00, br09, br13, br18, br19, br22, br23; + int dat, ix, iy, iw; + int i, j; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_do_drawglyph: (%d,%d) %dx%d\n", x, y, w, h); +#endif + + /* size in bytes of a padded scanline */ + nbytes = ROUND_UP_TO(w, 16) / 8; + + /* Total bytes of padded scanline data to write out. */ + nbytes = nbytes * h; + + /* + * Check if the glyph data exceeds the immediate mode limit. + * It would take a large font (1K pixels) to hit this limit. + */ + if (nbytes > MAX_MONO_IMM_SIZE) + return 0; + + /* Src data is packaged a dword (32-bit) at a time. */ + ndwords = ROUND_UP_TO(nbytes, 4) / 4; + + /* + * Ring has to be padded to a quad word. But because the command starts + with 7 bytes, pad only if there is an even number of ndwords + */ + pad = !(ndwords % 2); + + tmp = (XY_MONO_SRC_IMM_BLT_CMD & DW_LENGTH_MASK) + ndwords; + br00 = (XY_MONO_SRC_IMM_BLT_CMD & ~DW_LENGTH_MASK) | tmp; + br09 = dinfo->fb_start; + br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT); + br18 = bg; + br19 = fg; + br22 = (x << WIDTH_SHIFT) | (y << HEIGHT_SHIFT); + br23 = ((x + w) << WIDTH_SHIFT) | ((y + h) << HEIGHT_SHIFT); + + switch (bpp) { + case 8: + br13 |= COLOR_DEPTH_8; + break; + case 16: + br13 |= COLOR_DEPTH_16; + break; + case 32: + br13 |= COLOR_DEPTH_32; + br00 |= WRITE_ALPHA | WRITE_RGB; + break; + } + + START_RING(8 + ndwords); + OUT_RING(br00); + OUT_RING(br13); + OUT_RING(br22); + OUT_RING(br23); + OUT_RING(br09); + OUT_RING(br18); + OUT_RING(br19); + ix = iy = 0; + iw = ROUND_UP_TO(w, 8) / 8; + while (ndwords--) { + dat = 0; + for (j = 0; j < 2; ++j) { + for (i = 0; i < 2; ++i) { + if (ix != iw || i == 0) + dat |= cdat[iy*iw + ix++] << (i+j*2)*8; + } + if (ix == iw && iy != (h-1)) { + ix = 0; + ++iy; + } + } + OUT_RING(dat); + } + if (pad) + OUT_RING(MI_NOOP); + ADVANCE_RING(); + + return 1; +} + +/* HW cursor functions. */ +void +intelfbhw_cursor_init(struct intelfb_info *dinfo) +{ + u32 tmp; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_cursor_init\n"); +#endif + + if (dinfo->mobile) { + if (!dinfo->cursor.physical) + return; + tmp = INREG(CURSOR_A_CONTROL); + tmp &= ~(CURSOR_MODE_MASK | CURSOR_MOBILE_GAMMA_ENABLE | + CURSOR_MEM_TYPE_LOCAL | + (1 << CURSOR_PIPE_SELECT_SHIFT)); + tmp |= CURSOR_MODE_DISABLE; + OUTREG(CURSOR_A_CONTROL, tmp); + OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); + } else { + tmp = INREG(CURSOR_CONTROL); + tmp &= ~(CURSOR_FORMAT_MASK | CURSOR_GAMMA_ENABLE | + CURSOR_ENABLE | CURSOR_STRIDE_MASK); + tmp = CURSOR_FORMAT_3C; + OUTREG(CURSOR_CONTROL, tmp); + OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.offset << 12); + tmp = (64 << CURSOR_SIZE_H_SHIFT) | + (64 << CURSOR_SIZE_V_SHIFT); + OUTREG(CURSOR_SIZE, tmp); + } +} + +void +intelfbhw_cursor_hide(struct intelfb_info *dinfo) +{ + u32 tmp; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_cursor_hide\n"); +#endif + + dinfo->cursor_on = 0; + if (dinfo->mobile) { + if (!dinfo->cursor.physical) + return; + tmp = INREG(CURSOR_A_CONTROL); + tmp &= ~CURSOR_MODE_MASK; + tmp |= CURSOR_MODE_DISABLE; + OUTREG(CURSOR_A_CONTROL, tmp); + /* Flush changes */ + OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); + } else { + tmp = INREG(CURSOR_CONTROL); + tmp &= ~CURSOR_ENABLE; + OUTREG(CURSOR_CONTROL, tmp); + } +} + +void +intelfbhw_cursor_show(struct intelfb_info *dinfo) +{ + u32 tmp; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_cursor_show\n"); +#endif + + dinfo->cursor_on = 1; + + if (dinfo->cursor_blanked) + return; + + if (dinfo->mobile) { + if (!dinfo->cursor.physical) + return; + tmp = INREG(CURSOR_A_CONTROL); + tmp &= ~CURSOR_MODE_MASK; + tmp |= CURSOR_MODE_64_4C_AX; + OUTREG(CURSOR_A_CONTROL, tmp); + /* Flush changes */ + OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); + } else { + tmp = INREG(CURSOR_CONTROL); + tmp |= CURSOR_ENABLE; + OUTREG(CURSOR_CONTROL, tmp); + } +} + +void +intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y) +{ + u32 tmp; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_cursor_setpos: (%d, %d)\n", x, y); +#endif + + /* + * Sets the position. The coordinates are assumed to already + * have any offset adjusted. Assume that the cursor is never + * completely off-screen, and that x, y are always >= 0. + */ + + tmp = ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT) | + ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + OUTREG(CURSOR_A_POSITION, tmp); +} + +void +intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, u32 fg) +{ +#if VERBOSE > 0 + DBG_MSG("intelfbhw_cursor_setcolor\n"); +#endif + + OUTREG(CURSOR_A_PALETTE0, bg & CURSOR_PALETTE_MASK); + OUTREG(CURSOR_A_PALETTE1, fg & CURSOR_PALETTE_MASK); + OUTREG(CURSOR_A_PALETTE2, fg & CURSOR_PALETTE_MASK); + OUTREG(CURSOR_A_PALETTE3, bg & CURSOR_PALETTE_MASK); +} + +void +intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height, + u8 *data) +{ + u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual; + int i, j, w = width / 8; + int mod = width % 8, t_mask, d_mask; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_cursor_load\n"); +#endif + + if (!dinfo->cursor.virtual) + return; + + t_mask = 0xff >> mod; + d_mask = ~(0xff >> mod); + for (i = height; i--; ) { + for (j = 0; j < w; j++) { + writeb(0x00, addr + j); + writeb(*(data++), addr + j+8); + } + if (mod) { + writeb(t_mask, addr + j); + writeb(*(data++) & d_mask, addr + j+8); + } + addr += 16; + } +} + +void +intelfbhw_cursor_reset(struct intelfb_info *dinfo) { + u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual; + int i, j; + +#if VERBOSE > 0 + DBG_MSG("intelfbhw_cursor_reset\n"); +#endif + + if (!dinfo->cursor.virtual) + return; + + for (i = 64; i--; ) { + for (j = 0; j < 8; j++) { + writeb(0xff, addr + j+0); + writeb(0x00, addr + j+8); + } + addr += 16; + } +} diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h new file mode 100644 index 00000000000..ba1920159f5 --- /dev/null +++ b/drivers/video/intelfb/intelfbhw.h @@ -0,0 +1,570 @@ +#ifndef _INTELFBHW_H +#define _INTELFBHW_H + +/* $DHD: intelfb/intelfbhw.h,v 1.5 2003/06/27 15:06:25 dawes Exp $ */ + + +/*** HW-specific data ***/ + +/* Information about the 852GM/855GM variants */ +#define INTEL_85X_CAPID 0x44 +#define INTEL_85X_VARIANT_MASK 0x7 +#define INTEL_85X_VARIANT_SHIFT 5 +#define INTEL_VAR_855GME 0x0 +#define INTEL_VAR_855GM 0x4 +#define INTEL_VAR_852GME 0x2 +#define INTEL_VAR_852GM 0x5 + +/* Information about DVO/LVDS Ports */ +#define DVOA_PORT 0x1 +#define DVOB_PORT 0x2 +#define DVOC_PORT 0x4 +#define LVDS_PORT 0x8 + +/* + * The Bridge device's PCI config space has information about the + * fb aperture size and the amount of pre-reserved memory. + */ +#define INTEL_GMCH_CTRL 0x52 +#define INTEL_GMCH_ENABLED 0x4 +#define INTEL_GMCH_MEM_MASK 0x1 +#define INTEL_GMCH_MEM_64M 0x1 +#define INTEL_GMCH_MEM_128M 0 + +#define INTEL_830_GMCH_GMS_MASK (0x7 << 4) +#define INTEL_830_GMCH_GMS_DISABLED (0x0 << 4) +#define INTEL_830_GMCH_GMS_LOCAL (0x1 << 4) +#define INTEL_830_GMCH_GMS_STOLEN_512 (0x2 << 4) +#define INTEL_830_GMCH_GMS_STOLEN_1024 (0x3 << 4) +#define INTEL_830_GMCH_GMS_STOLEN_8192 (0x4 << 4) + +#define INTEL_855_GMCH_GMS_MASK (0x7 << 4) +#define INTEL_855_GMCH_GMS_DISABLED (0x0 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_1M (0x1 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_4M (0x2 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_8M (0x3 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_16M (0x4 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_32M (0x5 << 4) + +#define INTEL_915G_GMCH_GMS_STOLEN_48M (0x6 << 4) +#define INTEL_915G_GMCH_GMS_STOLEN_64M (0x7 << 4) + +/* HW registers */ + +/* Fence registers */ +#define FENCE 0x2000 +#define FENCE_NUM 8 + +/* Primary ring buffer */ +#define PRI_RING_TAIL 0x2030 +#define RING_TAIL_MASK 0x001ffff8 +#define RING_INUSE 0x1 + +#define PRI_RING_HEAD 0x2034 +#define RING_HEAD_WRAP_MASK 0x7ff +#define RING_HEAD_WRAP_SHIFT 21 +#define RING_HEAD_MASK 0x001ffffc + +#define PRI_RING_START 0x2038 +#define RING_START_MASK 0xfffff000 + +#define PRI_RING_LENGTH 0x203c +#define RING_LENGTH_MASK 0x001ff000 +#define RING_REPORT_MASK (0x3 << 1) +#define RING_NO_REPORT (0x0 << 1) +#define RING_REPORT_64K (0x1 << 1) +#define RING_REPORT_4K (0x2 << 1) +#define RING_REPORT_128K (0x3 << 1) +#define RING_ENABLE 0x1 + +/* + * Tail can't wrap to any closer than RING_MIN_FREE bytes of the head, + * and the last RING_MIN_FREE bytes need to be padded with MI_NOOP + */ +#define RING_MIN_FREE 64 + +#define IPEHR 0x2088 + +#define INSTDONE 0x2090 +#define PRI_RING_EMPTY 1 + +#define INSTPM 0x20c0 +#define SYNC_FLUSH_ENABLE (1 << 5) + +#define INSTPS 0x20c4 + +#define MEM_MODE 0x20cc + +#define MASK_SHIFT 16 + +#define FW_BLC_0 0x20d8 +#define FW_DISPA_WM_SHIFT 0 +#define FW_DISPA_WM_MASK 0x3f +#define FW_DISPA_BL_SHIFT 8 +#define FW_DISPA_BL_MASK 0xf +#define FW_DISPB_WM_SHIFT 16 +#define FW_DISPB_WM_MASK 0x1f +#define FW_DISPB_BL_SHIFT 24 +#define FW_DISPB_BL_MASK 0x7 + +#define FW_BLC_1 0x20dc +#define FW_DISPC_WM_SHIFT 0 +#define FW_DISPC_WM_MASK 0x1f +#define FW_DISPC_BL_SHIFT 8 +#define FW_DISPC_BL_MASK 0x7 + + +/* PLL registers */ +#define VGA0_DIVISOR 0x06000 +#define VGA1_DIVISOR 0x06004 +#define VGAPD 0x06010 +#define VGAPD_0_P1_SHIFT 0 +#define VGAPD_0_P1_FORCE_DIV2 (1 << 5) +#define VGAPD_0_P2_SHIFT 7 +#define VGAPD_1_P1_SHIFT 8 +#define VGAPD_1_P1_FORCE_DIV2 (1 << 13) +#define VGAPD_1_P2_SHIFT 15 + +#define DPLL_A 0x06014 +#define DPLL_B 0x06018 +#define DPLL_VCO_ENABLE (1 << 31) +#define DPLL_2X_CLOCK_ENABLE (1 << 30) +#define DPLL_SYNCLOCK_ENABLE (1 << 29) +#define DPLL_VGA_MODE_DISABLE (1 << 28) +#define DPLL_P2_MASK 1 +#define DPLL_P2_SHIFT 23 +#define DPLL_P1_FORCE_DIV2 (1 << 21) +#define DPLL_P1_MASK 0x1f +#define DPLL_P1_SHIFT 16 +#define DPLL_REFERENCE_SELECT_MASK (0x3 << 13) +#define DPLL_REFERENCE_DEFAULT (0x0 << 13) +#define DPLL_REFERENCE_TVCLK (0x2 << 13) +#define DPLL_RATE_SELECT_MASK (1 << 8) +#define DPLL_RATE_SELECT_FP0 (0 << 8) +#define DPLL_RATE_SELECT_FP1 (1 << 8) + +#define FPA0 0x06040 +#define FPA1 0x06044 +#define FPB0 0x06048 +#define FPB1 0x0604c +#define FP_DIVISOR_MASK 0x3f +#define FP_N_DIVISOR_SHIFT 16 +#define FP_M1_DIVISOR_SHIFT 8 +#define FP_M2_DIVISOR_SHIFT 0 + +/* PLL parameters (these are for 852GM/855GM/865G, check earlier chips). */ +/* Clock values are in units of kHz */ +#define PLL_REFCLK 48000 +#define MIN_VCO_FREQ 930000 +#define MAX_VCO_FREQ 1400000 +#define MIN_CLOCK 25000 +#define MAX_CLOCK 350000 +#define P_TRANSITION_CLOCK 165000 +#define MIN_M 108 +#define MAX_M 140 +#define MIN_M1 18 +#define MAX_M1 26 +#define MIN_M2 6 +#define MAX_M2 16 +#define MIN_P 4 +#define MAX_P 128 +#define MIN_P1 0 +#define MAX_P1 31 +#define MIN_N 3 +#define MAX_N 16 + +#define CALC_VCLOCK(m1, m2, n, p1, p2) \ + ((PLL_REFCLK * (5 * ((m1) + 2) + ((m2) + 2)) / ((n) + 2)) / \ + (((p1) + 2) * (1 << (p2 + 1)))) + +#define CALC_VCLOCK3(m, n, p) ((PLL_REFCLK * (m) / (n)) / (p)) + +/* Two pipes */ +#define PIPE_A 0 +#define PIPE_B 1 +#define PIPE_MASK 1 + +/* palette registers */ +#define PALETTE_A 0x0a000 +#define PALETTE_B 0x0a800 +#ifndef PALETTE_8_ENTRIES +#define PALETTE_8_ENTRIES 256 +#endif +#define PALETTE_8_SIZE (PALETTE_8_ENTRIES * 4) +#define PALETTE_10_ENTRIES 128 +#define PALETTE_10_SIZE (PALETTE_10_ENTRIES * 8) +#define PALETTE_8_MASK 0xff +#define PALETTE_8_RED_SHIFT 16 +#define PALETTE_8_GREEN_SHIFT 8 +#define PALETTE_8_BLUE_SHIFT 0 + +/* CRTC registers */ +#define HTOTAL_A 0x60000 +#define HBLANK_A 0x60004 +#define HSYNC_A 0x60008 +#define VTOTAL_A 0x6000c +#define VBLANK_A 0x60010 +#define VSYNC_A 0x60014 +#define SRC_SIZE_A 0x6001c +#define BCLRPAT_A 0x60020 + +#define HTOTAL_B 0x61000 +#define HBLANK_B 0x61004 +#define HSYNC_B 0x61008 +#define VTOTAL_B 0x6100c +#define VBLANK_B 0x61010 +#define VSYNC_B 0x61014 +#define SRC_SIZE_B 0x6101c +#define BCLRPAT_B 0x61020 + +#define HTOTAL_MASK 0xfff +#define HTOTAL_SHIFT 16 +#define HACTIVE_MASK 0x7ff +#define HACTIVE_SHIFT 0 +#define HBLANKEND_MASK 0xfff +#define HBLANKEND_SHIFT 16 +#define HBLANKSTART_MASK 0xfff +#define HBLANKSTART_SHIFT 0 +#define HSYNCEND_MASK 0xfff +#define HSYNCEND_SHIFT 16 +#define HSYNCSTART_MASK 0xfff +#define HSYNCSTART_SHIFT 0 +#define VTOTAL_MASK 0xfff +#define VTOTAL_SHIFT 16 +#define VACTIVE_MASK 0x7ff +#define VACTIVE_SHIFT 0 +#define VBLANKEND_MASK 0xfff +#define VBLANKEND_SHIFT 16 +#define VBLANKSTART_MASK 0xfff +#define VBLANKSTART_SHIFT 0 +#define VSYNCEND_MASK 0xfff +#define VSYNCEND_SHIFT 16 +#define VSYNCSTART_MASK 0xfff +#define VSYNCSTART_SHIFT 0 +#define SRC_SIZE_HORIZ_MASK 0x7ff +#define SRC_SIZE_HORIZ_SHIFT 16 +#define SRC_SIZE_VERT_MASK 0x7ff +#define SRC_SIZE_VERT_SHIFT 0 + +#define ADPA 0x61100 +#define ADPA_DAC_ENABLE (1 << 31) +#define ADPA_DAC_DISABLE 0 +#define ADPA_PIPE_SELECT_SHIFT 30 +#define ADPA_USE_VGA_HVPOLARITY (1 << 15) +#define ADPA_SETS_HVPOLARITY 0 +#define ADPA_DPMS_CONTROL_MASK (0x3 << 10) +#define ADPA_DPMS_D0 (0x0 << 10) +#define ADPA_DPMS_D2 (0x1 << 10) +#define ADPA_DPMS_D1 (0x2 << 10) +#define ADPA_DPMS_D3 (0x3 << 10) +#define ADPA_VSYNC_ACTIVE_SHIFT 4 +#define ADPA_HSYNC_ACTIVE_SHIFT 3 +#define ADPA_SYNC_ACTIVE_MASK 1 +#define ADPA_SYNC_ACTIVE_HIGH 1 +#define ADPA_SYNC_ACTIVE_LOW 0 + +#define DVOA 0x61120 +#define DVOB 0x61140 +#define DVOC 0x61160 +#define LVDS 0x61180 +#define PORT_ENABLE (1 << 31) +#define PORT_PIPE_SELECT_SHIFT 30 +#define PORT_TV_FLAGS_MASK 0xFF +#define PORT_TV_FLAGS 0xC4 // ripped from my BIOS + // to understand and correct + +#define DVOA_SRCDIM 0x61124 +#define DVOB_SRCDIM 0x61144 +#define DVOC_SRCDIM 0x61164 + +#define PIPEACONF 0x70008 +#define PIPEBCONF 0x71008 +#define PIPECONF_ENABLE (1 << 31) +#define PIPECONF_DISABLE 0 +#define PIPECONF_DOUBLE_WIDE (1 << 30) +#define PIPECONF_SINGLE_WIDE 0 +#define PIPECONF_LOCKED (1 << 25) +#define PIPECONF_UNLOCKED 0 +#define PIPECONF_GAMMA (1 << 24) +#define PIPECONF_PALETTE 0 + +#define DISPARB 0x70030 +#define DISPARB_AEND_MASK 0x1ff +#define DISPARB_AEND_SHIFT 0 +#define DISPARB_BEND_MASK 0x3ff +#define DISPARB_BEND_SHIFT 9 + +/* Desktop HW cursor */ +#define CURSOR_CONTROL 0x70080 +#define CURSOR_ENABLE (1 << 31) +#define CURSOR_GAMMA_ENABLE (1 << 30) +#define CURSOR_STRIDE_MASK (0x3 << 28) +#define CURSOR_STRIDE_256 (0x0 << 28) +#define CURSOR_STRIDE_512 (0x1 << 28) +#define CURSOR_STRIDE_1K (0x2 << 28) +#define CURSOR_STRIDE_2K (0x3 << 28) +#define CURSOR_FORMAT_MASK (0x7 << 24) +#define CURSOR_FORMAT_2C (0x0 << 24) +#define CURSOR_FORMAT_3C (0x1 << 24) +#define CURSOR_FORMAT_4C (0x2 << 24) +#define CURSOR_FORMAT_ARGB (0x4 << 24) +#define CURSOR_FORMAT_XRGB (0x5 << 24) + +/* Mobile HW cursor (and i810) */ +#define CURSOR_A_CONTROL CURSOR_CONTROL +#define CURSOR_B_CONTROL 0x700c0 +#define CURSOR_MODE_MASK 0x27 +#define CURSOR_MODE_DISABLE 0 +#define CURSOR_MODE_64_3C 0x04 +#define CURSOR_MODE_64_4C_AX 0x05 +#define CURSOR_MODE_64_4C 0x06 +#define CURSOR_MODE_64_32B_AX 0x07 +#define CURSOR_MODE_64_ARGB_AX 0x27 +#define CURSOR_PIPE_SELECT_SHIFT 28 +#define CURSOR_MOBILE_GAMMA_ENABLE (1 << 26) +#define CURSOR_MEM_TYPE_LOCAL (1 << 25) + +/* All platforms (desktop has no pipe B) */ +#define CURSOR_A_BASEADDR 0x70084 +#define CURSOR_B_BASEADDR 0x700c4 +#define CURSOR_BASE_MASK 0xffffff00 + +#define CURSOR_A_POSITION 0x70088 +#define CURSOR_B_POSITION 0x700c8 +#define CURSOR_POS_SIGN (1 << 15) +#define CURSOR_POS_MASK 0x7ff +#define CURSOR_X_SHIFT 0 +#define CURSOR_Y_SHIFT 16 + +#define CURSOR_A_PALETTE0 0x70090 +#define CURSOR_A_PALETTE1 0x70094 +#define CURSOR_A_PALETTE2 0x70098 +#define CURSOR_A_PALETTE3 0x7009c +#define CURSOR_B_PALETTE0 0x700d0 +#define CURSOR_B_PALETTE1 0x700d4 +#define CURSOR_B_PALETTE2 0x700d8 +#define CURSOR_B_PALETTE3 0x700dc +#define CURSOR_COLOR_MASK 0xff +#define CURSOR_RED_SHIFT 16 +#define CURSOR_GREEN_SHIFT 8 +#define CURSOR_BLUE_SHIFT 0 +#define CURSOR_PALETTE_MASK 0xffffff + +/* Desktop only */ +#define CURSOR_SIZE 0x700a0 +#define CURSOR_SIZE_MASK 0x3ff +#define CURSOR_SIZE_H_SHIFT 0 +#define CURSOR_SIZE_V_SHIFT 12 + +#define DSPACNTR 0x70180 +#define DSPBCNTR 0x71180 +#define DISPPLANE_PLANE_ENABLE (1 << 31) +#define DISPPLANE_PLANE_DISABLE 0 +#define DISPPLANE_GAMMA_ENABLE (1<<30) +#define DISPPLANE_GAMMA_DISABLE 0 +#define DISPPLANE_PIXFORMAT_MASK (0xf<<26) +#define DISPPLANE_8BPP (0x2<<26) +#define DISPPLANE_15_16BPP (0x4<<26) +#define DISPPLANE_16BPP (0x5<<26) +#define DISPPLANE_32BPP_NO_ALPHA (0x6<<26) +#define DISPPLANE_32BPP (0x7<<26) +#define DISPPLANE_STEREO_ENABLE (1<<25) +#define DISPPLANE_STEREO_DISABLE 0 +#define DISPPLANE_SEL_PIPE_SHIFT 24 +#define DISPPLANE_SRC_KEY_ENABLE (1<<22) +#define DISPPLANE_SRC_KEY_DISABLE 0 +#define DISPPLANE_LINE_DOUBLE (1<<20) +#define DISPPLANE_NO_LINE_DOUBLE 0 +#define DISPPLANE_STEREO_POLARITY_FIRST 0 +#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18) +/* plane B only */ +#define DISPPLANE_ALPHA_TRANS_ENABLE (1<<15) +#define DISPPLANE_ALPHA_TRANS_DISABLE 0 +#define DISPPLANE_SPRITE_ABOVE_DISPLAYA 0 +#define DISPPLANE_SPRITE_ABOVE_OVERLAY 1 + +#define DSPABASE 0x70184 +#define DSPASTRIDE 0x70188 + +#define DSPBBASE 0x71184 +#define DSPBSTRIDE 0x71188 + +#define VGACNTRL 0x71400 +#define VGA_DISABLE (1 << 31) +#define VGA_ENABLE 0 +#define VGA_PIPE_SELECT_SHIFT 29 +#define VGA_PALETTE_READ_SELECT 23 +#define VGA_PALETTE_A_WRITE_DISABLE (1 << 22) +#define VGA_PALETTE_B_WRITE_DISABLE (1 << 21) +#define VGA_LEGACY_PALETTE (1 << 20) +#define VGA_6BIT_DAC 0 +#define VGA_8BIT_DAC (1 << 20) + +#define ADD_ID 0x71408 +#define ADD_ID_MASK 0xff + +/* BIOS scratch area registers (830M and 845G). */ +#define SWF0 0x71410 +#define SWF1 0x71414 +#define SWF2 0x71418 +#define SWF3 0x7141c +#define SWF4 0x71420 +#define SWF5 0x71424 +#define SWF6 0x71428 + +/* BIOS scratch area registers (852GM, 855GM, 865G). */ +#define SWF00 0x70410 +#define SWF01 0x70414 +#define SWF02 0x70418 +#define SWF03 0x7041c +#define SWF04 0x70420 +#define SWF05 0x70424 +#define SWF06 0x70428 + +#define SWF10 SWF0 +#define SWF11 SWF1 +#define SWF12 SWF2 +#define SWF13 SWF3 +#define SWF14 SWF4 +#define SWF15 SWF5 +#define SWF16 SWF6 + +#define SWF30 0x72414 +#define SWF31 0x72418 +#define SWF32 0x7241c + +/* Memory Commands */ +#define MI_NOOP (0x00 << 23) +#define MI_NOOP_WRITE_ID (1 << 22) +#define MI_NOOP_ID_MASK ((1 << 22) - 1) + +#define MI_FLUSH (0x04 << 23) +#define MI_WRITE_DIRTY_STATE (1 << 4) +#define MI_END_SCENE (1 << 3) +#define MI_INHIBIT_RENDER_CACHE_FLUSH (1 << 2) +#define MI_INVALIDATE_MAP_CACHE (1 << 0) + +#define MI_STORE_DWORD_IMM ((0x20 << 23) | 1) + +/* 2D Commands */ +#define COLOR_BLT_CMD ((2 << 29) | (0x40 << 22) | 3) +#define XY_COLOR_BLT_CMD ((2 << 29) | (0x50 << 22) | 4) +#define XY_SETUP_CLIP_BLT_CMD ((2 << 29) | (0x03 << 22) | 1) +#define XY_SRC_COPY_BLT_CMD ((2 << 29) | (0x53 << 22) | 6) +#define SRC_COPY_BLT_CMD ((2 << 29) | (0x43 << 22) | 4) +#define XY_MONO_PAT_BLT_CMD ((2 << 29) | (0x52 << 22) | 7) +#define XY_MONO_SRC_BLT_CMD ((2 << 29) | (0x54 << 22) | 6) +#define XY_MONO_SRC_IMM_BLT_CMD ((2 << 29) | (0x71 << 22) | 5) +#define TXT_IMM_BLT_CMD ((2 << 29) | (0x30 << 22) | 2) +#define SETUP_BLT_CMD ((2 << 29) | (0x00 << 22) | 6) + +#define DW_LENGTH_MASK 0xff + +#define WRITE_ALPHA (1 << 21) +#define WRITE_RGB (1 << 20) +#define VERT_SEED (3 << 8) +#define HORIZ_SEED (3 << 12) + +#define COLOR_DEPTH_8 (0 << 24) +#define COLOR_DEPTH_16 (1 << 24) +#define COLOR_DEPTH_32 (3 << 24) + +#define SRC_ROP_GXCOPY 0xcc +#define SRC_ROP_GXXOR 0x66 + +#define PAT_ROP_GXCOPY 0xf0 +#define PAT_ROP_GXXOR 0x5a + +#define PITCH_SHIFT 0 +#define ROP_SHIFT 16 +#define WIDTH_SHIFT 0 +#define HEIGHT_SHIFT 16 + +/* in bytes */ +#define MAX_MONO_IMM_SIZE 128 + + +/*** Macros ***/ + +/* I/O macros */ +#define INREG8(addr) readb((u8 __iomem *)(dinfo->mmio_base + (addr))) +#define INREG(addr) readl((u32 __iomem *)(dinfo->mmio_base + (addr))) +#define OUTREG8(addr, val) writeb((val),(u8 __iomem *)(dinfo->mmio_base + \ + (addr))) +#define OUTREG(addr, val) writel((val),(u32 __iomem *)(dinfo->mmio_base + \ + (addr))) + +/* Ring buffer macros */ +#define OUT_RING(n) do { \ + writel((n), (u32 __iomem *)(dinfo->ring.virtual + dinfo->ring_tail));\ + dinfo->ring_tail += 4; \ + dinfo->ring_tail &= dinfo->ring_tail_mask; \ +} while (0) + +#define START_RING(n) do { \ + if (dinfo->ring_space < (n) * 4) \ + wait_ring(dinfo,(n) * 4); \ + dinfo->ring_space -= (n) * 4; \ +} while (0) + +#define ADVANCE_RING() do { \ + OUTREG(PRI_RING_TAIL, dinfo->ring_tail); \ +} while (0) + +#define DO_RING_IDLE() do { \ + u32 head, tail; \ + do { \ + head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK; \ + tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK; \ + udelay(10); \ + } while (head != tail); \ +} while (0) + + +/* function protoypes */ +extern int intelfbhw_get_chipset(struct pci_dev *pdev, const char **name, + int *chipset, int *mobile); +extern int intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, + int *stolen_size); +extern int intelfbhw_check_non_crt(struct intelfb_info *dinfo); +extern const char *intelfbhw_dvo_to_string(int dvo); +extern int intelfbhw_validate_mode(struct intelfb_info *dinfo, + struct fb_var_screeninfo *var); +extern int intelfbhw_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); +extern void intelfbhw_do_blank(int blank, struct fb_info *info); +extern void intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp); +extern int intelfbhw_read_hw_state(struct intelfb_info *dinfo, + struct intelfb_hwstate *hw, int flag); +extern void intelfbhw_print_hw_state(struct intelfb_info *dinfo, + struct intelfb_hwstate *hw); +extern int intelfbhw_mode_to_hw(struct intelfb_info *dinfo, + struct intelfb_hwstate *hw, + struct fb_var_screeninfo *var); +extern int intelfbhw_program_mode(struct intelfb_info *dinfo, + const struct intelfb_hwstate *hw, int blank); +extern void intelfbhw_do_sync(struct intelfb_info *dinfo); +extern void intelfbhw_2d_stop(struct intelfb_info *dinfo); +extern void intelfbhw_2d_start(struct intelfb_info *dinfo); +extern void intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y, + u32 w, u32 h, u32 color, u32 pitch, u32 bpp, + u32 rop); +extern void intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury, + u32 dstx, u32 dsty, u32 w, u32 h, u32 pitch, + u32 bpp); +extern int intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, + u32 w, u32 h, const u8* cdat, u32 x, u32 y, + u32 pitch, u32 bpp); +extern void intelfbhw_cursor_init(struct intelfb_info *dinfo); +extern void intelfbhw_cursor_hide(struct intelfb_info *dinfo); +extern void intelfbhw_cursor_show(struct intelfb_info *dinfo); +extern void intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y); +extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, + u32 fg); +extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, + int height, u8 *data); +extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo); + +#endif /* _INTELFBHW_H */ |