diff options
| author | Alan Hourihane <alanh@fairlite.demon.co.uk> | 2007-05-08 03:39:25 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:32 -0400 |
| commit | dbe7e429fedb3fbc93b496cc1c3eb4fc28333ac0 (patch) | |
| tree | 9f88a999af677f65beb7041604c3a5d63bfc58db | |
| parent | 249bdbbf0dbab5554a4bfe55639e324d4758da96 (diff) | |
vmlfb: framebuffer driver for Intel Vermilion Range
Add the Intel Vermilion Range framebuffer support.
Signed-off-by: Alan Hourihane <alanh@tungstengraphics.com>
Signed-off-by: Antonino Daplas <adaplas@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | drivers/video/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/video/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/backlight/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/video/backlight/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/backlight/cr_bllcd.c | 287 | ||||
| -rw-r--r-- | drivers/video/vermilion/Makefile | 5 | ||||
| -rw-r--r-- | drivers/video/vermilion/cr_pll.c | 208 | ||||
| -rw-r--r-- | drivers/video/vermilion/vermilion.c | 1195 | ||||
| -rw-r--r-- | drivers/video/vermilion/vermilion.h | 260 |
9 files changed, 1981 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 863c5983ee60..74d764e2e1fa 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
| @@ -882,6 +882,22 @@ config FB_I810_I2C | |||
| 882 | select FB_DDC | 882 | select FB_DDC |
| 883 | help | 883 | help |
| 884 | 884 | ||
| 885 | config FB_LE80578 | ||
| 886 | tristate "Intel LE80578 (Vermilion) support" | ||
| 887 | depends on FB && PCI && X86 | ||
| 888 | select FB_MODE_HELPERS | ||
| 889 | select FB_CFB_FILLRECT | ||
| 890 | select FB_CFB_COPYAREA | ||
| 891 | select FB_CFB_IMAGEBLIT | ||
| 892 | help | ||
| 893 | This driver supports the LE80578 (Vermilion Range) chipset | ||
| 894 | |||
| 895 | config FB_CARILLO_RANCH | ||
| 896 | tristate "Intel Carillo Ranch support" | ||
| 897 | depends on FB_LE80578 && FB && PCI && X86 | ||
| 898 | help | ||
| 899 | This driver supports the LE80578 (Carillo Ranch) board | ||
| 900 | |||
| 885 | config FB_INTEL | 901 | config FB_INTEL |
| 886 | tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)" | 902 | tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)" |
| 887 | depends on FB && EXPERIMENTAL && PCI && X86 | 903 | depends on FB && EXPERIMENTAL && PCI && X86 |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6c7b26e81fc2..a59395d6189d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
| @@ -56,6 +56,7 @@ obj-$(CONFIG_FB_IMSTT) += imsttfb.o | |||
| 56 | obj-$(CONFIG_FB_FM2) += fm2fb.o | 56 | obj-$(CONFIG_FB_FM2) += fm2fb.o |
| 57 | obj-$(CONFIG_FB_CYBLA) += cyblafb.o | 57 | obj-$(CONFIG_FB_CYBLA) += cyblafb.o |
| 58 | obj-$(CONFIG_FB_TRIDENT) += tridentfb.o | 58 | obj-$(CONFIG_FB_TRIDENT) += tridentfb.o |
| 59 | obj-$(CONFIG_FB_LE80578) += vermilion/ | ||
| 59 | obj-$(CONFIG_FB_S3) += s3fb.o | 60 | obj-$(CONFIG_FB_S3) += s3fb.o |
| 60 | obj-$(CONFIG_FB_STI) += stifb.o | 61 | obj-$(CONFIG_FB_STI) += stifb.o |
| 61 | obj-$(CONFIG_FB_FFB) += ffb.o sbuslib.o | 62 | obj-$(CONFIG_FB_FFB) += ffb.o sbuslib.o |
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 47d15b5d985a..fbef663fc057 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
| @@ -63,3 +63,11 @@ config BACKLIGHT_PROGEAR | |||
| 63 | help | 63 | help |
| 64 | If you have a Frontpath ProGear say Y to enable the | 64 | If you have a Frontpath ProGear say Y to enable the |
| 65 | backlight driver. | 65 | backlight driver. |
| 66 | |||
| 67 | config BACKLIGHT_CARILLO_RANCH | ||
| 68 | tristate "Intel Carillo Ranch Backlight Driver" | ||
| 69 | depends on BACKLIGHT_CLASS_DEVICE && LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578 | ||
| 70 | default n | ||
| 71 | help | ||
| 72 | If you have a Intel LE80578 (Carillo Ranch) say Y to enable the | ||
| 73 | backlight driver. | ||
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 0c3ce46f5094..c6e2266f63e2 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
| @@ -6,3 +6,4 @@ obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o | |||
| 6 | obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o | 6 | obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o |
| 7 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o | 7 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o |
| 8 | obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o | 8 | obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o |
| 9 | obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o | ||
diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c new file mode 100644 index 000000000000..e9bbc3455c94 --- /dev/null +++ b/drivers/video/backlight/cr_bllcd.c | |||
| @@ -0,0 +1,287 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) Intel Corp. 2007. | ||
| 3 | * All Rights Reserved. | ||
| 4 | * | ||
| 5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
| 6 | * develop this driver. | ||
| 7 | * | ||
| 8 | * This file is part of the Carillo Ranch video subsystem driver. | ||
| 9 | * The Carillo Ranch video subsystem driver is free software; | ||
| 10 | * you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * The Carillo Ranch video subsystem driver is distributed | ||
| 16 | * in the hope that it will be useful, | ||
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | * GNU General Public License for more details. | ||
| 20 | * | ||
| 21 | * You should have received a copy of the GNU General Public License | ||
| 22 | * along with this driver; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 24 | * | ||
| 25 | * Authors: | ||
| 26 | * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> | ||
| 27 | * Alan Hourihane <alanh-at-tungstengraphics-dot-com> | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/module.h> | ||
| 31 | #include <linux/kernel.h> | ||
| 32 | #include <linux/init.h> | ||
| 33 | #include <linux/platform_device.h> | ||
| 34 | #include <linux/mutex.h> | ||
| 35 | #include <linux/fb.h> | ||
| 36 | #include <linux/backlight.h> | ||
| 37 | #include <linux/lcd.h> | ||
| 38 | #include <linux/pci.h> | ||
| 39 | #include <asm/uaccess.h> | ||
| 40 | |||
| 41 | /* The LVDS- and panel power controls sits on the | ||
| 42 | * GPIO port of the ISA bridge. | ||
| 43 | */ | ||
| 44 | |||
| 45 | #define CRVML_DEVICE_LPC 0x27B8 | ||
| 46 | #define CRVML_REG_GPIOBAR 0x48 | ||
| 47 | #define CRVML_REG_GPIOEN 0x4C | ||
| 48 | #define CRVML_GPIOEN_BIT (1 << 4) | ||
| 49 | #define CRVML_PANEL_PORT 0x38 | ||
| 50 | #define CRVML_LVDS_ON 0x00000001 | ||
| 51 | #define CRVML_PANEL_ON 0x00000002 | ||
| 52 | #define CRVML_BACKLIGHT_OFF 0x00000004 | ||
| 53 | |||
| 54 | /* The PLL Clock register sits on Host bridge */ | ||
| 55 | #define CRVML_DEVICE_MCH 0x5001 | ||
| 56 | #define CRVML_REG_MCHBAR 0x44 | ||
| 57 | #define CRVML_REG_MCHEN 0x54 | ||
| 58 | #define CRVML_MCHEN_BIT (1 << 28) | ||
| 59 | #define CRVML_MCHMAP_SIZE 4096 | ||
| 60 | #define CRVML_REG_CLOCK 0xc3c | ||
| 61 | #define CRVML_CLOCK_SHIFT 8 | ||
| 62 | #define CRVML_CLOCK_MASK 0x00000f00 | ||
| 63 | |||
| 64 | static struct pci_dev *lpc_dev; | ||
| 65 | static u32 gpio_bar; | ||
| 66 | |||
| 67 | struct cr_panel { | ||
| 68 | struct backlight_device *cr_backlight_device; | ||
| 69 | struct lcd_device *cr_lcd_device; | ||
| 70 | }; | ||
| 71 | |||
| 72 | static int cr_backlight_set_intensity(struct backlight_device *bd) | ||
| 73 | { | ||
| 74 | int intensity = bd->props.brightness; | ||
| 75 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
| 76 | u32 cur = inl(addr); | ||
| 77 | |||
| 78 | if (bd->props.power == FB_BLANK_UNBLANK) | ||
| 79 | intensity = FB_BLANK_UNBLANK; | ||
| 80 | if (bd->props.fb_blank == FB_BLANK_UNBLANK) | ||
| 81 | intensity = FB_BLANK_UNBLANK; | ||
| 82 | if (bd->props.power == FB_BLANK_POWERDOWN) | ||
| 83 | intensity = FB_BLANK_POWERDOWN; | ||
| 84 | if (bd->props.fb_blank == FB_BLANK_POWERDOWN) | ||
| 85 | intensity = FB_BLANK_POWERDOWN; | ||
| 86 | |||
| 87 | if (intensity == FB_BLANK_UNBLANK) { /* FULL ON */ | ||
| 88 | cur &= ~CRVML_BACKLIGHT_OFF; | ||
| 89 | outl(cur, addr); | ||
| 90 | } else if (intensity == FB_BLANK_POWERDOWN) { /* OFF */ | ||
| 91 | cur |= CRVML_BACKLIGHT_OFF; | ||
| 92 | outl(cur, addr); | ||
| 93 | } /* anything else, don't bother */ | ||
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int cr_backlight_get_intensity(struct backlight_device *bd) | ||
| 99 | { | ||
| 100 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
| 101 | u32 cur = inl(addr); | ||
| 102 | u8 intensity; | ||
| 103 | |||
| 104 | if (cur & CRVML_BACKLIGHT_OFF) | ||
| 105 | intensity = FB_BLANK_POWERDOWN; | ||
| 106 | else | ||
| 107 | intensity = FB_BLANK_UNBLANK; | ||
| 108 | |||
| 109 | return intensity; | ||
| 110 | } | ||
| 111 | |||
| 112 | static struct backlight_ops cr_backlight_ops = { | ||
| 113 | .get_brightness = cr_backlight_get_intensity, | ||
| 114 | .update_status = cr_backlight_set_intensity, | ||
| 115 | }; | ||
| 116 | |||
| 117 | static void cr_panel_on(void) | ||
| 118 | { | ||
| 119 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
| 120 | u32 cur = inl(addr); | ||
| 121 | |||
| 122 | if (!(cur & CRVML_PANEL_ON)) { | ||
| 123 | /* Make sure LVDS controller is down. */ | ||
| 124 | if (cur & 0x00000001) { | ||
| 125 | cur &= ~CRVML_LVDS_ON; | ||
| 126 | outl(cur, addr); | ||
| 127 | } | ||
| 128 | /* Power up Panel */ | ||
| 129 | schedule_timeout(HZ / 10); | ||
| 130 | cur |= CRVML_PANEL_ON; | ||
| 131 | outl(cur, addr); | ||
| 132 | } | ||
| 133 | |||
| 134 | /* Power up LVDS controller */ | ||
| 135 | |||
| 136 | if (!(cur & CRVML_LVDS_ON)) { | ||
| 137 | schedule_timeout(HZ / 10); | ||
| 138 | outl(cur | CRVML_LVDS_ON, addr); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | static void cr_panel_off(void) | ||
| 143 | { | ||
| 144 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
| 145 | u32 cur = inl(addr); | ||
| 146 | |||
| 147 | /* Power down LVDS controller first to avoid high currents */ | ||
| 148 | if (cur & CRVML_LVDS_ON) { | ||
| 149 | cur &= ~CRVML_LVDS_ON; | ||
| 150 | outl(cur, addr); | ||
| 151 | } | ||
| 152 | if (cur & CRVML_PANEL_ON) { | ||
| 153 | schedule_timeout(HZ / 10); | ||
| 154 | outl(cur & ~CRVML_PANEL_ON, addr); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | static int cr_lcd_set_power(struct lcd_device *ld, int power) | ||
| 159 | { | ||
| 160 | if (power == FB_BLANK_UNBLANK) | ||
| 161 | cr_panel_on(); | ||
| 162 | if (power == FB_BLANK_POWERDOWN) | ||
| 163 | cr_panel_off(); | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static struct lcd_ops cr_lcd_ops = { | ||
| 169 | .set_power = cr_lcd_set_power, | ||
| 170 | }; | ||
| 171 | |||
| 172 | static int cr_backlight_probe(struct platform_device *pdev) | ||
| 173 | { | ||
| 174 | struct cr_panel *crp; | ||
| 175 | u8 dev_en; | ||
| 176 | |||
| 177 | crp = kzalloc(sizeof(crp), GFP_KERNEL); | ||
| 178 | if (crp == NULL) | ||
| 179 | return -ENOMEM; | ||
| 180 | |||
| 181 | lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, | ||
| 182 | CRVML_DEVICE_LPC, NULL); | ||
| 183 | if (!lpc_dev) { | ||
| 184 | printk("INTEL CARILLO RANCH LPC not found.\n"); | ||
| 185 | return -ENODEV; | ||
| 186 | } | ||
| 187 | |||
| 188 | pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en); | ||
| 189 | if (!(dev_en & CRVML_GPIOEN_BIT)) { | ||
| 190 | printk(KERN_ERR | ||
| 191 | "Carillo Ranch GPIO device was not enabled.\n"); | ||
| 192 | pci_dev_put(lpc_dev); | ||
| 193 | return -ENODEV; | ||
| 194 | } | ||
| 195 | |||
| 196 | crp->cr_backlight_device = backlight_device_register("cr-backlight", | ||
| 197 | &pdev->dev, NULL, | ||
| 198 | &cr_backlight_ops); | ||
| 199 | if (IS_ERR(crp->cr_backlight_device)) { | ||
| 200 | pci_dev_put(lpc_dev); | ||
| 201 | return PTR_ERR(crp->cr_backlight_device); | ||
| 202 | } | ||
| 203 | |||
| 204 | crp->cr_lcd_device = lcd_device_register("cr-lcd", | ||
| 205 | &pdev->dev, | ||
| 206 | &cr_lcd_ops); | ||
| 207 | |||
| 208 | if (IS_ERR(crp->cr_lcd_device)) { | ||
| 209 | pci_dev_put(lpc_dev); | ||
| 210 | return PTR_ERR(crp->cr_backlight_device); | ||
| 211 | } | ||
| 212 | |||
| 213 | pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, | ||
| 214 | &gpio_bar); | ||
| 215 | gpio_bar &= ~0x3F; | ||
| 216 | |||
| 217 | crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; | ||
| 218 | crp->cr_backlight_device->props.brightness = 0; | ||
| 219 | crp->cr_backlight_device->props.max_brightness = 0; | ||
| 220 | cr_backlight_set_intensity(crp->cr_backlight_device); | ||
| 221 | |||
| 222 | cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK); | ||
| 223 | |||
| 224 | platform_set_drvdata(pdev, crp); | ||
| 225 | |||
| 226 | return 0; | ||
| 227 | } | ||
| 228 | |||
| 229 | static int cr_backlight_remove(struct platform_device *pdev) | ||
| 230 | { | ||
| 231 | struct cr_panel *crp = platform_get_drvdata(pdev); | ||
| 232 | crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN; | ||
| 233 | crp->cr_backlight_device->props.brightness = 0; | ||
| 234 | crp->cr_backlight_device->props.max_brightness = 0; | ||
| 235 | cr_backlight_set_intensity(crp->cr_backlight_device); | ||
| 236 | cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN); | ||
| 237 | backlight_device_unregister(crp->cr_backlight_device); | ||
| 238 | lcd_device_unregister(crp->cr_lcd_device); | ||
| 239 | pci_dev_put(lpc_dev); | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | static struct platform_driver cr_backlight_driver = { | ||
| 245 | .probe = cr_backlight_probe, | ||
| 246 | .remove = cr_backlight_remove, | ||
| 247 | .driver = { | ||
| 248 | .name = "cr_backlight", | ||
| 249 | }, | ||
| 250 | }; | ||
| 251 | |||
| 252 | static struct platform_device *crp; | ||
| 253 | |||
| 254 | static int __init cr_backlight_init(void) | ||
| 255 | { | ||
| 256 | int ret = platform_driver_register(&cr_backlight_driver); | ||
| 257 | |||
| 258 | if (!ret) { | ||
| 259 | crp = platform_device_alloc("cr_backlight", -1); | ||
| 260 | if (!crp) | ||
| 261 | return -ENOMEM; | ||
| 262 | |||
| 263 | ret = platform_device_add(crp); | ||
| 264 | |||
| 265 | if (ret) { | ||
| 266 | platform_device_put(crp); | ||
| 267 | platform_driver_unregister(&cr_backlight_driver); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | printk("Carillo Ranch Backlight Driver Initialized.\n"); | ||
| 272 | |||
| 273 | return ret; | ||
| 274 | } | ||
| 275 | |||
| 276 | static void __exit cr_backlight_exit(void) | ||
| 277 | { | ||
| 278 | platform_device_unregister(crp); | ||
| 279 | platform_driver_unregister(&cr_backlight_driver); | ||
| 280 | } | ||
| 281 | |||
| 282 | module_init(cr_backlight_init); | ||
| 283 | module_exit(cr_backlight_exit); | ||
| 284 | |||
| 285 | MODULE_AUTHOR("Tungsten Graphics Inc."); | ||
| 286 | MODULE_DESCRIPTION("Carillo Ranch Backlight Driver"); | ||
| 287 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vermilion/Makefile b/drivers/video/vermilion/Makefile new file mode 100644 index 000000000000..cc21a656153d --- /dev/null +++ b/drivers/video/vermilion/Makefile | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | obj-$(CONFIG_FB_LE80578) += vmlfb.o | ||
| 2 | obj-$(CONFIG_FB_CARILLO_RANCH) += crvml.o | ||
| 3 | |||
| 4 | vmlfb-objs := vermilion.o | ||
| 5 | crvml-objs := cr_pll.o | ||
diff --git a/drivers/video/vermilion/cr_pll.c b/drivers/video/vermilion/cr_pll.c new file mode 100644 index 000000000000..ebc6e6e0dd0f --- /dev/null +++ b/drivers/video/vermilion/cr_pll.c | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) Intel Corp. 2007. | ||
| 3 | * All Rights Reserved. | ||
| 4 | * | ||
| 5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
| 6 | * develop this driver. | ||
| 7 | * | ||
| 8 | * This file is part of the Carillo Ranch video subsystem driver. | ||
| 9 | * The Carillo Ranch video subsystem driver is free software; | ||
| 10 | * you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * The Carillo Ranch video subsystem driver is distributed | ||
| 16 | * in the hope that it will be useful, | ||
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | * GNU General Public License for more details. | ||
| 20 | * | ||
| 21 | * You should have received a copy of the GNU General Public License | ||
| 22 | * along with this driver; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 24 | * | ||
| 25 | * Authors: | ||
| 26 | * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> | ||
| 27 | * Alan Hourihane <alanh-at-tungstengraphics-dot-com> | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/module.h> | ||
| 31 | #include <linux/kernel.h> | ||
| 32 | #include <linux/pci.h> | ||
| 33 | #include <linux/errno.h> | ||
| 34 | #include <linux/fb.h> | ||
| 35 | #include "vermilion.h" | ||
| 36 | |||
| 37 | /* The PLL Clock register sits on Host bridge */ | ||
| 38 | #define CRVML_DEVICE_MCH 0x5001 | ||
| 39 | #define CRVML_REG_MCHBAR 0x44 | ||
| 40 | #define CRVML_REG_MCHEN 0x54 | ||
| 41 | #define CRVML_MCHEN_BIT (1 << 28) | ||
| 42 | #define CRVML_MCHMAP_SIZE 4096 | ||
| 43 | #define CRVML_REG_CLOCK 0xc3c | ||
| 44 | #define CRVML_CLOCK_SHIFT 8 | ||
| 45 | #define CRVML_CLOCK_MASK 0x00000f00 | ||
| 46 | |||
| 47 | static struct pci_dev *mch_dev; | ||
| 48 | static u32 mch_bar; | ||
| 49 | static void __iomem *mch_regs_base; | ||
| 50 | static u32 saved_clock; | ||
| 51 | |||
| 52 | static const unsigned crvml_clocks[] = { | ||
| 53 | 6750, | ||
| 54 | 13500, | ||
| 55 | 27000, | ||
| 56 | 29700, | ||
| 57 | 37125, | ||
| 58 | 54000, | ||
| 59 | 59400, | ||
| 60 | 74250, | ||
| 61 | 120000 | ||
| 62 | /* | ||
| 63 | * There are more clocks, but they are disabled on the CR board. | ||
| 64 | */ | ||
| 65 | }; | ||
| 66 | |||
| 67 | static const u32 crvml_clock_bits[] = { | ||
| 68 | 0x0a, | ||
| 69 | 0x09, | ||
| 70 | 0x08, | ||
| 71 | 0x07, | ||
| 72 | 0x06, | ||
| 73 | 0x05, | ||
| 74 | 0x04, | ||
| 75 | 0x03, | ||
| 76 | 0x0b | ||
| 77 | }; | ||
| 78 | |||
| 79 | static const unsigned crvml_num_clocks = ARRAY_SIZE(crvml_clocks); | ||
| 80 | |||
| 81 | static int crvml_sys_restore(struct vml_sys *sys) | ||
| 82 | { | ||
| 83 | void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; | ||
| 84 | |||
| 85 | iowrite32(saved_clock, clock_reg); | ||
| 86 | ioread32(clock_reg); | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | static int crvml_sys_save(struct vml_sys *sys) | ||
| 92 | { | ||
| 93 | void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; | ||
| 94 | |||
| 95 | saved_clock = ioread32(clock_reg); | ||
| 96 | |||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int crvml_nearest_index(const struct vml_sys *sys, int clock) | ||
| 101 | { | ||
| 102 | int i; | ||
| 103 | int cur_index = 0; | ||
| 104 | int cur_diff; | ||
| 105 | int diff; | ||
| 106 | |||
| 107 | cur_diff = clock - crvml_clocks[0]; | ||
| 108 | cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; | ||
| 109 | for (i = 1; i < crvml_num_clocks; ++i) { | ||
| 110 | diff = clock - crvml_clocks[i]; | ||
| 111 | diff = (diff < 0) ? -diff : diff; | ||
| 112 | if (diff < cur_diff) { | ||
| 113 | cur_index = i; | ||
| 114 | cur_diff = diff; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | return cur_index; | ||
| 118 | } | ||
| 119 | |||
| 120 | static int crvml_nearest_clock(const struct vml_sys *sys, int clock) | ||
| 121 | { | ||
| 122 | return crvml_clocks[crvml_nearest_index(sys, clock)]; | ||
| 123 | } | ||
| 124 | |||
| 125 | static int crvml_set_clock(struct vml_sys *sys, int clock) | ||
| 126 | { | ||
| 127 | void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; | ||
| 128 | int index; | ||
| 129 | u32 clock_val; | ||
| 130 | |||
| 131 | index = crvml_nearest_index(sys, clock); | ||
| 132 | |||
| 133 | if (crvml_clocks[index] != clock) | ||
| 134 | return -EINVAL; | ||
| 135 | |||
| 136 | clock_val = ioread32(clock_reg) & ~CRVML_CLOCK_MASK; | ||
| 137 | clock_val = crvml_clock_bits[index] << CRVML_CLOCK_SHIFT; | ||
| 138 | iowrite32(clock_val, clock_reg); | ||
| 139 | ioread32(clock_reg); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | static struct vml_sys cr_pll_ops = { | ||
| 145 | .name = "Carillo Ranch", | ||
| 146 | .save = crvml_sys_save, | ||
| 147 | .restore = crvml_sys_restore, | ||
| 148 | .set_clock = crvml_set_clock, | ||
| 149 | .nearest_clock = crvml_nearest_clock, | ||
| 150 | }; | ||
| 151 | |||
| 152 | static int __init cr_pll_init(void) | ||
| 153 | { | ||
| 154 | int err; | ||
| 155 | u32 dev_en; | ||
| 156 | |||
| 157 | mch_dev = pci_get_device(PCI_VENDOR_ID_INTEL, | ||
| 158 | CRVML_DEVICE_MCH, NULL); | ||
| 159 | if (!mch_dev) { | ||
| 160 | printk(KERN_ERR | ||
| 161 | "Could not find Carillo Ranch MCH device.\n"); | ||
| 162 | return -ENODEV; | ||
| 163 | } | ||
| 164 | |||
| 165 | pci_read_config_dword(mch_dev, CRVML_REG_MCHEN, &dev_en); | ||
| 166 | if (!(dev_en & CRVML_MCHEN_BIT)) { | ||
| 167 | printk(KERN_ERR | ||
| 168 | "Carillo Ranch MCH device was not enabled.\n"); | ||
| 169 | pci_dev_put(mch_dev); | ||
| 170 | return -ENODEV; | ||
| 171 | } | ||
| 172 | |||
| 173 | pci_read_config_dword(mch_dev, CRVML_REG_MCHBAR, | ||
| 174 | &mch_bar); | ||
| 175 | mch_regs_base = | ||
| 176 | ioremap_nocache(mch_bar, CRVML_MCHMAP_SIZE); | ||
| 177 | if (!mch_regs_base) { | ||
| 178 | printk(KERN_ERR | ||
| 179 | "Carillo Ranch MCH device was not enabled.\n"); | ||
| 180 | pci_dev_put(mch_dev); | ||
| 181 | return -ENODEV; | ||
| 182 | } | ||
| 183 | |||
| 184 | err = vmlfb_register_subsys(&cr_pll_ops); | ||
| 185 | if (err) { | ||
| 186 | printk(KERN_ERR | ||
| 187 | "Carillo Ranch failed to initialize vml_sys.\n"); | ||
| 188 | pci_dev_put(mch_dev); | ||
| 189 | return err; | ||
| 190 | } | ||
| 191 | |||
| 192 | return 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | static void __exit cr_pll_exit(void) | ||
| 196 | { | ||
| 197 | vmlfb_unregister_subsys(&cr_pll_ops); | ||
| 198 | |||
| 199 | iounmap(mch_regs_base); | ||
| 200 | pci_dev_put(mch_dev); | ||
| 201 | } | ||
| 202 | |||
| 203 | module_init(cr_pll_init); | ||
| 204 | module_exit(cr_pll_exit); | ||
| 205 | |||
| 206 | MODULE_AUTHOR("Tungsten Graphics Inc."); | ||
| 207 | MODULE_DESCRIPTION("Carillo Ranch PLL Driver"); | ||
| 208 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vermilion/vermilion.c b/drivers/video/vermilion/vermilion.c new file mode 100644 index 000000000000..de531c907718 --- /dev/null +++ b/drivers/video/vermilion/vermilion.c | |||
| @@ -0,0 +1,1195 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) Intel Corp. 2007. | ||
| 3 | * All Rights Reserved. | ||
| 4 | * | ||
| 5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
| 6 | * develop this driver. | ||
| 7 | * | ||
| 8 | * This file is part of the Vermilion Range fb driver. | ||
| 9 | * The Vermilion Range fb driver is free software; | ||
| 10 | * you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * The Vermilion Range fb driver is distributed | ||
| 16 | * in the hope that it will be useful, | ||
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | * GNU General Public License for more details. | ||
| 20 | * | ||
| 21 | * You should have received a copy of the GNU General Public License | ||
| 22 | * along with this driver; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 24 | * | ||
| 25 | * Authors: | ||
| 26 | * Thomas Hellström <thomas-at-tungstengraphics-dot-com> | ||
| 27 | * Michel Dänzer <michel-at-tungstengraphics-dot-com> | ||
| 28 | * Alan Hourihane <alanh-at-tungstengraphics-dot-com> | ||
| 29 | */ | ||
| 30 | |||
| 31 | #include <linux/module.h> | ||
| 32 | #include <linux/kernel.h> | ||
| 33 | #include <linux/errno.h> | ||
| 34 | #include <linux/string.h> | ||
| 35 | #include <linux/delay.h> | ||
| 36 | #include <linux/mm.h> | ||
| 37 | #include <linux/fb.h> | ||
| 38 | #include <linux/pci.h> | ||
| 39 | #include <asm/cacheflush.h> | ||
| 40 | #include <asm/tlbflush.h> | ||
| 41 | #include <linux/mmzone.h> | ||
| 42 | #include <asm/uaccess.h> | ||
| 43 | |||
| 44 | /* #define VERMILION_DEBUG */ | ||
| 45 | |||
| 46 | #include "vermilion.h" | ||
| 47 | |||
| 48 | #define MODULE_NAME "vmlfb" | ||
| 49 | |||
| 50 | #define VML_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) | ||
| 51 | |||
| 52 | static struct mutex vml_mutex; | ||
| 53 | static struct list_head global_no_mode; | ||
| 54 | static struct list_head global_has_mode; | ||
| 55 | static struct fb_ops vmlfb_ops; | ||
| 56 | static struct vml_sys *subsys = NULL; | ||
| 57 | static char *vml_default_mode = "1024x768@60"; | ||
| 58 | static struct fb_videomode defaultmode = { | ||
| 59 | NULL, 60, 1024, 768, 12896, 144, 24, 29, 3, 136, 6, | ||
| 60 | 0, FB_VMODE_NONINTERLACED | ||
| 61 | }; | ||
| 62 | |||
| 63 | static u32 vml_mem_requested = (10 * 1024 * 1024); | ||
| 64 | static u32 vml_mem_contig = (4 * 1024 * 1024); | ||
| 65 | static u32 vml_mem_min = (4 * 1024 * 1024); | ||
| 66 | |||
| 67 | static u32 vml_clocks[] = { | ||
| 68 | 6750, | ||
| 69 | 13500, | ||
| 70 | 27000, | ||
| 71 | 29700, | ||
| 72 | 37125, | ||
| 73 | 54000, | ||
| 74 | 59400, | ||
| 75 | 74250, | ||
| 76 | 120000, | ||
| 77 | 148500 | ||
| 78 | }; | ||
| 79 | |||
| 80 | static u32 vml_num_clocks = ARRAY_SIZE(vml_clocks); | ||
| 81 | |||
| 82 | /* | ||
| 83 | * Allocate a contiguous vram area and make its linear kernel map | ||
| 84 | * uncached. | ||
| 85 | */ | ||
| 86 | |||
| 87 | static int vmlfb_alloc_vram_area(struct vram_area *va, unsigned max_order, | ||
| 88 | unsigned min_order) | ||
| 89 | { | ||
| 90 | gfp_t flags; | ||
| 91 | unsigned long i; | ||
| 92 | pgprot_t wc_pageprot; | ||
| 93 | |||
| 94 | wc_pageprot = PAGE_KERNEL_NOCACHE; | ||
| 95 | max_order++; | ||
| 96 | do { | ||
| 97 | /* | ||
| 98 | * Really try hard to get the needed memory. | ||
| 99 | * We need memory below the first 32MB, so we | ||
| 100 | * add the __GFP_DMA flag that guarantees that we are | ||
| 101 | * below the first 16MB. | ||
| 102 | */ | ||
| 103 | |||
| 104 | flags = __GFP_DMA | __GFP_HIGH; | ||
| 105 | va->logical = | ||
| 106 | __get_free_pages(flags, --max_order); | ||
| 107 | } while (va->logical == 0 && max_order > min_order); | ||
| 108 | |||
| 109 | if (!va->logical) | ||
| 110 | return -ENOMEM; | ||
| 111 | |||
| 112 | va->phys = virt_to_phys((void *)va->logical); | ||
| 113 | va->size = PAGE_SIZE << max_order; | ||
| 114 | va->order = max_order; | ||
| 115 | |||
| 116 | /* | ||
| 117 | * It seems like __get_free_pages only ups the usage count | ||
| 118 | * of the first page. This doesn't work with nopage mapping, so | ||
| 119 | * up the usage count once more. | ||
| 120 | */ | ||
| 121 | |||
| 122 | memset((void *)va->logical, 0x00, va->size); | ||
| 123 | for (i = va->logical; i < va->logical + va->size; i += PAGE_SIZE) { | ||
| 124 | get_page(virt_to_page(i)); | ||
| 125 | } | ||
| 126 | |||
| 127 | /* | ||
| 128 | * Change caching policy of the linear kernel map to avoid | ||
| 129 | * mapping type conflicts with user-space mappings. | ||
| 130 | * The first global_flush_tlb() is really only there to do a global | ||
| 131 | * wbinvd(). | ||
| 132 | */ | ||
| 133 | |||
| 134 | global_flush_tlb(); | ||
| 135 | change_page_attr(virt_to_page(va->logical), va->size >> PAGE_SHIFT, | ||
| 136 | wc_pageprot); | ||
| 137 | global_flush_tlb(); | ||
| 138 | |||
| 139 | printk(KERN_DEBUG MODULE_NAME | ||
| 140 | ": Allocated %ld bytes vram area at 0x%08lx\n", | ||
| 141 | va->size, va->phys); | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | /* | ||
| 147 | * Free a contiguous vram area and reset its linear kernel map | ||
| 148 | * mapping type. | ||
| 149 | */ | ||
| 150 | |||
| 151 | static void vmlfb_free_vram_area(struct vram_area *va) | ||
| 152 | { | ||
| 153 | unsigned long j; | ||
| 154 | |||
| 155 | if (va->logical) { | ||
| 156 | |||
| 157 | /* | ||
| 158 | * Reset the linear kernel map caching policy. | ||
| 159 | */ | ||
| 160 | |||
| 161 | change_page_attr(virt_to_page(va->logical), | ||
| 162 | va->size >> PAGE_SHIFT, PAGE_KERNEL); | ||
| 163 | global_flush_tlb(); | ||
| 164 | |||
| 165 | /* | ||
| 166 | * Decrease the usage count on the pages we've used | ||
| 167 | * to compensate for upping when allocating. | ||
| 168 | */ | ||
| 169 | |||
| 170 | for (j = va->logical; j < va->logical + va->size; | ||
| 171 | j += PAGE_SIZE) { | ||
| 172 | (void)put_page_testzero(virt_to_page(j)); | ||
| 173 | } | ||
| 174 | |||
| 175 | printk(KERN_DEBUG MODULE_NAME | ||
| 176 | ": Freeing %ld bytes vram area at 0x%08lx\n", | ||
| 177 | va->size, va->phys); | ||
| 178 | free_pages(va->logical, va->order); | ||
| 179 | |||
| 180 | va->logical = 0; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | /* | ||
| 185 | * Free allocated vram. | ||
| 186 | */ | ||
| 187 | |||
| 188 | static void vmlfb_free_vram(struct vml_info *vinfo) | ||
| 189 | { | ||
| 190 | int i; | ||
| 191 | |||
| 192 | for (i = 0; i < vinfo->num_areas; ++i) { | ||
| 193 | vmlfb_free_vram_area(&vinfo->vram[i]); | ||
| 194 | } | ||
| 195 | vinfo->num_areas = 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* | ||
| 199 | * Allocate vram. Currently we try to allocate contiguous areas from the | ||
| 200 | * __GFP_DMA zone and puzzle them together. A better approach would be to | ||
| 201 | * allocate one contiguous area for scanout and use one-page allocations for | ||
| 202 | * offscreen areas. This requires user-space and GPU virtual mappings. | ||
| 203 | */ | ||
| 204 | |||
| 205 | static int vmlfb_alloc_vram(struct vml_info *vinfo, | ||
| 206 | size_t requested, | ||
| 207 | size_t min_total, size_t min_contig) | ||
| 208 | { | ||
| 209 | int i, j; | ||
| 210 | int order; | ||
| 211 | int contiguous; | ||
| 212 | int err; | ||
| 213 | struct vram_area *va; | ||
| 214 | struct vram_area *va2; | ||
| 215 | |||
| 216 | vinfo->num_areas = 0; | ||
| 217 | for (i = 0; i < VML_VRAM_AREAS; ++i) { | ||
| 218 | va = &vinfo->vram[i]; | ||
| 219 | order = 0; | ||
| 220 | |||
| 221 | while (requested > (PAGE_SIZE << order) && order < MAX_ORDER) | ||
| 222 | order++; | ||
| 223 | |||
| 224 | err = vmlfb_alloc_vram_area(va, order, 0); | ||
| 225 | |||
| 226 | if (err) | ||
| 227 | break; | ||
| 228 | |||
| 229 | if (i == 0) { | ||
| 230 | vinfo->vram_start = va->phys; | ||
| 231 | vinfo->vram_logical = (void __iomem *) va->logical; | ||
| 232 | vinfo->vram_contig_size = va->size; | ||
| 233 | vinfo->num_areas = 1; | ||
| 234 | } else { | ||
| 235 | contiguous = 0; | ||
| 236 | |||
| 237 | for (j = 0; j < i; ++j) { | ||
| 238 | va2 = &vinfo->vram[j]; | ||
| 239 | if (va->phys + va->size == va2->phys || | ||
| 240 | va2->phys + va2->size == va->phys) { | ||
| 241 | contiguous = 1; | ||
| 242 | break; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | if (contiguous) { | ||
| 247 | vinfo->num_areas++; | ||
| 248 | if (va->phys < vinfo->vram_start) { | ||
| 249 | vinfo->vram_start = va->phys; | ||
| 250 | vinfo->vram_logical = | ||
| 251 | (void __iomem *)va->logical; | ||
| 252 | } | ||
| 253 | vinfo->vram_contig_size += va->size; | ||
| 254 | } else { | ||
| 255 | vmlfb_free_vram_area(va); | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | if (requested < va->size) | ||
| 261 | break; | ||
| 262 | else | ||
| 263 | requested -= va->size; | ||
| 264 | } | ||
| 265 | |||
| 266 | if (vinfo->vram_contig_size > min_total && | ||
| 267 | vinfo->vram_contig_size > min_contig) { | ||
| 268 | |||
| 269 | printk(KERN_DEBUG MODULE_NAME | ||
| 270 | ": Contiguous vram: %ld bytes at physical 0x%08lx.\n", | ||
| 271 | (unsigned long)vinfo->vram_contig_size, | ||
| 272 | (unsigned long)vinfo->vram_start); | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | printk(KERN_ERR MODULE_NAME | ||
| 278 | ": Could not allocate requested minimal amount of vram.\n"); | ||
| 279 | |||
| 280 | vmlfb_free_vram(vinfo); | ||
| 281 | |||
| 282 | return -ENOMEM; | ||
| 283 | } | ||
| 284 | |||
| 285 | /* | ||
| 286 | * Find the GPU to use with our display controller. | ||
| 287 | */ | ||
| 288 | |||
| 289 | static int vmlfb_get_gpu(struct vml_par *par) | ||
| 290 | { | ||
| 291 | mutex_lock(&vml_mutex); | ||
| 292 | |||
| 293 | par->gpu = pci_get_device(PCI_VENDOR_ID_INTEL, VML_DEVICE_GPU, NULL); | ||
| 294 | |||
| 295 | if (!par->gpu) { | ||
| 296 | mutex_unlock(&vml_mutex); | ||
| 297 | return -ENODEV; | ||
| 298 | } | ||
| 299 | |||
| 300 | mutex_unlock(&vml_mutex); | ||
| 301 | |||
| 302 | if (pci_enable_device(par->gpu) < 0) | ||
| 303 | return -ENODEV; | ||
| 304 | |||
| 305 | return 0; | ||
| 306 | } | ||
| 307 | |||
| 308 | /* | ||
| 309 | * Find a contiguous vram area that contains a given offset from vram start. | ||
| 310 | */ | ||
| 311 | static int vmlfb_vram_offset(struct vml_info *vinfo, unsigned long offset) | ||
| 312 | { | ||
| 313 | unsigned long aoffset; | ||
| 314 | unsigned i; | ||
| 315 | |||
| 316 | for (i = 0; i < vinfo->num_areas; ++i) { | ||
| 317 | aoffset = offset - (vinfo->vram[i].phys - vinfo->vram_start); | ||
| 318 | |||
| 319 | if (aoffset < vinfo->vram[i].size) { | ||
| 320 | return 0; | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | return -EINVAL; | ||
| 325 | } | ||
| 326 | |||
| 327 | /* | ||
| 328 | * Remap the MMIO register spaces of the VDC and the GPU. | ||
| 329 | */ | ||
| 330 | |||
| 331 | static int vmlfb_enable_mmio(struct vml_par *par) | ||
| 332 | { | ||
| 333 | int err; | ||
| 334 | |||
| 335 | par->vdc_mem_base = pci_resource_start(par->vdc, 0); | ||
| 336 | par->vdc_mem_size = pci_resource_len(par->vdc, 0); | ||
| 337 | if (!request_mem_region(par->vdc_mem_base, par->vdc_mem_size, "vmlfb")) { | ||
| 338 | printk(KERN_ERR MODULE_NAME | ||
| 339 | ": Could not claim display controller MMIO.\n"); | ||
| 340 | return -EBUSY; | ||
| 341 | } | ||
| 342 | par->vdc_mem = ioremap_nocache(par->vdc_mem_base, par->vdc_mem_size); | ||
| 343 | if (par->vdc_mem == NULL) { | ||
| 344 | printk(KERN_ERR MODULE_NAME | ||
| 345 | ": Could not map display controller MMIO.\n"); | ||
| 346 | err = -ENOMEM; | ||
| 347 | goto out_err_0; | ||
| 348 | } | ||
| 349 | |||
| 350 | par->gpu_mem_base = pci_resource_start(par->gpu, 0); | ||
| 351 | par->gpu_mem_size = pci_resource_len(par->gpu, 0); | ||
| 352 | if (!request_mem_region(par->gpu_mem_base, par->gpu_mem_size, "vmlfb")) { | ||
| 353 | printk(KERN_ERR MODULE_NAME ": Could not claim GPU MMIO.\n"); | ||
| 354 | err = -EBUSY; | ||
| 355 | goto out_err_1; | ||
| 356 | } | ||
| 357 | par->gpu_mem = ioremap_nocache(par->gpu_mem_base, par->gpu_mem_size); | ||
| 358 | if (par->gpu_mem == NULL) { | ||
| 359 | printk(KERN_ERR MODULE_NAME ": Could not map GPU MMIO.\n"); | ||
| 360 | err = -ENOMEM; | ||
| 361 | goto out_err_2; | ||
| 362 | } | ||
| 363 | |||
| 364 | return 0; | ||
| 365 | |||
| 366 | out_err_2: | ||
| 367 | release_mem_region(par->gpu_mem_base, par->gpu_mem_size); | ||
| 368 | out_err_1: | ||
| 369 | iounmap(par->vdc_mem); | ||
| 370 | out_err_0: | ||
| 371 | release_mem_region(par->vdc_mem_base, par->vdc_mem_size); | ||
| 372 | return err; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* | ||
| 376 | * Unmap the VDC and GPU register spaces. | ||
| 377 | */ | ||
| 378 | |||
| 379 | static void vmlfb_disable_mmio(struct vml_par *par) | ||
| 380 | { | ||
| 381 | iounmap(par->gpu_mem); | ||
| 382 | release_mem_region(par->gpu_mem_base, par->gpu_mem_size); | ||
| 383 | iounmap(par->vdc_mem); | ||
| 384 | release_mem_region(par->vdc_mem_base, par->vdc_mem_size); | ||
| 385 | } | ||
| 386 | |||
| 387 | /* | ||
| 388 | * Release and uninit the VDC and GPU. | ||
| 389 | */ | ||
| 390 | |||
| 391 | static void vmlfb_release_devices(struct vml_par *par) | ||
| 392 | { | ||
| 393 | if (atomic_dec_and_test(&par->refcount)) { | ||
| 394 | pci_set_drvdata(par->vdc, NULL); | ||
| 395 | pci_disable_device(par->gpu); | ||
| 396 | pci_disable_device(par->vdc); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | /* | ||
| 401 | * Free up allocated resources for a device. | ||
| 402 | */ | ||
| 403 | |||
| 404 | static void __devexit vml_pci_remove(struct pci_dev *dev) | ||
| 405 | { | ||
| 406 | struct fb_info *info; | ||
| 407 | struct vml_info *vinfo; | ||
| 408 | struct vml_par *par; | ||
| 409 | |||
| 410 | info = pci_get_drvdata(dev); | ||
| 411 | if (info) { | ||
| 412 | vinfo = container_of(info, struct vml_info, info); | ||
| 413 | par = vinfo->par; | ||
| 414 | mutex_lock(&vml_mutex); | ||
| 415 | unregister_framebuffer(info); | ||
| 416 | fb_dealloc_cmap(&info->cmap); | ||
| 417 | vmlfb_free_vram(vinfo); | ||
| 418 | vmlfb_disable_mmio(par); | ||
| 419 | vmlfb_release_devices(par); | ||
| 420 | kfree(vinfo); | ||
| 421 | kfree(par); | ||
| 422 | mutex_unlock(&vml_mutex); | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | static void vmlfb_set_pref_pixel_format(struct fb_var_screeninfo *var) | ||
| 427 | { | ||
| 428 | switch (var->bits_per_pixel) { | ||
| 429 | case 16: | ||
| 430 | var->blue.offset = 0; | ||
| 431 | var->blue.length = 5; | ||
| 432 | var->green.offset = 5; | ||
| 433 | var->green.length = 5; | ||
| 434 | var->red.offset = 10; | ||
| 435 | var->red.length = 5; | ||
| 436 | var->transp.offset = 15; | ||
| 437 | var->transp.length = 1; | ||
| 438 | break; | ||
| 439 | case 32: | ||
| 440 | var->blue.offset = 0; | ||
| 441 | var->blue.length = 8; | ||
| 442 | var->green.offset = 8; | ||
| 443 | var->green.length = 8; | ||
| 444 | var->red.offset = 16; | ||
| 445 | var->red.length = 8; | ||
| 446 | var->transp.offset = 24; | ||
| 447 | var->transp.length = 0; | ||
| 448 | break; | ||
| 449 | default: | ||
| 450 | break; | ||
| 451 | } | ||
| 452 | |||
| 453 | var->blue.msb_right = var->green.msb_right = | ||
| 454 | var->red.msb_right = var->transp.msb_right = 0; | ||
| 455 | } | ||
| 456 | |||
| 457 | /* | ||
| 458 | * Device initialization. | ||
| 459 | * We initialize one vml_par struct per device and one vml_info | ||
| 460 | * struct per pipe. Currently we have only one pipe. | ||
| 461 | */ | ||
| 462 | |||
| 463 | static int __devinit vml_pci_probe(struct pci_dev *dev, | ||
| 464 | const struct pci_device_id *id) | ||
| 465 | { | ||
| 466 | struct vml_info *vinfo; | ||
| 467 | struct fb_info *info; | ||
| 468 | struct vml_par *par; | ||
| 469 | int err = 0; | ||
| 470 | |||
| 471 | par = kzalloc(sizeof(*par), GFP_KERNEL); | ||
| 472 | if (par == NULL) | ||
| 473 | return -ENOMEM; | ||
| 474 | |||
| 475 | vinfo = kzalloc(sizeof(*vinfo), GFP_KERNEL); | ||
| 476 | if (vinfo == NULL) { | ||
| 477 | err = -ENOMEM; | ||
| 478 | goto out_err_0; | ||
| 479 | } | ||
| 480 | |||
| 481 | vinfo->par = par; | ||
| 482 | par->vdc = dev; | ||
| 483 | atomic_set(&par->refcount, 1); | ||
| 484 | |||
| 485 | switch (id->device) { | ||
| 486 | case VML_DEVICE_VDC: | ||
| 487 | if ((err = vmlfb_get_gpu(par))) | ||
| 488 | goto out_err_1; | ||
| 489 | pci_set_drvdata(dev, &vinfo->info); | ||
| 490 | break; | ||
| 491 | default: | ||
| 492 | err = -ENODEV; | ||
| 493 | goto out_err_1; | ||
| 494 | break; | ||
| 495 | } | ||
| 496 | |||
| 497 | info = &vinfo->info; | ||
| 498 | info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK; | ||
| 499 | |||
| 500 | err = vmlfb_enable_mmio(par); | ||
| 501 | if (err) | ||
| 502 | goto out_err_2; | ||
| 503 | |||
| 504 | err = vmlfb_alloc_vram(vinfo, vml_mem_requested, | ||
| 505 | vml_mem_contig, vml_mem_min); | ||
| 506 | if (err) | ||
| 507 | goto out_err_3; | ||
| 508 | |||
| 509 | strcpy(info->fix.id, "Vermilion Range"); | ||
| 510 | info->fix.mmio_start = 0; | ||
| 511 | info->fix.mmio_len = 0; | ||
| 512 | info->fix.smem_start = vinfo->vram_start; | ||
| 513 | info->fix.smem_len = vinfo->vram_contig_size; | ||
| 514 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
| 515 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 516 | info->fix.ypanstep = 1; | ||
| 517 | info->fix.xpanstep = 1; | ||
| 518 | info->fix.ywrapstep = 0; | ||
| 519 | info->fix.accel = FB_ACCEL_NONE; | ||
| 520 | info->screen_base = vinfo->vram_logical; | ||
| 521 | info->pseudo_palette = vinfo->pseudo_palette; | ||
| 522 | info->par = par; | ||
| 523 | info->fbops = &vmlfb_ops; | ||
| 524 | info->device = &dev->dev; | ||
| 525 | |||
| 526 | INIT_LIST_HEAD(&vinfo->head); | ||
| 527 | vinfo->pipe_disabled = 1; | ||
| 528 | vinfo->cur_blank_mode = FB_BLANK_UNBLANK; | ||
| 529 | |||
| 530 | info->var.grayscale = 0; | ||
| 531 | info->var.bits_per_pixel = 16; | ||
| 532 | vmlfb_set_pref_pixel_format(&info->var); | ||
| 533 | |||
| 534 | if (!fb_find_mode | ||
| 535 | (&info->var, info, vml_default_mode, NULL, 0, &defaultmode, 16)) { | ||
| 536 | printk(KERN_ERR MODULE_NAME ": Could not find initial mode\n"); | ||
| 537 | } | ||
| 538 | |||
| 539 | if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { | ||
| 540 | err = -ENOMEM; | ||
| 541 | goto out_err_4; | ||
| 542 | } | ||
| 543 | |||
| 544 | err = register_framebuffer(info); | ||
| 545 | if (err) { | ||
| 546 | printk(KERN_ERR MODULE_NAME ": Register framebuffer error.\n"); | ||
| 547 | goto out_err_5; | ||
| 548 | } | ||
| 549 | |||
| 550 | printk("Initialized vmlfb\n"); | ||
| 551 | |||
| 552 | return 0; | ||
| 553 | |||
| 554 | out_err_5: | ||
| 555 | fb_dealloc_cmap(&info->cmap); | ||
| 556 | out_err_4: | ||
| 557 | vmlfb_free_vram(vinfo); | ||
| 558 | out_err_3: | ||
| 559 | vmlfb_disable_mmio(par); | ||
| 560 | out_err_2: | ||
| 561 | vmlfb_release_devices(par); | ||
| 562 | out_err_1: | ||
| 563 | kfree(vinfo); | ||
| 564 | out_err_0: | ||
| 565 | kfree(par); | ||
| 566 | return err; | ||
| 567 | } | ||
| 568 | |||
| 569 | static int vmlfb_open(struct fb_info *info, int user) | ||
| 570 | { | ||
| 571 | /* | ||
| 572 | * Save registers here? | ||
| 573 | */ | ||
| 574 | return 0; | ||
| 575 | } | ||
| 576 | |||
| 577 | static int vmlfb_release(struct fb_info *info, int user) | ||
| 578 | { | ||
| 579 | /* | ||
| 580 | * Restore registers here. | ||
| 581 | */ | ||
| 582 | |||
| 583 | return 0; | ||
| 584 | } | ||
| 585 | |||
| 586 | static int vml_nearest_clock(int clock) | ||
| 587 | { | ||
| 588 | |||
| 589 | int i; | ||
| 590 | int cur_index; | ||
| 591 | int cur_diff; | ||
| 592 | int diff; | ||
| 593 | |||
| 594 | cur_index = 0; | ||
| 595 | cur_diff = clock - vml_clocks[0]; | ||
| 596 | cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; | ||
| 597 | for (i = 1; i < vml_num_clocks; ++i) { | ||
| 598 | diff = clock - vml_clocks[i]; | ||
| 599 | diff = (diff < 0) ? -diff : diff; | ||
| 600 | if (diff < cur_diff) { | ||
| 601 | cur_index = i; | ||
| 602 | cur_diff = diff; | ||
| 603 | } | ||
| 604 | } | ||
| 605 | return vml_clocks[cur_index]; | ||
| 606 | } | ||
| 607 | |||
| 608 | static int vmlfb_check_var_locked(struct fb_var_screeninfo *var, | ||
| 609 | struct vml_info *vinfo) | ||
| 610 | { | ||
| 611 | u32 pitch; | ||
| 612 | u64 mem; | ||
| 613 | int nearest_clock; | ||
| 614 | int clock; | ||
| 615 | int clock_diff; | ||
| 616 | struct fb_var_screeninfo v; | ||
| 617 | |||
| 618 | v = *var; | ||
| 619 | clock = PICOS2KHZ(var->pixclock); | ||
| 620 | |||
| 621 | if (subsys && subsys->nearest_clock) { | ||
| 622 | nearest_clock = subsys->nearest_clock(subsys, clock); | ||
| 623 | } else { | ||
| 624 | nearest_clock = vml_nearest_clock(clock); | ||
| 625 | } | ||
| 626 | |||
| 627 | /* | ||
| 628 | * Accept a 20% diff. | ||
| 629 | */ | ||
| 630 | |||
| 631 | clock_diff = nearest_clock - clock; | ||
| 632 | clock_diff = (clock_diff < 0) ? -clock_diff : clock_diff; | ||
| 633 | if (clock_diff > clock / 5) { | ||
| 634 | #if 0 | ||
| 635 | printk(KERN_DEBUG MODULE_NAME ": Diff failure. %d %d\n",clock_diff,clock); | ||
| 636 | #endif | ||
| 637 | return -EINVAL; | ||
| 638 | } | ||
| 639 | |||
| 640 | v.pixclock = KHZ2PICOS(nearest_clock); | ||
| 641 | |||
| 642 | if (var->xres > VML_MAX_XRES || var->yres > VML_MAX_YRES) { | ||
| 643 | printk(KERN_DEBUG MODULE_NAME ": Resolution failure.\n"); | ||
| 644 | return -EINVAL; | ||
| 645 | } | ||
| 646 | if (var->xres_virtual > VML_MAX_XRES_VIRTUAL) { | ||
| 647 | printk(KERN_DEBUG MODULE_NAME | ||
| 648 | ": Virtual resolution failure.\n"); | ||
| 649 | return -EINVAL; | ||
| 650 | } | ||
| 651 | switch (v.bits_per_pixel) { | ||
| 652 | case 0 ... 16: | ||
| 653 | v.bits_per_pixel = 16; | ||
| 654 | break; | ||
| 655 | case 17 ... 32: | ||
| 656 | v.bits_per_pixel = 32; | ||
| 657 | break; | ||
| 658 | default: | ||
| 659 | printk(KERN_DEBUG MODULE_NAME ": Invalid bpp: %d.\n", | ||
| 660 | var->bits_per_pixel); | ||
| 661 | return -EINVAL; | ||
| 662 | } | ||
| 663 | |||
| 664 | pitch = __ALIGN_MASK((var->xres * var->bits_per_pixel) >> 3, 0x3F); | ||
| 665 | mem = pitch * var->yres_virtual; | ||
| 666 | if (mem > vinfo->vram_contig_size) { | ||
| 667 | return -ENOMEM; | ||
| 668 | } | ||
| 669 | |||
| 670 | switch (v.bits_per_pixel) { | ||
| 671 | case 16: | ||
| 672 | if (var->blue.offset != 0 || | ||
| 673 | var->blue.length != 5 || | ||
| 674 | var->green.offset != 5 || | ||
| 675 | var->green.length != 5 || | ||
| 676 | var->red.offset != 10 || | ||
| 677 | var->red.length != 5 || | ||
| 678 | var->transp.offset != 15 || var->transp.length != 1) { | ||
| 679 | vmlfb_set_pref_pixel_format(&v); | ||
| 680 | } | ||
| 681 | break; | ||
| 682 | case 32: | ||
| 683 | if (var->blue.offset != 0 || | ||
| 684 | var->blue.length != 8 || | ||
| 685 | var->green.offset != 8 || | ||
| 686 | var->green.length != 8 || | ||
| 687 | var->red.offset != 16 || | ||
| 688 | var->red.length != 8 || | ||
| 689 | (var->transp.length != 0 && var->transp.length != 8) || | ||
| 690 | (var->transp.length == 8 && var->transp.offset != 24)) { | ||
| 691 | vmlfb_set_pref_pixel_format(&v); | ||
| 692 | } | ||
| 693 | break; | ||
| 694 | default: | ||
| 695 | return -EINVAL; | ||
| 696 | } | ||
| 697 | |||
| 698 | *var = v; | ||
| 699 | |||
| 700 | return 0; | ||
| 701 | } | ||
| 702 | |||
| 703 | static int vmlfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
| 704 | { | ||
| 705 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
| 706 | int ret; | ||
| 707 | |||
| 708 | mutex_lock(&vml_mutex); | ||
| 709 | ret = vmlfb_check_var_locked(var, vinfo); | ||
| 710 | mutex_unlock(&vml_mutex); | ||
| 711 | |||
| 712 | return ret; | ||
| 713 | } | ||
| 714 | |||
| 715 | static void vml_wait_vblank(struct vml_info *vinfo) | ||
| 716 | { | ||
| 717 | /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ | ||
| 718 | mdelay(20); | ||
| 719 | } | ||
| 720 | |||
| 721 | static void vmlfb_disable_pipe(struct vml_info *vinfo) | ||
| 722 | { | ||
| 723 | struct vml_par *par = vinfo->par; | ||
| 724 | |||
| 725 | /* Disable the MDVO pad */ | ||
| 726 | VML_WRITE32(par, VML_RCOMPSTAT, 0); | ||
| 727 | while (!(VML_READ32(par, VML_RCOMPSTAT) & VML_MDVO_VDC_I_RCOMP)) ; | ||
| 728 | |||
| 729 | /* Disable display planes */ | ||
| 730 | VML_WRITE32(par, VML_DSPCCNTR, | ||
| 731 | VML_READ32(par, VML_DSPCCNTR) & ~VML_GFX_ENABLE); | ||
| 732 | (void)VML_READ32(par, VML_DSPCCNTR); | ||
| 733 | /* Wait for vblank for the disable to take effect */ | ||
| 734 | vml_wait_vblank(vinfo); | ||
| 735 | |||
| 736 | /* Next, disable display pipes */ | ||
| 737 | VML_WRITE32(par, VML_PIPEACONF, 0); | ||
| 738 | (void)VML_READ32(par, VML_PIPEACONF); | ||
| 739 | |||
| 740 | vinfo->pipe_disabled = 1; | ||
| 741 | } | ||
| 742 | |||
| 743 | #ifdef VERMILION_DEBUG | ||
| 744 | static void vml_dump_regs(struct vml_info *vinfo) | ||
| 745 | { | ||
| 746 | struct vml_par *par = vinfo->par; | ||
| 747 | |||
| 748 | printk(KERN_DEBUG MODULE_NAME ": Modesetting register dump:\n"); | ||
| 749 | printk(KERN_DEBUG MODULE_NAME ": \tHTOTAL_A : 0x%08x\n", | ||
| 750 | (unsigned)VML_READ32(par, VML_HTOTAL_A)); | ||
| 751 | printk(KERN_DEBUG MODULE_NAME ": \tHBLANK_A : 0x%08x\n", | ||
| 752 | (unsigned)VML_READ32(par, VML_HBLANK_A)); | ||
| 753 | printk(KERN_DEBUG MODULE_NAME ": \tHSYNC_A : 0x%08x\n", | ||
| 754 | (unsigned)VML_READ32(par, VML_HSYNC_A)); | ||
| 755 | printk(KERN_DEBUG MODULE_NAME ": \tVTOTAL_A : 0x%08x\n", | ||
| 756 | (unsigned)VML_READ32(par, VML_VTOTAL_A)); | ||
| 757 | printk(KERN_DEBUG MODULE_NAME ": \tVBLANK_A : 0x%08x\n", | ||
| 758 | (unsigned)VML_READ32(par, VML_VBLANK_A)); | ||
| 759 | printk(KERN_DEBUG MODULE_NAME ": \tVSYNC_A : 0x%08x\n", | ||
| 760 | (unsigned)VML_READ32(par, VML_VSYNC_A)); | ||
| 761 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCSTRIDE : 0x%08x\n", | ||
| 762 | (unsigned)VML_READ32(par, VML_DSPCSTRIDE)); | ||
| 763 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCSIZE : 0x%08x\n", | ||
| 764 | (unsigned)VML_READ32(par, VML_DSPCSIZE)); | ||
| 765 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCPOS : 0x%08x\n", | ||
| 766 | (unsigned)VML_READ32(par, VML_DSPCPOS)); | ||
| 767 | printk(KERN_DEBUG MODULE_NAME ": \tDSPARB : 0x%08x\n", | ||
| 768 | (unsigned)VML_READ32(par, VML_DSPARB)); | ||
| 769 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCADDR : 0x%08x\n", | ||
| 770 | (unsigned)VML_READ32(par, VML_DSPCADDR)); | ||
| 771 | printk(KERN_DEBUG MODULE_NAME ": \tBCLRPAT_A : 0x%08x\n", | ||
| 772 | (unsigned)VML_READ32(par, VML_BCLRPAT_A)); | ||
| 773 | printk(KERN_DEBUG MODULE_NAME ": \tCANVSCLR_A : 0x%08x\n", | ||
| 774 | (unsigned)VML_READ32(par, VML_CANVSCLR_A)); | ||
| 775 | printk(KERN_DEBUG MODULE_NAME ": \tPIPEASRC : 0x%08x\n", | ||
| 776 | (unsigned)VML_READ32(par, VML_PIPEASRC)); | ||
| 777 | printk(KERN_DEBUG MODULE_NAME ": \tPIPEACONF : 0x%08x\n", | ||
| 778 | (unsigned)VML_READ32(par, VML_PIPEACONF)); | ||
| 779 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCCNTR : 0x%08x\n", | ||
| 780 | (unsigned)VML_READ32(par, VML_DSPCCNTR)); | ||
| 781 | printk(KERN_DEBUG MODULE_NAME ": \tRCOMPSTAT : 0x%08x\n", | ||
| 782 | (unsigned)VML_READ32(par, VML_RCOMPSTAT)); | ||
| 783 | printk(KERN_DEBUG MODULE_NAME ": End of modesetting register dump.\n"); | ||
| 784 | } | ||
| 785 | #endif | ||
| 786 | |||
| 787 | static int vmlfb_set_par_locked(struct vml_info *vinfo) | ||
| 788 | { | ||
| 789 | struct vml_par *par = vinfo->par; | ||
| 790 | struct fb_info *info = &vinfo->info; | ||
| 791 | struct fb_var_screeninfo *var = &info->var; | ||
| 792 | u32 htotal, hactive, hblank_start, hblank_end, hsync_start, hsync_end; | ||
| 793 | u32 vtotal, vactive, vblank_start, vblank_end, vsync_start, vsync_end; | ||
| 794 | u32 dspcntr; | ||
| 795 | int clock; | ||
| 796 | |||
| 797 | vinfo->bytes_per_pixel = var->bits_per_pixel >> 3; | ||
| 798 | vinfo->stride = | ||
| 799 | __ALIGN_MASK(var->xres_virtual * vinfo->bytes_per_pixel, 0x3F); | ||
| 800 | info->fix.line_length = vinfo->stride; | ||
| 801 | |||
| 802 | if (!subsys) | ||
| 803 | return 0; | ||
| 804 | |||
| 805 | htotal = | ||
| 806 | var->xres + var->right_margin + var->hsync_len + var->left_margin; | ||
| 807 | hactive = var->xres; | ||
| 808 | hblank_start = var->xres; | ||
| 809 | hblank_end = htotal; | ||
| 810 | hsync_start = hactive + var->right_margin; | ||
| 811 | hsync_end = hsync_start + var->hsync_len; | ||
| 812 | |||
| 813 | vtotal = | ||
| 814 | var->yres + var->lower_margin + var->vsync_len + var->upper_margin; | ||
| 815 | vactive = var->yres; | ||
| 816 | vblank_start = var->yres; | ||
| 817 | vblank_end = vtotal; | ||
| 818 | vsync_start = vactive + var->lower_margin; | ||
| 819 | vsync_end = vsync_start + var->vsync_len; | ||
| 820 | |||
| 821 | dspcntr = VML_GFX_ENABLE | VML_GFX_GAMMABYPASS; | ||
| 822 | clock = PICOS2KHZ(var->pixclock); | ||
| 823 | |||
| 824 | if (subsys->nearest_clock) { | ||
| 825 | clock = subsys->nearest_clock(subsys, clock); | ||
| 826 | } else { | ||
| 827 | clock = vml_nearest_clock(clock); | ||
| 828 | } | ||
| 829 | printk(KERN_DEBUG MODULE_NAME | ||
| 830 | ": Set mode Hfreq : %d kHz, Vfreq : %d Hz.\n", clock / htotal, | ||
| 831 | ((clock / htotal) * 1000) / vtotal); | ||
| 832 | |||
| 833 | switch (var->bits_per_pixel) { | ||
| 834 | case 16: | ||
| 835 | dspcntr |= VML_GFX_ARGB1555; | ||
| 836 | break; | ||
| 837 | case 32: | ||
| 838 | if (var->transp.length == 8) | ||
| 839 | dspcntr |= VML_GFX_ARGB8888 | VML_GFX_ALPHAMULT; | ||
| 840 | else | ||
| 841 | dspcntr |= VML_GFX_RGB0888; | ||
| 842 | break; | ||
| 843 | default: | ||
| 844 | return -EINVAL; | ||
| 845 | } | ||
| 846 | |||
| 847 | vmlfb_disable_pipe(vinfo); | ||
| 848 | mb(); | ||
| 849 | |||
| 850 | if (subsys->set_clock) | ||
| 851 | subsys->set_clock(subsys, clock); | ||
| 852 | else | ||
| 853 | return -EINVAL; | ||
| 854 | |||
| 855 | VML_WRITE32(par, VML_HTOTAL_A, ((htotal - 1) << 16) | (hactive - 1)); | ||
| 856 | VML_WRITE32(par, VML_HBLANK_A, | ||
| 857 | ((hblank_end - 1) << 16) | (hblank_start - 1)); | ||
| 858 | VML_WRITE32(par, VML_HSYNC_A, | ||
| 859 | ((hsync_end - 1) << 16) | (hsync_start - 1)); | ||
| 860 | VML_WRITE32(par, VML_VTOTAL_A, ((vtotal - 1) << 16) | (vactive - 1)); | ||
| 861 | VML_WRITE32(par, VML_VBLANK_A, | ||
| 862 | ((vblank_end - 1) << 16) | (vblank_start - 1)); | ||
| 863 | VML_WRITE32(par, VML_VSYNC_A, | ||
| 864 | ((vsync_end - 1) << 16) | (vsync_start - 1)); | ||
| 865 | VML_WRITE32(par, VML_DSPCSTRIDE, vinfo->stride); | ||
| 866 | VML_WRITE32(par, VML_DSPCSIZE, | ||
| 867 | ((var->yres - 1) << 16) | (var->xres - 1)); | ||
| 868 | VML_WRITE32(par, VML_DSPCPOS, 0x00000000); | ||
| 869 | VML_WRITE32(par, VML_DSPARB, VML_FIFO_DEFAULT); | ||
| 870 | VML_WRITE32(par, VML_BCLRPAT_A, 0x00000000); | ||
| 871 | VML_WRITE32(par, VML_CANVSCLR_A, 0x00000000); | ||
| 872 | VML_WRITE32(par, VML_PIPEASRC, | ||
| 873 | ((var->xres - 1) << 16) | (var->yres - 1)); | ||
| 874 | |||
| 875 | wmb(); | ||
| 876 | VML_WRITE32(par, VML_PIPEACONF, VML_PIPE_ENABLE); | ||
| 877 | wmb(); | ||
| 878 | VML_WRITE32(par, VML_DSPCCNTR, dspcntr); | ||
| 879 | wmb(); | ||
| 880 | VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + | ||
| 881 | var->yoffset * vinfo->stride + | ||
| 882 | var->xoffset * vinfo->bytes_per_pixel); | ||
| 883 | |||
| 884 | VML_WRITE32(par, VML_RCOMPSTAT, VML_MDVO_PAD_ENABLE); | ||
| 885 | |||
| 886 | while (!(VML_READ32(par, VML_RCOMPSTAT) & | ||
| 887 | (VML_MDVO_VDC_I_RCOMP | VML_MDVO_PAD_ENABLE))) ; | ||
| 888 | |||
| 889 | vinfo->pipe_disabled = 0; | ||
| 890 | #ifdef VERMILION_DEBUG | ||
| 891 | vml_dump_regs(vinfo); | ||
| 892 | #endif | ||
| 893 | |||
| 894 | return 0; | ||
| 895 | } | ||
| 896 | |||
| 897 | static int vmlfb_set_par(struct fb_info *info) | ||
| 898 | { | ||
| 899 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
| 900 | int ret; | ||
| 901 | |||
| 902 | mutex_lock(&vml_mutex); | ||
| 903 | list_del(&vinfo->head); | ||
| 904 | list_add(&vinfo->head, (subsys) ? &global_has_mode : &global_no_mode); | ||
| 905 | ret = vmlfb_set_par_locked(vinfo); | ||
| 906 | |||
| 907 | mutex_unlock(&vml_mutex); | ||
| 908 | return ret; | ||
| 909 | } | ||
| 910 | |||
| 911 | static int vmlfb_blank_locked(struct vml_info *vinfo) | ||
| 912 | { | ||
| 913 | struct vml_par *par = vinfo->par; | ||
| 914 | u32 cur = VML_READ32(par, VML_PIPEACONF); | ||
| 915 | |||
| 916 | switch (vinfo->cur_blank_mode) { | ||
| 917 | case FB_BLANK_UNBLANK: | ||
| 918 | if (vinfo->pipe_disabled) { | ||
| 919 | vmlfb_set_par_locked(vinfo); | ||
| 920 | } | ||
| 921 | VML_WRITE32(par, VML_PIPEACONF, cur & ~VML_PIPE_FORCE_BORDER); | ||
| 922 | (void)VML_READ32(par, VML_PIPEACONF); | ||
| 923 | break; | ||
| 924 | case FB_BLANK_NORMAL: | ||
| 925 | if (vinfo->pipe_disabled) { | ||
| 926 | vmlfb_set_par_locked(vinfo); | ||
| 927 | } | ||
| 928 | VML_WRITE32(par, VML_PIPEACONF, cur | VML_PIPE_FORCE_BORDER); | ||
| 929 | (void)VML_READ32(par, VML_PIPEACONF); | ||
| 930 | break; | ||
| 931 | case FB_BLANK_VSYNC_SUSPEND: | ||
| 932 | case FB_BLANK_HSYNC_SUSPEND: | ||
| 933 | if (!vinfo->pipe_disabled) { | ||
| 934 | vmlfb_disable_pipe(vinfo); | ||
| 935 | } | ||
| 936 | break; | ||
| 937 | case FB_BLANK_POWERDOWN: | ||
| 938 | if (!vinfo->pipe_disabled) { | ||
| 939 | vmlfb_disable_pipe(vinfo); | ||
| 940 | } | ||
| 941 | break; | ||
| 942 | default: | ||
| 943 | return -EINVAL; | ||
| 944 | } | ||
| 945 | |||
| 946 | return 0; | ||
| 947 | } | ||
| 948 | |||
| 949 | static int vmlfb_blank(int blank_mode, struct fb_info *info) | ||
| 950 | { | ||
| 951 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
| 952 | int ret; | ||
| 953 | |||
| 954 | mutex_lock(&vml_mutex); | ||
| 955 | vinfo->cur_blank_mode = blank_mode; | ||
| 956 | ret = vmlfb_blank_locked(vinfo); | ||
| 957 | mutex_unlock(&vml_mutex); | ||
| 958 | return ret; | ||
| 959 | } | ||
| 960 | |||
| 961 | static int vmlfb_pan_display(struct fb_var_screeninfo *var, | ||
| 962 | struct fb_info *info) | ||
| 963 | { | ||
| 964 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
| 965 | struct vml_par *par = vinfo->par; | ||
| 966 | |||
| 967 | mutex_lock(&vml_mutex); | ||
| 968 | VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + | ||
| 969 | var->yoffset * vinfo->stride + | ||
| 970 | var->xoffset * vinfo->bytes_per_pixel); | ||
| 971 | (void)VML_READ32(par, VML_DSPCADDR); | ||
| 972 | mutex_unlock(&vml_mutex); | ||
| 973 | |||
| 974 | return 0; | ||
| 975 | } | ||
| 976 | |||
| 977 | static int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
| 978 | u_int transp, struct fb_info *info) | ||
| 979 | { | ||
| 980 | u32 v; | ||
| 981 | |||
| 982 | if (regno >= 16) | ||
| 983 | return -EINVAL; | ||
| 984 | |||
| 985 | if (info->var.grayscale) { | ||
| 986 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | ||
| 987 | } | ||
| 988 | |||
| 989 | if (info->fix.visual != FB_VISUAL_TRUECOLOR) | ||
| 990 | return -EINVAL; | ||
| 991 | |||
| 992 | red = VML_TOHW(red, info->var.red.length); | ||
| 993 | blue = VML_TOHW(blue, info->var.blue.length); | ||
| 994 | green = VML_TOHW(green, info->var.green.length); | ||
| 995 | transp = VML_TOHW(transp, info->var.transp.length); | ||
| 996 | |||
| 997 | v = (red << info->var.red.offset) | | ||
| 998 | (green << info->var.green.offset) | | ||
| 999 | (blue << info->var.blue.offset) | | ||
| 1000 | (transp << info->var.transp.offset); | ||
| 1001 | |||
| 1002 | switch (info->var.bits_per_pixel) { | ||
| 1003 | case 16: | ||
| 1004 | ((u32 *) info->pseudo_palette)[regno] = v; | ||
| 1005 | break; | ||
| 1006 | case 24: | ||
| 1007 | case 32: | ||
| 1008 | ((u32 *) info->pseudo_palette)[regno] = v; | ||
| 1009 | break; | ||
| 1010 | } | ||
| 1011 | return 0; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | static int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) | ||
| 1015 | { | ||
| 1016 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
| 1017 | unsigned long size = vma->vm_end - vma->vm_start; | ||
| 1018 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | ||
| 1019 | int ret; | ||
| 1020 | |||
| 1021 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | ||
| 1022 | return -EINVAL; | ||
| 1023 | if (offset + size > vinfo->vram_contig_size) | ||
| 1024 | return -EINVAL; | ||
| 1025 | ret = vmlfb_vram_offset(vinfo, offset); | ||
| 1026 | if (ret) | ||
| 1027 | return -EINVAL; | ||
| 1028 | offset += vinfo->vram_start; | ||
| 1029 | pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; | ||
| 1030 | pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; | ||
| 1031 | vma->vm_flags |= VM_RESERVED | VM_IO; | ||
| 1032 | if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, | ||
| 1033 | size, vma->vm_page_prot)) | ||
| 1034 | return -EAGAIN; | ||
| 1035 | return 0; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | static int vmlfb_sync(struct fb_info *info) | ||
| 1039 | { | ||
| 1040 | return 0; | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | static int vmlfb_cursor(struct fb_info *info, struct fb_cursor *cursor) | ||
| 1044 | { | ||
| 1045 | return -EINVAL; /* just to force soft_cursor() call */ | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | static struct fb_ops vmlfb_ops = { | ||
| 1049 | .owner = THIS_MODULE, | ||
| 1050 | .fb_open = vmlfb_open, | ||
| 1051 | .fb_release = vmlfb_release, | ||
| 1052 | .fb_check_var = vmlfb_check_var, | ||
| 1053 | .fb_set_par = vmlfb_set_par, | ||
| 1054 | .fb_blank = vmlfb_blank, | ||
| 1055 | .fb_pan_display = vmlfb_pan_display, | ||
| 1056 | .fb_fillrect = cfb_fillrect, | ||
| 1057 | .fb_copyarea = cfb_copyarea, | ||
| 1058 | .fb_imageblit = cfb_imageblit, | ||
| 1059 | .fb_cursor = vmlfb_cursor, | ||
| 1060 | .fb_sync = vmlfb_sync, | ||
| 1061 | .fb_mmap = vmlfb_mmap, | ||
| 1062 | .fb_setcolreg = vmlfb_setcolreg | ||
| 1063 | }; | ||
| 1064 | |||
| 1065 | static struct pci_device_id vml_ids[] = { | ||
| 1066 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, VML_DEVICE_VDC)}, | ||
| 1067 | {0} | ||
| 1068 | }; | ||
| 1069 | |||
| 1070 | static struct pci_driver vmlfb_pci_driver = { | ||
| 1071 | .name = "vmlfb", | ||
| 1072 | .id_table = vml_ids, | ||
| 1073 | .probe = vml_pci_probe, | ||
| 1074 | .remove = __devexit_p(vml_pci_remove) | ||
| 1075 | }; | ||
| 1076 | |||
| 1077 | static void __exit vmlfb_cleanup(void) | ||
| 1078 | { | ||
| 1079 | pci_unregister_driver(&vmlfb_pci_driver); | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | static int __init vmlfb_init(void) | ||
| 1083 | { | ||
| 1084 | |||
| 1085 | #ifndef MODULE | ||
| 1086 | char *option = NULL; | ||
| 1087 | |||
| 1088 | if (fb_get_options(MODULE_NAME, &option)) | ||
| 1089 | return -ENODEV; | ||
| 1090 | #endif | ||
| 1091 | |||
| 1092 | printk(KERN_DEBUG MODULE_NAME ": initializing\n"); | ||
| 1093 | mutex_init(&vml_mutex); | ||
| 1094 | INIT_LIST_HEAD(&global_no_mode); | ||
| 1095 | INIT_LIST_HEAD(&global_has_mode); | ||
| 1096 | |||
| 1097 | return pci_register_driver(&vmlfb_pci_driver); | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | int vmlfb_register_subsys(struct vml_sys *sys) | ||
| 1101 | { | ||
| 1102 | struct vml_info *entry; | ||
| 1103 | struct list_head *list; | ||
| 1104 | u32 save_activate; | ||
| 1105 | |||
| 1106 | mutex_lock(&vml_mutex); | ||
| 1107 | if (subsys != NULL) { | ||
| 1108 | subsys->restore(subsys); | ||
| 1109 | } | ||
| 1110 | subsys = sys; | ||
| 1111 | subsys->save(subsys); | ||
| 1112 | |||
| 1113 | /* | ||
| 1114 | * We need to restart list traversal for each item, since we | ||
| 1115 | * release the list mutex in the loop. | ||
| 1116 | */ | ||
| 1117 | |||
| 1118 | list = global_no_mode.next; | ||
| 1119 | while (list != &global_no_mode) { | ||
| 1120 | list_del_init(list); | ||
| 1121 | entry = list_entry(list, struct vml_info, head); | ||
| 1122 | |||
| 1123 | /* | ||
| 1124 | * First, try the current mode which might not be | ||
| 1125 | * completely validated with respect to the pixel clock. | ||
| 1126 | */ | ||
| 1127 | |||
| 1128 | if (!vmlfb_check_var_locked(&entry->info.var, entry)) { | ||
| 1129 | vmlfb_set_par_locked(entry); | ||
| 1130 | list_add_tail(list, &global_has_mode); | ||
| 1131 | } else { | ||
| 1132 | |||
| 1133 | /* | ||
| 1134 | * Didn't work. Try to find another mode, | ||
| 1135 | * that matches this subsys. | ||
| 1136 | */ | ||
| 1137 | |||
| 1138 | mutex_unlock(&vml_mutex); | ||
| 1139 | save_activate = entry->info.var.activate; | ||
| 1140 | entry->info.var.bits_per_pixel = 16; | ||
| 1141 | vmlfb_set_pref_pixel_format(&entry->info.var); | ||
| 1142 | if (fb_find_mode(&entry->info.var, | ||
| 1143 | &entry->info, | ||
| 1144 | vml_default_mode, NULL, 0, NULL, 16)) { | ||
| 1145 | entry->info.var.activate |= | ||
| 1146 | FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; | ||
| 1147 | fb_set_var(&entry->info, &entry->info.var); | ||
| 1148 | } else { | ||
| 1149 | printk(KERN_ERR MODULE_NAME | ||
| 1150 | ": Sorry. no mode found for this subsys.\n"); | ||
| 1151 | } | ||
| 1152 | entry->info.var.activate = save_activate; | ||
| 1153 | mutex_lock(&vml_mutex); | ||
| 1154 | } | ||
| 1155 | vmlfb_blank_locked(entry); | ||
| 1156 | list = global_no_mode.next; | ||
| 1157 | } | ||
| 1158 | mutex_unlock(&vml_mutex); | ||
| 1159 | |||
| 1160 | printk(KERN_DEBUG MODULE_NAME ": Registered %s subsystem.\n", | ||
| 1161 | subsys->name ? subsys->name : "unknown"); | ||
| 1162 | return 0; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | EXPORT_SYMBOL_GPL(vmlfb_register_subsys); | ||
| 1166 | |||
| 1167 | void vmlfb_unregister_subsys(struct vml_sys *sys) | ||
| 1168 | { | ||
| 1169 | struct vml_info *entry, *next; | ||
| 1170 | |||
| 1171 | mutex_lock(&vml_mutex); | ||
| 1172 | if (subsys != sys) { | ||
| 1173 | mutex_unlock(&vml_mutex); | ||
| 1174 | return; | ||
| 1175 | } | ||
| 1176 | subsys->restore(subsys); | ||
| 1177 | subsys = NULL; | ||
| 1178 | list_for_each_entry_safe(entry, next, &global_has_mode, head) { | ||
| 1179 | printk(KERN_DEBUG MODULE_NAME ": subsys disable pipe\n"); | ||
| 1180 | vmlfb_disable_pipe(entry); | ||
| 1181 | list_del(&entry->head); | ||
| 1182 | list_add_tail(&entry->head, &global_no_mode); | ||
| 1183 | } | ||
| 1184 | mutex_unlock(&vml_mutex); | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | EXPORT_SYMBOL_GPL(vmlfb_unregister_subsys); | ||
| 1188 | |||
| 1189 | module_init(vmlfb_init); | ||
| 1190 | module_exit(vmlfb_cleanup); | ||
| 1191 | |||
| 1192 | MODULE_AUTHOR("Tungsten Graphics"); | ||
| 1193 | MODULE_DESCRIPTION("Initialization of the Vermilion display devices"); | ||
| 1194 | MODULE_VERSION("1.0.0"); | ||
| 1195 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vermilion/vermilion.h b/drivers/video/vermilion/vermilion.h new file mode 100644 index 000000000000..1fc6695a49d2 --- /dev/null +++ b/drivers/video/vermilion/vermilion.h | |||
| @@ -0,0 +1,260 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) Intel Corp. 2007. | ||
| 3 | * All Rights Reserved. | ||
| 4 | * | ||
| 5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
| 6 | * develop this driver. | ||
| 7 | * | ||
| 8 | * This file is part of the Vermilion Range fb driver. | ||
| 9 | * The Vermilion Range fb driver is free software; | ||
| 10 | * you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * The Vermilion Range fb driver is distributed | ||
| 16 | * in the hope that it will be useful, | ||
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | * GNU General Public License for more details. | ||
| 20 | * | ||
| 21 | * You should have received a copy of the GNU General Public License | ||
| 22 | * along with this driver; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 24 | * | ||
| 25 | * Authors: | ||
| 26 | * Thomas Hellström <thomas-at-tungstengraphics-dot-com> | ||
| 27 | */ | ||
| 28 | |||
| 29 | #ifndef _VERMILION_H_ | ||
| 30 | #define _VERMILION_H_ | ||
| 31 | |||
| 32 | #include <linux/kernel.h> | ||
| 33 | #include <linux/version.h> | ||
| 34 | #include <linux/pci.h> | ||
| 35 | #include <asm/atomic.h> | ||
| 36 | #include <linux/mutex.h> | ||
| 37 | |||
| 38 | #define VML_DEVICE_GPU 0x5002 | ||
| 39 | #define VML_DEVICE_VDC 0x5009 | ||
| 40 | |||
| 41 | #define VML_VRAM_AREAS 3 | ||
| 42 | #define VML_MAX_XRES 1024 | ||
| 43 | #define VML_MAX_YRES 768 | ||
| 44 | #define VML_MAX_XRES_VIRTUAL 1040 | ||
| 45 | |||
| 46 | /* | ||
| 47 | * Display controller registers: | ||
| 48 | */ | ||
| 49 | |||
| 50 | /* Display controller 10-bit color representation */ | ||
| 51 | |||
| 52 | #define VML_R_MASK 0x3FF00000 | ||
| 53 | #define VML_R_SHIFT 20 | ||
| 54 | #define VML_G_MASK 0x000FFC00 | ||
| 55 | #define VML_G_SHIFT 10 | ||
| 56 | #define VML_B_MASK 0x000003FF | ||
| 57 | #define VML_B_SHIFT 0 | ||
| 58 | |||
| 59 | /* Graphics plane control */ | ||
| 60 | #define VML_DSPCCNTR 0x00072180 | ||
| 61 | #define VML_GFX_ENABLE 0x80000000 | ||
| 62 | #define VML_GFX_GAMMABYPASS 0x40000000 | ||
| 63 | #define VML_GFX_ARGB1555 0x0C000000 | ||
| 64 | #define VML_GFX_RGB0888 0x18000000 | ||
| 65 | #define VML_GFX_ARGB8888 0x1C000000 | ||
| 66 | #define VML_GFX_ALPHACONST 0x02000000 | ||
| 67 | #define VML_GFX_ALPHAMULT 0x01000000 | ||
| 68 | #define VML_GFX_CONST_ALPHA 0x000000FF | ||
| 69 | |||
| 70 | /* Graphics plane start address. Pixel aligned. */ | ||
| 71 | #define VML_DSPCADDR 0x00072184 | ||
| 72 | |||
| 73 | /* Graphics plane stride register. */ | ||
| 74 | #define VML_DSPCSTRIDE 0x00072188 | ||
| 75 | |||
| 76 | /* Graphics plane position register. */ | ||
| 77 | #define VML_DSPCPOS 0x0007218C | ||
| 78 | #define VML_POS_YMASK 0x0FFF0000 | ||
| 79 | #define VML_POS_YSHIFT 16 | ||
| 80 | #define VML_POS_XMASK 0x00000FFF | ||
| 81 | #define VML_POS_XSHIFT 0 | ||
| 82 | |||
| 83 | /* Graphics plane height and width */ | ||
| 84 | #define VML_DSPCSIZE 0x00072190 | ||
| 85 | #define VML_SIZE_HMASK 0x0FFF0000 | ||
| 86 | #define VML_SIZE_HSHIFT 16 | ||
| 87 | #define VML_SISE_WMASK 0x00000FFF | ||
| 88 | #define VML_SIZE_WSHIFT 0 | ||
| 89 | |||
| 90 | /* Graphics plane gamma correction lookup table registers (129 * 32 bits) */ | ||
| 91 | #define VML_DSPCGAMLUT 0x00072200 | ||
| 92 | |||
| 93 | /* Pixel video output configuration register */ | ||
| 94 | #define VML_PVOCONFIG 0x00061140 | ||
| 95 | #define VML_CONFIG_BASE 0x80000000 | ||
| 96 | #define VML_CONFIG_PIXEL_SWAP 0x04000000 | ||
| 97 | #define VML_CONFIG_DE_INV 0x01000000 | ||
| 98 | #define VML_CONFIG_HREF_INV 0x00400000 | ||
| 99 | #define VML_CONFIG_VREF_INV 0x00100000 | ||
| 100 | #define VML_CONFIG_CLK_INV 0x00040000 | ||
| 101 | #define VML_CONFIG_CLK_DIV2 0x00010000 | ||
| 102 | #define VML_CONFIG_ESTRB_INV 0x00008000 | ||
| 103 | |||
| 104 | /* Pipe A Horizontal total register */ | ||
| 105 | #define VML_HTOTAL_A 0x00060000 | ||
| 106 | #define VML_HTOTAL_MASK 0x1FFF0000 | ||
| 107 | #define VML_HTOTAL_SHIFT 16 | ||
| 108 | #define VML_HTOTAL_VAL 8192 | ||
| 109 | #define VML_HACTIVE_MASK 0x000007FF | ||
| 110 | #define VML_HACTIVE_SHIFT 0 | ||
| 111 | #define VML_HACTIVE_VAL 4096 | ||
| 112 | |||
| 113 | /* Pipe A Horizontal Blank register */ | ||
| 114 | #define VML_HBLANK_A 0x00060004 | ||
| 115 | #define VML_HBLANK_END_MASK 0x1FFF0000 | ||
| 116 | #define VML_HBLANK_END_SHIFT 16 | ||
| 117 | #define VML_HBLANK_END_VAL 8192 | ||
| 118 | #define VML_HBLANK_START_MASK 0x00001FFF | ||
| 119 | #define VML_HBLANK_START_SHIFT 0 | ||
| 120 | #define VML_HBLANK_START_VAL 8192 | ||
| 121 | |||
| 122 | /* Pipe A Horizontal Sync register */ | ||
| 123 | #define VML_HSYNC_A 0x00060008 | ||
| 124 | #define VML_HSYNC_END_MASK 0x1FFF0000 | ||
| 125 | #define VML_HSYNC_END_SHIFT 16 | ||
| 126 | #define VML_HSYNC_END_VAL 8192 | ||
| 127 | #define VML_HSYNC_START_MASK 0x00001FFF | ||
| 128 | #define VML_HSYNC_START_SHIFT 0 | ||
| 129 | #define VML_HSYNC_START_VAL 8192 | ||
| 130 | |||
| 131 | /* Pipe A Vertical total register */ | ||
| 132 | #define VML_VTOTAL_A 0x0006000C | ||
| 133 | #define VML_VTOTAL_MASK 0x1FFF0000 | ||
| 134 | #define VML_VTOTAL_SHIFT 16 | ||
| 135 | #define VML_VTOTAL_VAL 8192 | ||
| 136 | #define VML_VACTIVE_MASK 0x000007FF | ||
| 137 | #define VML_VACTIVE_SHIFT 0 | ||
| 138 | #define VML_VACTIVE_VAL 4096 | ||
| 139 | |||
| 140 | /* Pipe A Vertical Blank register */ | ||
| 141 | #define VML_VBLANK_A 0x00060010 | ||
| 142 | #define VML_VBLANK_END_MASK 0x1FFF0000 | ||
| 143 | #define VML_VBLANK_END_SHIFT 16 | ||
| 144 | #define VML_VBLANK_END_VAL 8192 | ||
| 145 | #define VML_VBLANK_START_MASK 0x00001FFF | ||
| 146 | #define VML_VBLANK_START_SHIFT 0 | ||
| 147 | #define VML_VBLANK_START_VAL 8192 | ||
| 148 | |||
| 149 | /* Pipe A Vertical Sync register */ | ||
| 150 | #define VML_VSYNC_A 0x00060014 | ||
| 151 | #define VML_VSYNC_END_MASK 0x1FFF0000 | ||
| 152 | #define VML_VSYNC_END_SHIFT 16 | ||
| 153 | #define VML_VSYNC_END_VAL 8192 | ||
| 154 | #define VML_VSYNC_START_MASK 0x00001FFF | ||
| 155 | #define VML_VSYNC_START_SHIFT 0 | ||
| 156 | #define VML_VSYNC_START_VAL 8192 | ||
| 157 | |||
| 158 | /* Pipe A Source Image size (minus one - equal to active size) | ||
| 159 | * Programmable while pipe is enabled. | ||
| 160 | */ | ||
| 161 | #define VML_PIPEASRC 0x0006001C | ||
| 162 | #define VML_PIPEASRC_HMASK 0x0FFF0000 | ||
| 163 | #define VML_PIPEASRC_HSHIFT 16 | ||
| 164 | #define VML_PIPEASRC_VMASK 0x00000FFF | ||
| 165 | #define VML_PIPEASRC_VSHIFT 0 | ||
| 166 | |||
| 167 | /* Pipe A Border Color Pattern register (10 bit color) */ | ||
| 168 | #define VML_BCLRPAT_A 0x00060020 | ||
| 169 | |||
| 170 | /* Pipe A Canvas Color register (10 bit color) */ | ||
| 171 | #define VML_CANVSCLR_A 0x00060024 | ||
| 172 | |||
| 173 | /* Pipe A Configuration register */ | ||
| 174 | #define VML_PIPEACONF 0x00070008 | ||
| 175 | #define VML_PIPE_BASE 0x00000000 | ||
| 176 | #define VML_PIPE_ENABLE 0x80000000 | ||
| 177 | #define VML_PIPE_FORCE_BORDER 0x02000000 | ||
| 178 | #define VML_PIPE_PLANES_OFF 0x00080000 | ||
| 179 | #define VML_PIPE_ARGB_OUTPUT_MODE 0x00040000 | ||
| 180 | |||
| 181 | /* Pipe A FIFO setting */ | ||
| 182 | #define VML_DSPARB 0x00070030 | ||
| 183 | #define VML_FIFO_DEFAULT 0x00001D9C | ||
| 184 | |||
| 185 | /* MDVO rcomp status & pads control register */ | ||
| 186 | #define VML_RCOMPSTAT 0x00070048 | ||
| 187 | #define VML_MDVO_VDC_I_RCOMP 0x80000000 | ||
| 188 | #define VML_MDVO_POWERSAVE_OFF 0x00000008 | ||
| 189 | #define VML_MDVO_PAD_ENABLE 0x00000004 | ||
| 190 | #define VML_MDVO_PULLDOWN_ENABLE 0x00000001 | ||
| 191 | |||
| 192 | struct vml_par { | ||
| 193 | struct pci_dev *vdc; | ||
| 194 | u64 vdc_mem_base; | ||
| 195 | u64 vdc_mem_size; | ||
| 196 | char __iomem *vdc_mem; | ||
| 197 | |||
| 198 | struct pci_dev *gpu; | ||
| 199 | u64 gpu_mem_base; | ||
| 200 | u64 gpu_mem_size; | ||
| 201 | char __iomem *gpu_mem; | ||
| 202 | |||
| 203 | atomic_t refcount; | ||
| 204 | }; | ||
| 205 | |||
| 206 | struct vram_area { | ||
| 207 | unsigned long logical; | ||
| 208 | unsigned long phys; | ||
| 209 | unsigned long size; | ||
| 210 | unsigned order; | ||
| 211 | }; | ||
| 212 | |||
| 213 | struct vml_info { | ||
| 214 | struct fb_info info; | ||
| 215 | struct vml_par *par; | ||
| 216 | struct list_head head; | ||
| 217 | struct vram_area vram[VML_VRAM_AREAS]; | ||
| 218 | u64 vram_start; | ||
| 219 | u64 vram_contig_size; | ||
| 220 | u32 num_areas; | ||
| 221 | void __iomem *vram_logical; | ||
| 222 | u32 pseudo_palette[16]; | ||
| 223 | u32 stride; | ||
| 224 | u32 bytes_per_pixel; | ||
| 225 | atomic_t vmas; | ||
| 226 | int cur_blank_mode; | ||
| 227 | int pipe_disabled; | ||
| 228 | }; | ||
| 229 | |||
| 230 | /* | ||
| 231 | * Subsystem | ||
| 232 | */ | ||
| 233 | |||
| 234 | struct vml_sys { | ||
| 235 | char *name; | ||
| 236 | |||
| 237 | /* | ||
| 238 | * Save / Restore; | ||
| 239 | */ | ||
| 240 | |||
| 241 | int (*save) (struct vml_sys * sys); | ||
| 242 | int (*restore) (struct vml_sys * sys); | ||
| 243 | |||
| 244 | /* | ||
| 245 | * PLL programming; | ||
| 246 | */ | ||
| 247 | |||
| 248 | int (*set_clock) (struct vml_sys * sys, int clock); | ||
| 249 | int (*nearest_clock) (const struct vml_sys * sys, int clock); | ||
| 250 | }; | ||
| 251 | |||
| 252 | extern int vmlfb_register_subsys(struct vml_sys *sys); | ||
| 253 | extern void vmlfb_unregister_subsys(struct vml_sys *sys); | ||
| 254 | |||
| 255 | #define VML_READ32(_par, _offset) \ | ||
| 256 | (ioread32((_par)->vdc_mem + (_offset))) | ||
| 257 | #define VML_WRITE32(_par, _offset, _value) \ | ||
| 258 | iowrite32(_value, (_par)->vdc_mem + (_offset)) | ||
| 259 | |||
| 260 | #endif | ||
