Add Intel Vermilion Range support Intel funded Tungsten Graphics for this work. Signed-off-by: Alan Hourihane diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e4f0dd0..24d9bf2 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -806,6 +806,22 @@ config FB_I810_I2C select FB_DDC help +config FB_VERMILION + tristate "Vermilion support" + depends on FB && PCI && X86 + select FB_MODE_HELPERS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This driver supports the Vermilion Range chipset + +config FB_CARILLO_RANCH + tristate "Carillo Ranch (Vermilion) support" + depends on FB_VERMILION && FB && PCI && X86 + help + This driver supports the Carillo Ranch (Vermilion Range) chipset + config FB_INTEL tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)" depends on FB && EXPERIMENTAL && PCI && X86 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 760305c..102ad27 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_FB_IMSTT) += imsttfb.o obj-$(CONFIG_FB_FM2) += fm2fb.o obj-$(CONFIG_FB_CYBLA) += cyblafb.o obj-$(CONFIG_FB_TRIDENT) += tridentfb.o +obj-$(CONFIG_FB_VERMILION) += vermilion/ obj-$(CONFIG_FB_S3) += s3fb.o vgastate.o obj-$(CONFIG_FB_STI) += stifb.o obj-$(CONFIG_FB_FFB) += ffb.o sbuslib.o diff --git a/drivers/video/vermilion/Makefile b/drivers/video/vermilion/Makefile new file mode 100644 index 0000000..6331357 --- /dev/null +++ b/drivers/video/vermilion/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_FB_VERMILION) += vmlfb.o +obj-$(CONFIG_FB_CARILLO_RANCH) += crvml.o + +vmlfb-objs := vermilion.o +crvml-objs := carillo_ranch.o diff --git a/drivers/video/vermilion/carillo_ranch.c b/drivers/video/vermilion/carillo_ranch.c new file mode 100644 index 0000000..1541f59 --- /dev/null +++ b/drivers/video/vermilion/carillo_ranch.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) Intel Corp. 2007. + * All Rights Reserved. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + * This file is part of the Carillo Ranch video subsystem driver. + * The Carillo Ranch video subsystem driver 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. + * + * The Carillo Ranch video subsystem driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Thomas Hellstrom + */ + +#include +#include +#include +#include +#include +#include "vermilion.h" + +/* The LVDS- and panel power controls sits on the + * GPIO port of the ISA bridge. + */ + +#define CRVML_DEVICE_LPC 0x27B8 +#define CRVML_REG_GPIOBAR 0x48 +#define CRVML_REG_GPIOEN 0x4C +#define CRVML_GPIOEN_BIT (1 << 4) +#define CRVML_PANEL_PORT 0x38 +#define CRVML_LVDS_ON 0x00000001 +#define CRVML_PANEL_ON 0x00000002 +#define CRVML_BACKLIGHT_OFF 0x00000004 + +/* The PLL Clock register sits on Host bridge */ +#define CRVML_DEVICE_MCH 0x5001 +#define CRVML_REG_MCHBAR 0x44 +#define CRVML_REG_MCHEN 0x54 +#define CRVML_MCHEN_BIT (1 << 28) +#define CRVML_MCHMAP_SIZE 4096 +#define CRVML_REG_CLOCK 0xc3c +#define CRVML_CLOCK_SHIFT 8 +#define CRVML_CLOCK_MASK 0x00000f00 + +#define MODULE_NAME "crvml" + +struct cr_sys { + struct vml_sys sys; + struct pci_dev *mch_dev; + struct pci_dev *lpc_dev; + __u32 mch_bar; + __u8 *mch_regs_base; + __u32 gpio_bar; + __u32 saved_panel_state; + __u32 saved_clock; +}; + +static struct vml_sys *my_sys = NULL; + +static const unsigned crvml_clocks[] = { + 6750, + 13500, + 27000, + 29700, + 37125, + 54000, + 59400, + 74250, + 120000 + /* + * There are more clocks, but they are disabled on the CR board. + */ +}; + +static const __u32 crvml_clock_bits[] = { + 0x0a, + 0x09, + 0x08, + 0x07, + 0x06, + 0x05, + 0x04, + 0x03, + 0x0b +}; + +static const unsigned crvml_num_clocks = sizeof(crvml_clocks) / sizeof(__u32); + +static int crvml_sysinit(struct cr_sys *crsys) +{ + __u32 dev_en; + __u8 dev_en8; + + if (!crsys) + return -ENODEV; + + crsys->mch_dev = pci_get_device(PCI_VENDOR_ID_INTEL, + CRVML_DEVICE_MCH, NULL); + if (!crsys->mch_dev) { + printk(KERN_ERR MODULE_NAME + ": Could not find Carillo Ranch MCH device.\n"); + return -ENODEV; + } + + pci_read_config_dword(crsys->mch_dev, CRVML_REG_MCHEN, &dev_en); + if (!(dev_en & CRVML_MCHEN_BIT)) { + printk(KERN_ERR MODULE_NAME + ": Carillo Ranch MCH device was not enabled.\n"); + goto out_err_0; + } + + pci_read_config_dword(crsys->mch_dev, CRVML_REG_MCHBAR, + &crsys->mch_bar); + crsys->mch_regs_base = + ioremap_nocache(crsys->mch_bar, CRVML_MCHMAP_SIZE); + if (!crsys->mch_regs_base) { + printk(KERN_ERR MODULE_NAME + ": Carillo Ranch MCH device was not enabled.\n"); + goto out_err_0; + } + + /* + * Get the gpio bar. + */ + + crsys->lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, + CRVML_DEVICE_LPC, NULL); + if (!crsys->lpc_dev) { + printk(KERN_ERR MODULE_NAME + ": Could not find Carillo Ranch LPC device.\n"); + goto out_err_1; + } + + pci_read_config_byte(crsys->lpc_dev, CRVML_REG_GPIOEN, &dev_en8); + if (!(dev_en8 & CRVML_GPIOEN_BIT)) { + printk(KERN_ERR MODULE_NAME + ": Carillo Ranch GPIO device was not enabled.\n"); + goto out_err_2; + } + + pci_read_config_dword(crsys->lpc_dev, CRVML_REG_GPIOBAR, + &crsys->gpio_bar); + crsys->gpio_bar &= ~0x3F; + + return 0; + + out_err_2: + pci_dev_put(crsys->lpc_dev); + out_err_1: + iounmap(crsys->mch_regs_base); + out_err_0: + pci_dev_put(crsys->mch_dev); + return -ENODEV; +} + +static void crvml_sys_destroy(struct vml_sys *sys) +{ + struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + + pci_dev_put(crsys->lpc_dev); + iounmap(crsys->mch_regs_base); + pci_dev_put(crsys->mch_dev); + + if (sys == my_sys) + my_sys = NULL; + + kfree(crsys); +} + +static void crvml_panel_on(const struct vml_sys *sys) +{ + const struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + __u32 addr = crsys->gpio_bar + CRVML_PANEL_PORT; + __u32 cur = inl(addr); + + if (!(cur & CRVML_PANEL_ON)) { + /* Make sure LVDS controller is down. */ + if (cur & 0x00000001) { + cur &= ~CRVML_LVDS_ON; + outl(cur, addr); + } + /* Power up Panel */ + schedule_timeout(HZ / 10); + cur |= CRVML_PANEL_ON; + outl(cur, addr); + } + + /* Power up LVDS controller */ + + if (!(cur & CRVML_LVDS_ON)) { + schedule_timeout(HZ / 10); + outl(cur | CRVML_LVDS_ON, addr); + } +} + +static void crvml_panel_off(const struct vml_sys *sys) +{ + const struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + + __u32 addr = crsys->gpio_bar + CRVML_PANEL_PORT; + __u32 cur = inl(addr); + + /* Power down LVDS controller first to avoid high currents */ + if (cur & CRVML_LVDS_ON) { + cur &= ~CRVML_LVDS_ON; + outl(cur, addr); + } + if (cur & CRVML_PANEL_ON) { + schedule_timeout(HZ / 10); + outl(cur & ~CRVML_PANEL_ON, addr); + } +} + +static void crvml_backlight_on(const struct vml_sys *sys) +{ + const struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + __u32 addr = crsys->gpio_bar + CRVML_PANEL_PORT; + __u32 cur = inl(addr); + + if (cur & CRVML_BACKLIGHT_OFF) { + cur &= ~CRVML_BACKLIGHT_OFF; + outl(cur, addr); + } +} + +static void crvml_backlight_off(const struct vml_sys *sys) +{ + const struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + __u32 addr = crsys->gpio_bar + CRVML_PANEL_PORT; + __u32 cur = inl(addr); + + if (!(cur & CRVML_BACKLIGHT_OFF)) { + cur |= CRVML_BACKLIGHT_OFF; + outl(cur, addr); + } +} + +static int crvml_sys_restore(struct vml_sys *sys) +{ + struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + __u32 *clock_reg = (__u32 *) (crsys->mch_regs_base + CRVML_REG_CLOCK); + __u32 cur = crsys->saved_panel_state; + + if (cur & CRVML_BACKLIGHT_OFF) { + crvml_backlight_off(sys); + } else { + crvml_backlight_on(sys); + } + + if (cur & CRVML_PANEL_ON) { + crvml_panel_on(sys); + } else { + crvml_panel_off(sys); + if (cur & CRVML_LVDS_ON) { + ; + /* Will not power up LVDS controller while panel is off */ + } + } + iowrite32(crsys->saved_clock, clock_reg); + ioread32(clock_reg); + + return 0; +} + +static int crvml_sys_save(struct vml_sys *sys) +{ + struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + __u32 *clock_reg = (__u32 *) (crsys->mch_regs_base + CRVML_REG_CLOCK); + + crsys->saved_panel_state = inl(crsys->gpio_bar + CRVML_PANEL_PORT); + crsys->saved_clock = ioread32(clock_reg); + + return 0; +} + +static int crvml_nearest_index(const struct vml_sys *sys, int clock) +{ + + int i; + int cur_index; + int cur_diff; + int diff; + + cur_index = 0; + cur_diff = clock - crvml_clocks[0]; + cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; + for (i = 1; i < crvml_num_clocks; ++i) { + diff = clock - crvml_clocks[i]; + diff = (diff < 0) ? -diff : diff; + if (diff < cur_diff) { + cur_index = i; + cur_diff = diff; + } + } + return cur_index; +} + +static int crvml_nearest_clock(const struct vml_sys *sys, int clock) +{ + return crvml_clocks[crvml_nearest_index(sys, clock)]; +} + +static int crvml_set_clock(struct vml_sys *sys, int clock) +{ + struct cr_sys *crsys = container_of(sys, struct cr_sys, sys); + __u32 *clock_reg = (__u32 *) (crsys->mch_regs_base + CRVML_REG_CLOCK); + int index; + __u32 clock_val; + + index = crvml_nearest_index(sys, clock); + + if (crvml_clocks[index] != clock) + return -EINVAL; + + clock_val = ioread32(clock_reg) & ~CRVML_CLOCK_MASK; + clock_val = crvml_clock_bits[index] << CRVML_CLOCK_SHIFT; + iowrite32(clock_val, clock_reg); + ioread32(clock_reg); + + return 0; +} + +static void crvml_subsys(const struct vml_sys *sys, const char **name) +{ + *name = "Carillo Ranch"; +} + +static int crvml_false(const struct vml_sys *sys) +{ + return 0; +} + +static int crvml_true(const struct vml_sys *sys) +{ + return 1; +} + +static int __init crvml_init(void) +{ + int err = 0; + struct vml_sys *sys; + struct cr_sys *crsys; + + crsys = (struct cr_sys *)kmalloc(sizeof(*crsys), GFP_KERNEL); + + if (!crsys) + return -ENOMEM; + + sys = &crsys->sys; + err = crvml_sysinit(crsys); + if (err) { + kfree(crsys); + return err; + } + + sys->destroy = crvml_sys_destroy; + sys->subsys = crvml_subsys; + sys->save = crvml_sys_save; + sys->restore = crvml_sys_restore; + sys->prog_clock = crvml_false; + sys->set_clock = crvml_set_clock; + sys->has_panel = crvml_true; + sys->panel_on = crvml_panel_on; + sys->panel_off = crvml_panel_off; + sys->backlight_on = crvml_backlight_on; + sys->backlight_off = crvml_backlight_off; + sys->nearest_clock = crvml_nearest_clock; + + err = vmlfb_register_subsys(sys); + if (err) { + crvml_sys_destroy(sys); + return err; + } + my_sys = sys; + return 0; +} + +static void __exit crvml_exit(void) +{ + if (my_sys) { + vmlfb_unregister_subsys(my_sys); + crvml_sys_destroy(my_sys); + } +} + +module_init(crvml_init); +module_exit(crvml_exit); + +MODULE_AUTHOR("Tungsten Graphics Inc."); +MODULE_DESCRIPTION("Carillo Ranch Video Subsystem Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/vermilion/vermilion.c b/drivers/video/vermilion/vermilion.c new file mode 100644 index 0000000..d66a848 --- /dev/null +++ b/drivers/video/vermilion/vermilion.c @@ -0,0 +1,1495 @@ +/* + * Copyright (c) Intel Corp. 2007. + * All Rights Reserved. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + * This file is part of the Vermilion Range fb driver. + * The Vermilion Range fb driver 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. + * + * The Vermilion Range fb driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Thomas Hellström + * Michel Dänzer + * Alan Hourihane + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vermilion.h" +#include "vermilion_kernel.h" + +#define MODULE_NAME "vmlfb" + +#if !defined(CONFIG_X86) +#warning The vermilion fb driver has not been adequately ported to this architecture. +#endif + +#if (defined(CONFIG_X86) && defined(X86_FEATURE_PAT) && defined(X86_FEATURE_MSR)) + +/* + * Use the i386 Page Attribute Table for write-combining. + * We can't include this code in the kernel submission, since some of it + * belongs at kernel startup. + */ + +#define USE_PAT_WC +#else +#undef USE_PAT_WC +#endif + +#ifdef USE_PAT_WC +#ifndef MSR_IA32_CR_PAT +#define MSR_IA32_CR_PAT 0x0277 +#endif + +#ifndef _PAGE_PAT +#define _PAGE_PAT 0x080 /* Note that this is the same value as _PAGE_PROTNONE */ +#endif + +static int vml_has_pat = 0; + +#endif /* USE_PAT_WC */ + +#ifndef FB_BLANK_UNBLANK +#define FB_BLANK_UNBLANK VESA_NO_BLANKING +#endif +#ifndef FB_BLANK_NORMAL +#define FB_BLANK_NORMAL VESA_NO_BLANKING + 1 +#endif +#ifndef FB_BLANK_VSYNC_SUSPEND +#define FB_BLANK_VSYNC_SUSPEND VESA_VSYNC_SUSPEND + 1 +#endif +#ifndef FB_BLANK_HSYNC_SUSPEND +#define FB_BLANK_HSYNC_SUSPEND VESA_HSYNC_SUSPEND + 1 +#endif +#ifndef FB_BLANK_POWERDOWN +#define FB_BLANK_POWERDOWN VESA_POWERDOWN + 1 +#endif + +#define VML_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) +#define VML_ALIGN_TO(_val, _align) (((_val) + ((_align) - 1)) & ~((_align) - 1)) +#define VML_PCI_TAG(_b, _s, _f) ({vml_pci_tag __tmp = {.bus = (_b), \ + .slot = (_s), \ + .function = (_f)}; \ + __tmp;}) + +struct vml_info_entry { + struct list_head head; + struct vml_info *info; +}; + +static struct mutex global_mutex; +static struct list_head global_gpu_list; +static struct list_head global_no_mode; +static struct list_head global_has_mode; +static struct fb_ops vmlfb_ops; +static struct vml_sys *subsys = NULL; +static char *vml_default_mode = "1024x768@60"; +static struct fb_videomode defaultmode = { + NULL, 60, 1024, 768, 12896, 144, 24, 29, 3, 136, 6, + 0, FB_VMODE_NONINTERLACED +}; + +static __u32 vml_mem_requested = (10 * 1024 * 1024); +static __u32 vml_mem_contig = (4 * 1024 * 1024); +static __u32 vml_mem_min = (4 * 1024 * 1024); + +static __u32 vml_clocks[] = { + 6750, + 13500, + 27000, + 29700, + 37125, + 54000, + 59400, + 74250, + 120000, + 148500 +}; + +static __u32 vml_num_clocks = sizeof(vml_clocks) / sizeof(__u32); + +static void vmlfb_vma_open(struct vm_area_struct *vma); +static void vmlfb_vma_close(struct vm_area_struct *vma); +static struct page *vmlfb_nopage(struct vm_area_struct *vma, + unsigned long address, int *type); + +static struct vm_operations_struct vmlfb_vm_ops = { + .open = vmlfb_vma_open, + .close = vmlfb_vma_close, + .nopage = vmlfb_nopage +}; + +#ifdef USE_PAT_WC +static void vmlfb_pat_ipi_handler(void *notused) +{ + __u32 v1, v2; + + rdmsr(MSR_IA32_CR_PAT, v1, v2); + v2 &= 0xFFFFFFF8; + v2 |= 0x00000001; + wbinvd(); + wrmsr(MSR_IA32_CR_PAT, v1, v2); + __flush_tlb_all(); +} + +/* + * Set i386 PAT entry PAT4 to Write-combining memory type on all processors. + */ + +static int vmlfb_init_pat(void) +{ + + if (!boot_cpu_has(X86_FEATURE_PAT)) { + printk(KERN_DEBUG MODULE_NAME ": PAT is not supported\n"); + return 0; + } + if (on_each_cpu(vmlfb_pat_ipi_handler, NULL, 1, 1) != 0) + printk(KERN_ERR MODULE_NAME + ": Timed out setting up CPU PAT.\n"); + + return 1; +} +#endif + +/* + * Allocate a contiguous vram area and make its linear kernel map + * uncached. + */ + +static int vmlfb_alloc_vram_area(struct vram_area *va, unsigned max_order, + unsigned min_order) +{ + u32 flags; + unsigned long i; + pgprot_t wc_pageprot; + + wc_pageprot = PAGE_KERNEL_NOCACHE; +#ifdef USE_PAT_WC + if (vml_has_pat) { + wc_pageprot = PAGE_KERNEL; + pgprot_val(wc_pageprot) |= _PAGE_PAT; + } +#endif + max_order++; + do { + /* + * Really try hard to get the needed memory. + * We need memory below the first 32MB, so we + * add the __GFP_DMA flag that guarantees that we are + * below the first 16MB. + */ + + flags = __GFP_DMA | __GFP_HIGH; + va->logical = __get_free_pages(flags, --max_order); + } while (va->logical == 0 && max_order > min_order); + + if (!va->logical) + return -ENOMEM; + + va->phys = virt_to_phys((void *)va->logical); + va->size = PAGE_SIZE << max_order; + va->order = max_order; + + /* + * It seems like __get_free_pages only ups the usage count + * of the first page. This doesn't work with nopage mapping, so + * up the usage count once more. + */ + + memset((void *)va->logical, 0x00, va->size); + for (i = va->logical; i < va->logical + va->size; i += PAGE_SIZE) { + get_page(virt_to_page(i)); + } + + /* + * Change caching policy of the linear kernel map to avoid + * mapping type conflicts with user-space mappings. + * The first global_flush_tlb() is really only there to do a global + * wbinvd(). + */ + +#if defined(CONFIG_X86) + global_flush_tlb(); + change_page_attr(virt_to_page(va->logical), va->size >> PAGE_SHIFT, + wc_pageprot); + global_flush_tlb(); +#endif + + printk(KERN_DEBUG MODULE_NAME + ": Allocated %ld bytes vram area at 0x%08lx\n", + va->size, va->phys); + + return 0; +} + +/* + * Free a contiguous vram area and reset its linear kernel map + * mapping type. + */ + +static void vmlfb_free_vram_area(struct vram_area *va) +{ + unsigned long j; + + if (va->logical) { + + /* + * Reset the linear kernel map caching policy. + */ + +#if defined(CONFIG_X86) + change_page_attr(virt_to_page(va->logical), + va->size >> PAGE_SHIFT, PAGE_KERNEL); + global_flush_tlb(); +#endif + + /* + * Decrease the usage count on the pages we've used + * to compensate for upping when allocating. + */ + + for (j = va->logical; j < va->logical + va->size; + j += PAGE_SIZE) { + (void)put_page_testzero(virt_to_page(j)); + } + + printk(KERN_DEBUG MODULE_NAME + ": Freeing %ld bytes vram area at 0x%08lx\n", + va->size, va->phys); + free_pages(va->logical, va->order); + + va->logical = 0; + } +} + +/* + * Free allocated vram. + */ + +static void vmlfb_free_vram(struct vml_info *vinfo) +{ + int i; + + for (i = 0; i < vinfo->num_areas; ++i) { + vmlfb_free_vram_area(&vinfo->vram[i]); + } + vinfo->num_areas = 0; +} + +/* + * Allocate vram. Currently we try to allocate contiguous areas from the + * __GFP_DMA zone and puzzle them together. A better approach would be to + * allocate one contiguous area for scanout and use one-page allocations for + * offscreen areas. This requires user-space and GPU virtual mappings. + */ + +static int vmlfb_alloc_vram(struct vml_info *vinfo, + size_t requested, + size_t min_total, size_t min_contig) +{ + int i, j; + int order; + int contiguous; + int err; + struct vram_area *va; + struct vram_area *va2; + + vinfo->num_areas = 0; + for (i = 0; i < VML_VRAM_AREAS; ++i) { + va = &vinfo->vram[i]; + order = 0; + + while (requested > (PAGE_SIZE << order) && order < MAX_ORDER) + order++; + + err = vmlfb_alloc_vram_area(va, order, 0); + + if (err) + break; + + if (i == 0) { + vinfo->vram_start = va->phys; + vinfo->vram_logical = (__u8 *) va->logical; + vinfo->vram_contig_size = va->size; + vinfo->num_areas = 1; + } else { + contiguous = 0; + + for (j = 0; j < i; ++j) { + va2 = &vinfo->vram[j]; + if (va->phys + va->size == va2->phys || + va2->phys + va2->size == va->phys) { + contiguous = 1; + break; + } + } + + if (contiguous) { + vinfo->num_areas++; + if (va->phys < vinfo->vram_start) { + vinfo->vram_start = va->phys; + vinfo->vram_logical = + (__u8 *) va->logical; + } + vinfo->vram_contig_size += va->size; + } else { + vmlfb_free_vram_area(va); + break; + } + } + + if (requested < va->size) + break; + else + requested -= va->size; + } + + if (vinfo->vram_contig_size > min_total && + vinfo->vram_contig_size > min_contig) { + + printk(KERN_DEBUG MODULE_NAME + ": Contiguous vram: %ld bytes at physical 0x%08lx.\n", + (unsigned long)vinfo->vram_contig_size, + (unsigned long)vinfo->vram_start); + + return 0; + } + + printk(KERN_ERR MODULE_NAME + ": Could not allocate requested minimal amount of vram.\n"); + + vmlfb_free_vram(vinfo); + + return -ENOMEM; +} + +/* + * Find a GPU to use with our display controller. + */ + +static int vmlfb_get_gpu(struct vml_par *par) +{ + struct list_head *head = par->gpu_list; + struct list_head *list; + struct pci_dev *gpu_dev = NULL; + struct gpu_entry *entry; + int found; + int rc; + + mutex_lock(par->mutex); + + while ((gpu_dev = pci_get_device(PCI_VENDOR_ID_INTEL, + VML_DEVICE_GPU, gpu_dev))) { + + found = 0; + list_for_each(list, head) { + entry = list_entry(list, struct gpu_entry, head); + found = + ((entry->dev->bus->number == gpu_dev->bus->number) + && (entry->dev->devfn == gpu_dev->devfn)); + + if (found) + break; + } + if (!found) + break; + + pci_dev_put(gpu_dev); + } + + if (!gpu_dev) { + mutex_unlock(par->mutex); + return -ENODEV; + } + + par->gpu.dev = gpu_dev; + list_add_tail(&par->gpu.head, par->gpu_list); + mutex_unlock(par->mutex); + rc = pci_enable_device(gpu_dev); + if (rc < 0) + return -ENODEV; + + return 0; +} + +/* + * Find a contiguous vram area that contains a given offset from vram start. + */ +static int vmlfb_vram_offset(struct vml_info *vinfo, unsigned long offset, + unsigned *area, unsigned long *aoffset) +{ + unsigned i; + + for (i = 0; i < vinfo->num_areas; ++i) { + *aoffset = offset - (vinfo->vram[i].phys - vinfo->vram_start); + + if (*aoffset < vinfo->vram[i].size) { + *area = i; + return 0; + } + } + + return -EINVAL; +} + +/* + * User space mapping of video RAM. + */ + +static struct page *vmlfb_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + struct vml_info *vinfo = vma->vm_private_data; + struct vml_par *par = vinfo->par; + struct page *page = NOPAGE_SIGBUS; + int ret; + unsigned long offset; + unsigned long aoffset; + unsigned area; + unsigned long ptr; + + mutex_lock(par->mutex); + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + ret = vmlfb_vram_offset(vinfo, offset, &area, &aoffset); + if (ret) + goto out; + + ptr = (vinfo->vram[area].logical + aoffset); + page = virt_to_page((void *)(ptr)); + + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + + out: + mutex_unlock(par->mutex); + return page; + +} + +/* + * Register a VMA and make sure the mapping is either write-combined + * (if supported by the CPU) or uncached. + */ + +static void vmlfb_vma_open(struct vm_area_struct *vma) +{ + struct vml_info *vinfo = vma->vm_private_data; + +#ifdef USE_PAT_WC + if (vml_has_pat) { + pgprot_val(vma->vm_page_prot) |= _PAGE_PAT; + } else { + /* + * Uncached mapping. + */ + + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; + } +#elif defined(CONFIG_X86) + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; +#endif + + atomic_inc(&vinfo->vmas); +} + +/* + * Unregister a VMA. + */ + +static void vmlfb_vma_close(struct vm_area_struct *vma) +{ + struct vml_info *vinfo = vma->vm_private_data; + atomic_dec(&vinfo->vmas); +} + +/* + * Remap the MMIO register spaces of the VDC and the GPU. + */ + +static int vmlfb_enable_mmio(struct vml_par *par) +{ + int err; + + par->vdc_mem_base = pci_resource_start(par->vdc, 0); + par->vdc_mem_size = pci_resource_len(par->vdc, 0); + if (!request_mem_region(par->vdc_mem_base, par->vdc_mem_size, "vmlfb")) { + printk(KERN_ERR MODULE_NAME + ": Could not claim display controller MMIO.\n"); + return -EBUSY; + } + par->vdc_mem = ioremap_nocache(par->vdc_mem_base, par->vdc_mem_size); + if (par->vdc_mem == NULL) { + printk(KERN_ERR MODULE_NAME + ": Could not map display controller MMIO.\n"); + err = -ENOMEM; + goto out_err_0; + } + + par->gpu_mem_base = pci_resource_start(par->gpu.dev, 0); + par->gpu_mem_size = pci_resource_len(par->gpu.dev, 0); + if (!request_mem_region(par->gpu_mem_base, par->gpu_mem_size, "vmlfb")) { + printk(KERN_ERR MODULE_NAME ": Could not claim GPU MMIO.\n"); + err = -EBUSY; + goto out_err_1; + } + par->gpu_mem = ioremap_nocache(par->gpu_mem_base, par->gpu_mem_size); + if (par->gpu_mem == NULL) { + printk(KERN_ERR MODULE_NAME ": Could not map GPU MMIO.\n"); + err = -ENOMEM; + goto out_err_2; + } + + return 0; + + out_err_2: + release_mem_region(par->gpu_mem_base, par->gpu_mem_size); + out_err_1: + iounmap(par->vdc_mem); + out_err_0: + release_mem_region(par->vdc_mem_base, par->vdc_mem_size); + return err; +} + +/* + * Unmap the VDC and GPU register spaces. + */ + +static void vmlfb_disable_mmio(struct vml_par *par) +{ + iounmap(par->gpu_mem); + release_mem_region(par->gpu_mem_base, par->gpu_mem_size); + iounmap(par->vdc_mem); + release_mem_region(par->vdc_mem_base, par->vdc_mem_size); +} + +/* + * Release and uninit the VDC and GPU. + */ + +static void vmlfb_release_devices(struct vml_par *par) +{ + if (atomic_dec_and_test(&par->refcount)) { + pci_set_drvdata(par->vdc, NULL); + list_del(&par->gpu.head); + pci_disable_device(par->gpu.dev); + pci_dev_put(par->gpu.dev); + pci_disable_device(par->vdc); + } +} + +/* + * Free up allocated resources for a device. + */ + +static void __devexit vml_pci_remove(struct pci_dev *dev) +{ + struct fb_info *info; + struct vml_info *vinfo; + struct vml_par *par; + + info = pci_get_drvdata(dev); + if (info) { + vinfo = container_of(info, struct vml_info, info); + par = vinfo->par; + mutex_lock(&global_mutex); + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + vmlfb_free_vram(vinfo); + vmlfb_disable_mmio(par); + vmlfb_release_devices(par); + kfree(vinfo); + kfree(par); + mutex_unlock(&global_mutex); + } +} + +static void vmlfb_set_pref_pixel_format(struct fb_var_screeninfo *var) +{ + switch (var->bits_per_pixel) { + case 16: + var->blue.offset = 0; + var->blue.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->red.offset = 10; + var->red.length = 5; + var->transp.offset = 15; + var->transp.length = 1; + break; + case 32: + var->blue.offset = 0; + var->blue.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->red.offset = 16; + var->red.length = 8; + var->transp.offset = 24; + var->transp.length = 0; + break; + default: + break; + } + + var->blue.msb_right = var->green.msb_right = + var->red.msb_right = var->transp.msb_right = 0; +} + +/* + * Device initialization. + * We initialize one vml_par struct per device and one vml_info + * struct per pipe. Currently we have only one pipe. + */ + +static int __devinit vml_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct vml_info *vinfo; + struct fb_info *info; + struct vml_par *par; + int err = 0; + + par = kmalloc(sizeof(*par), GFP_KERNEL); + if (par == NULL) + return -ENOMEM; + + vinfo = kmalloc(sizeof(*vinfo), GFP_KERNEL); + if (vinfo == NULL) { + err = -ENOMEM; + goto out_err_0; + } + + memset(vinfo, 0, sizeof(*vinfo)); + memset(par, 0, sizeof(*par)); + + vinfo->par = par; + vinfo->pipe = 0; + par->mutex = &global_mutex; + INIT_LIST_HEAD(&par->gpu.head); + par->gpu_list = &global_gpu_list; + par->vdc = dev; + atomic_set(&par->refcount, 1); + + switch (id->device) { + case VML_DEVICE_VDC: + if ((err = vmlfb_get_gpu(par))) + goto out_err_1; + if ((err = pci_enable_device(dev))) + goto out_err_1; + pci_set_drvdata(dev, &vinfo->info); + break; + default: + err = -ENODEV; + goto out_err_1; + break; + } + + info = &vinfo->info; + info->flags = FBINFO_PARTIAL_PAN_OK; + + err = vmlfb_enable_mmio(par); + if (err) + goto out_err_2; + + err = vmlfb_alloc_vram(vinfo, vml_mem_requested, + vml_mem_contig, vml_mem_min); + if (err) + goto out_err_3; + + strcpy(info->fix.id, "Vermilion Range"); + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + info->fix.smem_start = vinfo->vram_start; + info->fix.smem_len = vinfo->vram_contig_size; + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.ypanstep = 1; + info->fix.xpanstep = 1; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + info->screen_base = vinfo->vram_logical; + info->pseudo_palette = vinfo->pseudo_palette; + info->par = par; + info->fbops = &vmlfb_ops; + + INIT_LIST_HEAD(&vinfo->head); + vinfo->pipe_disabled = 1; + vinfo->cur_blank_mode = FB_BLANK_UNBLANK; + + info->var.grayscale = 0; + info->var.bits_per_pixel = 16; + vmlfb_set_pref_pixel_format(&info->var); + + if (!fb_find_mode + (&info->var, info, vml_default_mode, NULL, 0, &defaultmode, 16)) { + printk(KERN_ERR MODULE_NAME ": Could not find initial mode\n"); + } + + if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { + err = -ENOMEM; + goto out_err_4; + } + + err = register_framebuffer(info); + if (err) { + printk(KERN_ERR MODULE_NAME ": Register framebuffer error.\n"); + goto out_err_5; + } + + printk("Initialized vmlfb\n"); + + return 0; + + out_err_5: + fb_dealloc_cmap(&info->cmap); + out_err_4: + vmlfb_free_vram(vinfo); + out_err_3: + vmlfb_disable_mmio(par); + out_err_2: + vmlfb_release_devices(par); + out_err_1: + kfree(vinfo); + out_err_0: + kfree(par); + return err; +} + +static int vmlfb_open(struct fb_info *info, int user) +{ + /* + * Save registers here? + */ + return 0; +} + +static int vmlfb_release(struct fb_info *info, int user) +{ + /* + * Restore registers here. + */ + + return 0; +} + +static int vml_nearest_clock(int clock) +{ + + int i; + int cur_index; + int cur_diff; + int diff; + + cur_index = 0; + cur_diff = clock - vml_clocks[0]; + cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; + for (i = 1; i < vml_num_clocks; ++i) { + diff = clock - vml_clocks[i]; + diff = (diff < 0) ? -diff : diff; + if (diff < cur_diff) { + cur_index = i; + cur_diff = diff; + } + } + return vml_clocks[cur_index]; +} + +static int vmlfb_check_var_locked(struct fb_var_screeninfo *var, + struct vml_info *vinfo) +{ + __u32 pitch; + __u64 mem; + int nearest_clock; + int clock; + int clock_diff; + struct fb_var_screeninfo v; + + v = *var; + clock = PICOS2KHZ(var->pixclock); + + if (subsys && subsys->nearest_clock) { + nearest_clock = subsys->nearest_clock(subsys, clock); + } else { + nearest_clock = vml_nearest_clock(clock); + } + + /* + * Accept a 20% diff. + */ + + clock_diff = nearest_clock - clock; + clock_diff = (clock_diff < 0) ? -clock_diff : clock_diff; + if (clock_diff > clock / 5) { +#if 0 + printk(KERN_DEBUG MODULE_NAME ": Diff failure. %d %d\n",clock_diff,clock); +#endif + return -EINVAL; + } + + v.pixclock = KHZ2PICOS(nearest_clock); + + if (var->xres > VML_MAX_XRES || var->yres > VML_MAX_YRES) { + printk(KERN_DEBUG MODULE_NAME ": Resolution failure.\n"); + return -EINVAL; + } + if (var->xres_virtual > VML_MAX_XRES_VIRTUAL) { + printk(KERN_DEBUG MODULE_NAME + ": Virtual resolution failure.\n"); + return -EINVAL; + } + switch (v.bits_per_pixel) { + case 0 ... 16: + v.bits_per_pixel = 16; + break; + case 17 ... 32: + v.bits_per_pixel = 32; + break; + default: + printk(KERN_DEBUG MODULE_NAME ": Invalid bpp: %d.\n", + var->bits_per_pixel); + return -EINVAL; + } + + pitch = VML_ALIGN_TO((var->xres * var->bits_per_pixel) >> 3, 64); + mem = pitch * var->yres_virtual; + if (mem > vinfo->vram_contig_size) { + return -ENOMEM; + } + + switch (v.bits_per_pixel) { + case 16: + if (var->blue.offset != 0 || + var->blue.length != 5 || + var->green.offset != 5 || + var->green.length != 5 || + var->red.offset != 10 || + var->red.length != 5 || + var->transp.offset != 15 || var->transp.length != 1) { + vmlfb_set_pref_pixel_format(&v); + } + break; + case 32: + if (var->blue.offset != 0 || + var->blue.length != 8 || + var->green.offset != 8 || + var->green.length != 8 || + var->red.offset != 16 || + var->red.length != 8 || + (var->transp.length != 0 && var->transp.length != 8) || + (var->transp.length == 8 && var->transp.offset != 24)) { + vmlfb_set_pref_pixel_format(&v); + } + break; + default: + return -EINVAL; + } + + *var = v; + + return 0; +} + +static int vmlfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct vml_info *vinfo = container_of(info, struct vml_info, info); + struct vml_par *par = vinfo->par; + int ret; + + mutex_lock(par->mutex); + ret = vmlfb_check_var_locked(var, vinfo); + mutex_unlock(par->mutex); + + return ret; +} + +static void vml_wait_vblank(struct vml_info *vinfo) +{ + schedule_timeout(HZ / 40); +} + +static void vmlfb_disable_pipe(struct vml_info *vinfo) +{ + struct vml_par *par = vinfo->par; + + /* Disable the MDVO pad */ + VML_WRITE32(par, VML_RCOMPSTAT, 0); + while (!(VML_READ32(par, VML_RCOMPSTAT) & VML_MDVO_VDC_I_RCOMP)) ; + + /* Disable display planes */ + VML_WRITE32(par, VML_DSPCCNTR, + VML_READ32(par, VML_DSPCCNTR) & ~VML_GFX_ENABLE); + (void)VML_READ32(par, VML_DSPCCNTR); + /* Wait for vblank for the disable to take effect */ + vml_wait_vblank(vinfo); + + /* Next, disable display pipes */ + VML_WRITE32(par, VML_PIPEACONF, 0); + (void)VML_READ32(par, VML_PIPEACONF); + + vinfo->pipe_disabled = 1; +} + +static void vml_dump_regs(struct vml_info *vinfo) +{ + struct vml_par *par = vinfo->par; + + printk(KERN_DEBUG MODULE_NAME ": Modesetting register dump:\n"); + printk(KERN_DEBUG MODULE_NAME ": \tHTOTAL_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_HTOTAL_A)); + printk(KERN_DEBUG MODULE_NAME ": \tHBLANK_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_HBLANK_A)); + printk(KERN_DEBUG MODULE_NAME ": \tHSYNC_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_HSYNC_A)); + printk(KERN_DEBUG MODULE_NAME ": \tVTOTAL_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_VTOTAL_A)); + printk(KERN_DEBUG MODULE_NAME ": \tVBLANK_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_VBLANK_A)); + printk(KERN_DEBUG MODULE_NAME ": \tVSYNC_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_VSYNC_A)); + printk(KERN_DEBUG MODULE_NAME ": \tDSPCSTRIDE : 0x%08x\n", + (unsigned)VML_READ32(par, VML_DSPCSTRIDE)); + printk(KERN_DEBUG MODULE_NAME ": \tDSPCSIZE : 0x%08x\n", + (unsigned)VML_READ32(par, VML_DSPCSIZE)); + printk(KERN_DEBUG MODULE_NAME ": \tDSPCPOS : 0x%08x\n", + (unsigned)VML_READ32(par, VML_DSPCPOS)); + printk(KERN_DEBUG MODULE_NAME ": \tDSPARB : 0x%08x\n", + (unsigned)VML_READ32(par, VML_DSPARB)); + printk(KERN_DEBUG MODULE_NAME ": \tDSPCADDR : 0x%08x\n", + (unsigned)VML_READ32(par, VML_DSPCADDR)); + printk(KERN_DEBUG MODULE_NAME ": \tBCLRPAT_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_BCLRPAT_A)); + printk(KERN_DEBUG MODULE_NAME ": \tCANVSCLR_A : 0x%08x\n", + (unsigned)VML_READ32(par, VML_CANVSCLR_A)); + printk(KERN_DEBUG MODULE_NAME ": \tPIPEASRC : 0x%08x\n", + (unsigned)VML_READ32(par, VML_PIPEASRC)); + printk(KERN_DEBUG MODULE_NAME ": \tPIPEACONF : 0x%08x\n", + (unsigned)VML_READ32(par, VML_PIPEACONF)); + printk(KERN_DEBUG MODULE_NAME ": \tDSPCCNTR : 0x%08x\n", + (unsigned)VML_READ32(par, VML_DSPCCNTR)); + printk(KERN_DEBUG MODULE_NAME ": \tRCOMPSTAT : 0x%08x\n", + (unsigned)VML_READ32(par, VML_RCOMPSTAT)); + printk(KERN_DEBUG MODULE_NAME ": End of modesetting register dump.\n"); +} + +static int vmlfb_set_par_locked(struct vml_info *vinfo) +{ + struct vml_par *par = vinfo->par; + struct fb_info *info = &vinfo->info; + struct fb_var_screeninfo *var = &info->var; + __u32 htotal, hactive, hblank_start, hblank_end, hsync_start, hsync_end; + __u32 vtotal, vactive, vblank_start, vblank_end, vsync_start, vsync_end; + __u32 dspcntr; + int clock; + + vinfo->bytes_per_pixel = var->bits_per_pixel >> 3; + vinfo->stride = + VML_ALIGN_TO(var->xres_virtual * vinfo->bytes_per_pixel, 64); + info->fix.line_length = vinfo->stride; + + if (!subsys) + return 0; + + htotal = + var->xres + var->right_margin + var->hsync_len + var->left_margin; + hactive = var->xres; + hblank_start = var->xres; + hblank_end = htotal; + hsync_start = hactive + var->right_margin; + hsync_end = hsync_start + var->hsync_len; + + vtotal = + var->yres + var->lower_margin + var->vsync_len + var->upper_margin; + vactive = var->yres; + vblank_start = var->yres; + vblank_end = vtotal; + vsync_start = vactive + var->lower_margin; + vsync_end = vsync_start + var->vsync_len; + + dspcntr = VML_GFX_ENABLE | VML_GFX_GAMMABYPASS; + clock = PICOS2KHZ(var->pixclock); + + if (subsys->nearest_clock) { + clock = subsys->nearest_clock(subsys, clock); + } else { + clock = vml_nearest_clock(clock); + } + printk(KERN_DEBUG MODULE_NAME + ": Set mode Hfreq : %d kHz, Vfreq : %d Hz.\n", clock / htotal, + ((clock / htotal) * 1000) / vtotal); + + switch (var->bits_per_pixel) { + case 16: + dspcntr |= VML_GFX_ARGB1555; + break; + case 32: + if (var->transp.length == 8) + dspcntr |= VML_GFX_ARGB8888 | VML_GFX_ALPHAMULT; + else + dspcntr |= VML_GFX_RGB0888; + break; + default: + return -EINVAL; + } + + vmlfb_disable_pipe(vinfo); + mb(); + + if (subsys->set_clock) + subsys->set_clock(subsys, clock); + else + return -EINVAL; + + VML_WRITE32(par, VML_HTOTAL_A, ((htotal - 1) << 16) | (hactive - 1)); + VML_WRITE32(par, VML_HBLANK_A, + ((hblank_end - 1) << 16) | (hblank_start - 1)); + VML_WRITE32(par, VML_HSYNC_A, + ((hsync_end - 1) << 16) | (hsync_start - 1)); + VML_WRITE32(par, VML_VTOTAL_A, ((vtotal - 1) << 16) | (vactive - 1)); + VML_WRITE32(par, VML_VBLANK_A, + ((vblank_end - 1) << 16) | (vblank_start - 1)); + VML_WRITE32(par, VML_VSYNC_A, + ((vsync_end - 1) << 16) | (vsync_start - 1)); + VML_WRITE32(par, VML_DSPCSTRIDE, vinfo->stride); + VML_WRITE32(par, VML_DSPCSIZE, + ((var->yres - 1) << 16) | (var->xres - 1)); + VML_WRITE32(par, VML_DSPCPOS, 0x00000000); + VML_WRITE32(par, VML_DSPARB, VML_FIFO_DEFAULT); + VML_WRITE32(par, VML_BCLRPAT_A, 0x00000000); + VML_WRITE32(par, VML_CANVSCLR_A, 0x00000000); + VML_WRITE32(par, VML_PIPEASRC, + ((var->xres - 1) << 16) | (var->yres - 1)); + + wmb(); + VML_WRITE32(par, VML_PIPEACONF, VML_PIPE_ENABLE); + wmb(); + VML_WRITE32(par, VML_DSPCCNTR, dspcntr); + wmb(); + VML_WRITE32(par, VML_DSPCADDR, (__u32) vinfo->vram_start + + var->yoffset * vinfo->stride + + var->xoffset * vinfo->bytes_per_pixel); + + VML_WRITE32(par, VML_RCOMPSTAT, VML_MDVO_PAD_ENABLE); + + while (!(VML_READ32(par, VML_RCOMPSTAT) & + (VML_MDVO_VDC_I_RCOMP | VML_MDVO_PAD_ENABLE))) ; + + vinfo->pipe_disabled = 0; + vml_dump_regs(vinfo); + + return 0; +} + +static int vmlfb_set_par(struct fb_info *info) +{ + struct vml_info *vinfo = container_of(info, struct vml_info, info); + struct vml_par *par = vinfo->par; + int ret; + + mutex_lock(par->mutex); + list_del(&vinfo->head); + list_add(&vinfo->head, (subsys) ? &global_has_mode : &global_no_mode); + ret = vmlfb_set_par_locked(vinfo); + + mutex_unlock(par->mutex); + return ret; +} + +static int vmlfb_blank_locked(struct vml_info *vinfo) +{ + struct vml_par *par = vinfo->par; + __u32 cur = VML_READ32(par, VML_PIPEACONF); + int has_panel = 0; + + if (subsys && subsys->has_panel) + has_panel = subsys->has_panel(subsys); + + switch (vinfo->cur_blank_mode) { + case FB_BLANK_UNBLANK: + if (vinfo->pipe_disabled) { + vmlfb_set_par_locked(vinfo); + } + VML_WRITE32(par, VML_PIPEACONF, cur & ~VML_PIPE_FORCE_BORDER); + (void)VML_READ32(par, VML_PIPEACONF); + if (has_panel && subsys->panel_on) + subsys->panel_on(subsys); + if (has_panel && subsys->backlight_on) + subsys->backlight_on(subsys); + break; + case FB_BLANK_NORMAL: + if (vinfo->pipe_disabled) { + vmlfb_set_par_locked(vinfo); + } + VML_WRITE32(par, VML_PIPEACONF, cur | VML_PIPE_FORCE_BORDER); + (void)VML_READ32(par, VML_PIPEACONF); + if (has_panel && subsys->panel_on) + subsys->panel_on(subsys); + if (has_panel && subsys->backlight_on) + subsys->backlight_on(subsys); + break; + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + if (!vinfo->pipe_disabled) { + vmlfb_disable_pipe(vinfo); + } + if (has_panel && subsys->panel_on) + subsys->panel_on(subsys); + if (has_panel && subsys->backlight_off) + subsys->backlight_off(subsys); + break; + case FB_BLANK_POWERDOWN: + if (!vinfo->pipe_disabled) { + vmlfb_disable_pipe(vinfo); + } + if (has_panel && subsys->panel_off) + subsys->panel_off(subsys); + if (has_panel && subsys->backlight_off) + subsys->backlight_off(subsys); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vmlfb_blank(int blank_mode, struct fb_info *info) +{ + struct vml_info *vinfo = container_of(info, struct vml_info, info); + struct vml_par *par = vinfo->par; + int ret; + + mutex_lock(par->mutex); + vinfo->cur_blank_mode = blank_mode; + ret = vmlfb_blank_locked(vinfo); + mutex_unlock(par->mutex); + return ret; +} + +static int vmlfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct vml_info *vinfo = container_of(info, struct vml_info, info); + struct vml_par *par = vinfo->par; + + mutex_lock(par->mutex); + VML_WRITE32(par, VML_DSPCADDR, (__u32) vinfo->vram_start + + var->yoffset * vinfo->stride + + var->xoffset * vinfo->bytes_per_pixel); + (void)VML_READ32(par, VML_DSPCADDR); + mutex_unlock(par->mutex); + + return 0; +} + +static int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + __u32 v; + + if (regno >= 16) + return -EINVAL; + + if (info->var.grayscale) { + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + if (info->fix.visual != FB_VISUAL_TRUECOLOR) + return -EINVAL; + + red = VML_TOHW(red, info->var.red.length); + blue = VML_TOHW(blue, info->var.blue.length); + green = VML_TOHW(green, info->var.green.length); + transp = VML_TOHW(transp, info->var.transp.length); + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + switch (info->var.bits_per_pixel) { + case 16: + ((__u32 *) info->pseudo_palette)[regno] = v; + break; + case 24: + case 32: + ((__u32 *) info->pseudo_palette)[regno] = v; + break; + } + return 0; +} + +static inline int tag_equal(vml_pci_tag a, vml_pci_tag b) +{ + return ((a.bus == b.bus) && + (a.slot == b.slot) && (a.function == b.function)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) +static int vmlfb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, struct fb_info *info) +#else +static int vmlfb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +#endif +{ + vml_init_t varg; + vml_init_req_t *req = &varg.req; + vml_init_rep_t *rep = &varg.rep; + struct vml_info *vinfo = container_of(info, struct vml_info, info); + struct vml_par *par = vinfo->par; + struct pci_dev *gpu = par->gpu.dev; + int ret = 0; + vml_pci_tag vdc_tag = VML_PCI_TAG(par->vdc->bus->number, + PCI_SLOT(par->vdc->devfn), + PCI_FUNC(par->vdc->devfn)); + vml_pci_tag gpu_tag = VML_PCI_TAG(gpu->bus->number, + PCI_SLOT(gpu->devfn), + PCI_FUNC(gpu->devfn)); + + if (_IOC_TYPE(cmd) != VML_IOC_MAGIC) + return -ENOTTY; + if (_IOC_NR(cmd) > VML_IOC_MAXNR) + return -ENOTTY; + + if (copy_from_user(&varg, (void __user *)arg, sizeof(varg))) + return -EFAULT; + + if ((req->major != VML_KI_MAJOR) || (req->minor > VML_KI_MINOR)) { + printk(KERN_ERR MODULE_NAME + ": Kernel driver version mismatch.\n"); + return -EINVAL; + } + + mutex_lock(par->mutex); + if (!tag_equal(req->vdc_tag, vdc_tag) || req->pipe != vinfo->pipe) { + mutex_unlock(par->mutex); + printk(KERN_ERR MODULE_NAME ": Incorrect pci tag.\n"); + return -EINVAL; + } + rep->gpu_tag = gpu_tag; + rep->vram_offset = vinfo->vram_start; + rep->vram_contig_size = vinfo->vram_contig_size; + rep->vram_total_size = vinfo->vram_total_size; + + mutex_unlock(par->mutex); + + if (copy_to_user((void __user *)arg, &varg, sizeof(varg))) + return -EFAULT; + return ret; +} + +static int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + vma->vm_ops = &vmlfb_vm_ops; + vma->vm_flags |= VM_RESERVED | VM_IO; + vma->vm_private_data = container_of(info, struct vml_info, info); + + vmlfb_vma_open(vma); + return 0; +} + +static int vmlfb_sync(struct fb_info *info) +{ + return 0; +} + +static int vmlfb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + return -EINVAL; /* just to force soft_cursor() call */ +} + +static struct fb_ops vmlfb_ops = { + .owner = THIS_MODULE, + .fb_open = vmlfb_open, + .fb_release = vmlfb_release, + .fb_check_var = vmlfb_check_var, + .fb_set_par = vmlfb_set_par, + .fb_blank = vmlfb_blank, + .fb_pan_display = vmlfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = vmlfb_cursor, + .fb_sync = vmlfb_sync, + .fb_ioctl = vmlfb_ioctl, + .fb_mmap = vmlfb_mmap, + .fb_setcolreg = vmlfb_setcolreg +}; + +static struct pci_device_id vml_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, VML_DEVICE_VDC)}, + {0} +}; + +static struct pci_driver vmlfb_pci_driver = { + .name = "vmlfb", + .id_table = vml_ids, + .probe = vml_pci_probe, + .remove = __devexit_p(vml_pci_remove) +}; + +static void __exit vmlfb_cleanup(void) +{ + pci_unregister_driver(&vmlfb_pci_driver); +} + +int __init vmlfb_setup(char *options) +{ + if (!options || !*options) + return 0; + + return 0; +} + +int __init vmlfb_init(void) +{ + +#ifndef MODULE + char *option = NULL; + + if (fb_get_options(MODULE_NAME, &option)) + return -ENODEV; + vmlfb_setup(option); +#endif + + printk(KERN_DEBUG MODULE_NAME ": initializing\n"); + mutex_init(&global_mutex); + INIT_LIST_HEAD(&global_gpu_list); + INIT_LIST_HEAD(&global_no_mode); + INIT_LIST_HEAD(&global_has_mode); + +#ifdef USE_PAT_WC + vml_has_pat = vmlfb_init_pat(); +#endif + + return pci_register_driver(&vmlfb_pci_driver); +} + +int vmlfb_register_subsys(struct vml_sys *sys) +{ + struct vml_info *entry; + struct list_head *list; + __u32 save_activate; + const char *name = "unknown"; + + mutex_lock(&global_mutex); + if (subsys != NULL) { + subsys->restore(subsys); + subsys->destroy(subsys); + } + subsys = sys; + subsys->save(subsys); + + /* + * We need to restart list traversal for each item, since we + * release the list mutex in the loop. + */ + + list = global_no_mode.next; + while (list != &global_no_mode) { + list_del_init(list); + entry = list_entry(list, struct vml_info, head); + + /* + * First, try the current mode which might not be + * completely validated with respect to the pixel clock. + */ + + if (!vmlfb_check_var_locked(&entry->info.var, entry)) { + vmlfb_set_par_locked(entry); + list_add_tail(list, &global_has_mode); + } else { + + /* + * Didn't work. Try to find another mode, + * that matches this subsys. + */ + + mutex_unlock(&global_mutex); + save_activate = entry->info.var.activate; + entry->info.var.bits_per_pixel = 16; + vmlfb_set_pref_pixel_format(&entry->info.var); + if (fb_find_mode(&entry->info.var, + &entry->info, + vml_default_mode, NULL, 0, NULL, 16)) { + entry->info.var.activate |= + FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; + fb_set_var(&entry->info, &entry->info.var); + } else { + printk(KERN_ERR MODULE_NAME + ": Sorry. no mode found for this subsys.\n"); + } + entry->info.var.activate = save_activate; + mutex_lock(&global_mutex); + } + vmlfb_blank_locked(entry); + list = global_no_mode.next; + } + mutex_unlock(&global_mutex); + + if (sys->subsys) + sys->subsys(sys, &name); + + printk(KERN_DEBUG MODULE_NAME ": Registered %s subsystem.\n", name); + return 0; +} + +EXPORT_SYMBOL(vmlfb_register_subsys); + +void vmlfb_unregister_subsys(struct vml_sys *sys) +{ + struct vml_info *entry, *next; + + mutex_lock(&global_mutex); + if (subsys != sys) { + mutex_unlock(&global_mutex); + return; + } + subsys->restore(subsys); + subsys = NULL; + list_for_each_entry_safe(entry, next, &global_has_mode, head) { + printk(KERN_DEBUG MODULE_NAME ": subsys disable pipe\n"); + vmlfb_disable_pipe(entry); + list_del(&entry->head); + list_add_tail(&entry->head, &global_no_mode); + } + mutex_unlock(&global_mutex); +} + +EXPORT_SYMBOL(vmlfb_unregister_subsys); + +module_init(vmlfb_init); +module_exit(vmlfb_cleanup); + +MODULE_AUTHOR("Tungsten Graphics"); +MODULE_DESCRIPTION("Initialization of the Vermilion display devices"); +MODULE_VERSION("1.0.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/vermilion/vermilion.h b/drivers/video/vermilion/vermilion.h new file mode 100644 index 0000000..47fce43 --- /dev/null +++ b/drivers/video/vermilion/vermilion.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) Intel Corp. 2007. + * All Rights Reserved. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + * This file is part of the Vermilion Range fb driver. + * The Vermilion Range fb driver 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. + * + * The Vermilion Range fb driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Thomas Hellström + */ + +#ifndef _VERMILION_H_ +#define _VERMILION_H_ + +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) +#include + +#define mutex_lock down +#define mutex_unlock up + +#define mutex semaphore + +#define mutex_init(a) sema_init((a), 1) + +#else +#include +#endif + +#define VML_DEVICE_GPU 0x5002 +#define VML_DEVICE_VDC 0x5009 + +#define VML_VRAM_AREAS 3 +#define VML_MAX_XRES 1024 +#define VML_MAX_YRES 768 +#define VML_MAX_XRES_VIRTUAL 1040 + +/* + * Display controller registers: + */ + +/* Display controller 10-bit color representation */ + +#define VML_R_MASK 0x3FF00000 +#define VML_R_SHIFT 20 +#define VML_G_MASK 0x000FFC00 +#define VML_G_SHIFT 10 +#define VML_B_MASK 0x000003FF +#define VML_B_SHIFT 0 + +/* Graphics plane control */ +#define VML_DSPCCNTR 0x00072180 +#define VML_GFX_ENABLE 0x80000000 +#define VML_GFX_GAMMABYPASS 0x40000000 +#define VML_GFX_ARGB1555 0x0C000000 +#define VML_GFX_RGB0888 0x18000000 +#define VML_GFX_ARGB8888 0x1C000000 +#define VML_GFX_ALPHACONST 0x02000000 +#define VML_GFX_ALPHAMULT 0x01000000 +#define VML_GFX_CONST_ALPHA 0x000000FF + +/* Graphics plane start address. Pixel aligned. */ +#define VML_DSPCADDR 0x00072184 + +/* Graphics plane stride register. */ +#define VML_DSPCSTRIDE 0x00072188 + +/* Graphics plane position register. */ +#define VML_DSPCPOS 0x0007218C +#define VML_POS_YMASK 0x0FFF0000 +#define VML_POS_YSHIFT 16 +#define VML_POS_XMASK 0x00000FFF +#define VML_POS_XSHIFT 0 + +/* Graphics plane height and width */ +#define VML_DSPCSIZE 0x00072190 +#define VML_SIZE_HMASK 0x0FFF0000 +#define VML_SIZE_HSHIFT 16 +#define VML_SISE_WMASK 0x00000FFF +#define VML_SIZE_WSHIFT 0 + +/* Graphics plane gamma correction lookup table registers (129 * 32 bits) */ +#define VML_DSPCGAMLUT 0x00072200 + +/* Pixel video output configuration register */ +#define VML_PVOCONFIG 0x00061140 +#define VML_CONFIG_BASE 0x80000000 +#define VML_CONFIG_PIXEL_SWAP 0x04000000 +#define VML_CONFIG_DE_INV 0x01000000 +#define VML_CONFIG_HREF_INV 0x00400000 +#define VML_CONFIG_VREF_INV 0x00100000 +#define VML_CONFIG_CLK_INV 0x00040000 +#define VML_CONFIG_CLK_DIV2 0x00010000 +#define VML_CONFIG_ESTRB_INV 0x00008000 + +/* Pipe A Horizontal total register */ +#define VML_HTOTAL_A 0x00060000 +#define VML_HTOTAL_MASK 0x1FFF0000 +#define VML_HTOTAL_SHIFT 16 +#define VML_HTOTAL_VAL 8192 +#define VML_HACTIVE_MASK 0x000007FF +#define VML_HACTIVE_SHIFT 0 +#define VML_HACTIVE_VAL 4096 + +/* Pipe A Horizontal Blank register */ +#define VML_HBLANK_A 0x00060004 +#define VML_HBLANK_END_MASK 0x1FFF0000 +#define VML_HBLANK_END_SHIFT 16 +#define VML_HBLANK_END_VAL 8192 +#define VML_HBLANK_START_MASK 0x00001FFF +#define VML_HBLANK_START_SHIFT 0 +#define VML_HBLANK_START_VAL 8192 + +/* Pipe A Horizontal Sync register */ +#define VML_HSYNC_A 0x00060008 +#define VML_HSYNC_END_MASK 0x1FFF0000 +#define VML_HSYNC_END_SHIFT 16 +#define VML_HSYNC_END_VAL 8192 +#define VML_HSYNC_START_MASK 0x00001FFF +#define VML_HSYNC_START_SHIFT 0 +#define VML_HSYNC_START_VAL 8192 + +/* Pipe A Vertical total register */ +#define VML_VTOTAL_A 0x0006000C +#define VML_VTOTAL_MASK 0x1FFF0000 +#define VML_VTOTAL_SHIFT 16 +#define VML_VTOTAL_VAL 8192 +#define VML_VACTIVE_MASK 0x000007FF +#define VML_VACTIVE_SHIFT 0 +#define VML_VACTIVE_VAL 4096 + +/* Pipe A Vertical Blank register */ +#define VML_VBLANK_A 0x00060010 +#define VML_VBLANK_END_MASK 0x1FFF0000 +#define VML_VBLANK_END_SHIFT 16 +#define VML_VBLANK_END_VAL 8192 +#define VML_VBLANK_START_MASK 0x00001FFF +#define VML_VBLANK_START_SHIFT 0 +#define VML_VBLANK_START_VAL 8192 + +/* Pipe A Vertical Sync register */ +#define VML_VSYNC_A 0x00060014 +#define VML_VSYNC_END_MASK 0x1FFF0000 +#define VML_VSYNC_END_SHIFT 16 +#define VML_VSYNC_END_VAL 8192 +#define VML_VSYNC_START_MASK 0x00001FFF +#define VML_VSYNC_START_SHIFT 0 +#define VML_VSYNC_START_VAL 8192 + +/* Pipe A Source Image size (minus one - equal to active size) + * Programmable while pipe is enabled. + */ +#define VML_PIPEASRC 0x0006001C +#define VML_PIPEASRC_HMASK 0x0FFF0000 +#define VML_PIPEASRC_HSHIFT 16 +#define VML_PIPEASRC_VMASK 0x00000FFF +#define VML_PIPEASRC_VSHIFT 0 + +/* Pipe A Border Color Pattern register (10 bit color) */ +#define VML_BCLRPAT_A 0x00060020 + +/* Pipe A Canvas Color register (10 bit color) */ +#define VML_CANVSCLR_A 0x00060024 + +/* Pipe A Configuration register */ +#define VML_PIPEACONF 0x00070008 +#define VML_PIPE_BASE 0x00000000 +#define VML_PIPE_ENABLE 0x80000000 +#define VML_PIPE_FORCE_BORDER 0x02000000 +#define VML_PIPE_PLANES_OFF 0x00080000 +#define VML_PIPE_ARGB_OUTPUT_MODE 0x00040000 + +/* Pipe A FIFO setting */ +#define VML_DSPARB 0x00070030 +#define VML_FIFO_DEFAULT 0x00001D9C + +/* MDVO rcomp status & pads control register */ +#define VML_RCOMPSTAT 0x00070048 +#define VML_MDVO_VDC_I_RCOMP 0x80000000 +#define VML_MDVO_POWERSAVE_OFF 0x00000008 +#define VML_MDVO_PAD_ENABLE 0x00000004 +#define VML_MDVO_PULLDOWN_ENABLE 0x00000001 + +struct gpu_entry { + struct list_head head; + struct pci_dev *dev; +}; + +struct vml_par { + struct mutex *mutex; + struct list_head *gpu_list; + struct gpu_entry gpu; + struct pci_dev *vdc; + u64 vdc_mem_base; + u64 vdc_mem_size; + u64 gpu_mem_base; + u64 gpu_mem_size; + u8 *vdc_mem; + u8 *gpu_mem; + atomic_t refcount; +}; + +struct vram_area { + unsigned long logical; + unsigned long phys; + unsigned long size; + unsigned order; +}; + +struct vml_info { + struct fb_info info; + struct vml_par *par; + struct list_head head; + struct vram_area vram[VML_VRAM_AREAS]; + __u64 vram_start; + __u64 vram_contig_size; + __u64 vram_total_size; + __u32 num_areas; + __u8 *vram_logical; + __u32 pseudo_palette[16]; + __u32 stride; + __u32 bytes_per_pixel; + atomic_t vmas; + __u32 pipe; + int cur_blank_mode; + int pipe_disabled; +}; + +/* + * Subsystem + */ + +struct vml_sys { + void (*destroy) (struct vml_sys * sys); + void (*subsys) (const struct vml_sys * sys, const char **name); + + /* + * Save / Restore; + */ + + int (*save) (struct vml_sys * sys); + int (*restore) (struct vml_sys * sys); + + /* + * PLL programming; + */ + + int (*prog_clock) (const struct vml_sys * sys); + int (*set_clock) (struct vml_sys * sys, int clock); + int (*nearest_clock) (const struct vml_sys * sys, int clock); + + /* + * Panel type and functions. + */ + + int (*has_panel) (const struct vml_sys * sys); + void (*panel_on) (const struct vml_sys * sys); + void (*panel_off) (const struct vml_sys * sys); + void (*backlight_on) (const struct vml_sys * sys); + void (*backlight_off) (const struct vml_sys * sys); + +}; + +extern int vmlfb_register_subsys(struct vml_sys *sys); +extern void vmlfb_unregister_subsys(struct vml_sys *sys); + +#define VML_READ32(_par, _offset) \ + (ioread32((_par)->vdc_mem + (_offset))) +#define VML_WRITE32(_par, _offset, _value) \ + iowrite32(_value, (_par)->vdc_mem + (_offset)) + +#endif diff --git a/drivers/video/vermilion/vermilion_kernel.h b/drivers/video/vermilion/vermilion_kernel.h new file mode 100644 index 0000000..b7e2cfa --- /dev/null +++ b/drivers/video/vermilion/vermilion_kernel.h @@ -0,0 +1,81 @@ +/************************************************************************** + * + * Copyright (c) Intel Corp. 2007. + * All Rights Reserved. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: + * Michel Dänzer + * Thomas Hellström + * Alan Hourihane + */ +#ifndef _VERMILION_KERNEL_H_ +#define _VERMILION_KERNEL_H_ + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#include +#define __u64 uint64_t +#endif + +#define VML_KI_MAJOR 2 +#define VML_KI_MINOR 0 + +typedef struct { + unsigned bus; + unsigned slot; + unsigned function; +} vml_pci_tag; + +typedef struct { + vml_pci_tag gpu_tag; + __u64 vram_offset; + __u64 vram_contig_size; + __u64 vram_total_size; +} vml_init_rep_t; + +typedef struct { + unsigned major; + unsigned minor; + vml_pci_tag vdc_tag; + unsigned pipe; +} vml_init_req_t; + +typedef union { + vml_init_req_t req; + vml_init_rep_t rep; +} vml_init_t; + +#define VML_IOC_MAGIC 0xD0 +#define VML_INIT_DEVICE _IOWR(VML_IOC_MAGIC, 0, vml_init_t) +#define VML_IOC_MAXNR 0 + +#endif