162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) Intel Corp. 2007. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to 762306a36Sopenharmony_ci * develop this driver. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file is part of the Vermilion Range fb driver. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Authors: 1262306a36Sopenharmony_ci * Thomas Hellström <thomas-at-tungstengraphics-dot-com> 1362306a36Sopenharmony_ci * Michel Dänzer <michel-at-tungstengraphics-dot-com> 1462306a36Sopenharmony_ci * Alan Hourihane <alanh-at-tungstengraphics-dot-com> 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/aperture.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/mm.h> 2562306a36Sopenharmony_ci#include <linux/fb.h> 2662306a36Sopenharmony_ci#include <linux/pci.h> 2762306a36Sopenharmony_ci#include <asm/set_memory.h> 2862306a36Sopenharmony_ci#include <asm/tlbflush.h> 2962306a36Sopenharmony_ci#include <linux/mmzone.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* #define VERMILION_DEBUG */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "vermilion.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MODULE_NAME "vmlfb" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define VML_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic struct mutex vml_mutex; 4062306a36Sopenharmony_cistatic struct list_head global_no_mode; 4162306a36Sopenharmony_cistatic struct list_head global_has_mode; 4262306a36Sopenharmony_cistatic struct fb_ops vmlfb_ops; 4362306a36Sopenharmony_cistatic struct vml_sys *subsys = NULL; 4462306a36Sopenharmony_cistatic char *vml_default_mode = "1024x768@60"; 4562306a36Sopenharmony_cistatic const struct fb_videomode defaultmode = { 4662306a36Sopenharmony_ci NULL, 60, 1024, 768, 12896, 144, 24, 29, 3, 136, 6, 4762306a36Sopenharmony_ci 0, FB_VMODE_NONINTERLACED 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic u32 vml_mem_requested = (10 * 1024 * 1024); 5162306a36Sopenharmony_cistatic u32 vml_mem_contig = (4 * 1024 * 1024); 5262306a36Sopenharmony_cistatic u32 vml_mem_min = (4 * 1024 * 1024); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic u32 vml_clocks[] = { 5562306a36Sopenharmony_ci 6750, 5662306a36Sopenharmony_ci 13500, 5762306a36Sopenharmony_ci 27000, 5862306a36Sopenharmony_ci 29700, 5962306a36Sopenharmony_ci 37125, 6062306a36Sopenharmony_ci 54000, 6162306a36Sopenharmony_ci 59400, 6262306a36Sopenharmony_ci 74250, 6362306a36Sopenharmony_ci 120000, 6462306a36Sopenharmony_ci 148500 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic u32 vml_num_clocks = ARRAY_SIZE(vml_clocks); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Allocate a contiguous vram area and make its linear kernel map 7162306a36Sopenharmony_ci * uncached. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int vmlfb_alloc_vram_area(struct vram_area *va, unsigned max_order, 7562306a36Sopenharmony_ci unsigned min_order) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci gfp_t flags; 7862306a36Sopenharmony_ci unsigned long i; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci max_order++; 8162306a36Sopenharmony_ci do { 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Really try hard to get the needed memory. 8462306a36Sopenharmony_ci * We need memory below the first 32MB, so we 8562306a36Sopenharmony_ci * add the __GFP_DMA flag that guarantees that we are 8662306a36Sopenharmony_ci * below the first 16MB. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci flags = __GFP_DMA | __GFP_HIGH | __GFP_KSWAPD_RECLAIM; 9062306a36Sopenharmony_ci va->logical = 9162306a36Sopenharmony_ci __get_free_pages(flags, --max_order); 9262306a36Sopenharmony_ci } while (va->logical == 0 && max_order > min_order); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (!va->logical) 9562306a36Sopenharmony_ci return -ENOMEM; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci va->phys = virt_to_phys((void *)va->logical); 9862306a36Sopenharmony_ci va->size = PAGE_SIZE << max_order; 9962306a36Sopenharmony_ci va->order = max_order; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * It seems like __get_free_pages only ups the usage count 10362306a36Sopenharmony_ci * of the first page. This doesn't work with fault mapping, so 10462306a36Sopenharmony_ci * up the usage count once more (XXX: should use split_page or 10562306a36Sopenharmony_ci * compound page). 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci memset((void *)va->logical, 0x00, va->size); 10962306a36Sopenharmony_ci for (i = va->logical; i < va->logical + va->size; i += PAGE_SIZE) { 11062306a36Sopenharmony_ci get_page(virt_to_page(i)); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * Change caching policy of the linear kernel map to avoid 11562306a36Sopenharmony_ci * mapping type conflicts with user-space mappings. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci set_pages_uc(virt_to_page(va->logical), va->size >> PAGE_SHIFT); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 12062306a36Sopenharmony_ci ": Allocated %ld bytes vram area at 0x%08lx\n", 12162306a36Sopenharmony_ci va->size, va->phys); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * Free a contiguous vram area and reset its linear kernel map 12862306a36Sopenharmony_ci * mapping type. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void vmlfb_free_vram_area(struct vram_area *va) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned long j; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (va->logical) { 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Reset the linear kernel map caching policy. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci set_pages_wb(virt_to_page(va->logical), 14262306a36Sopenharmony_ci va->size >> PAGE_SHIFT); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Decrease the usage count on the pages we've used 14662306a36Sopenharmony_ci * to compensate for upping when allocating. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci for (j = va->logical; j < va->logical + va->size; 15062306a36Sopenharmony_ci j += PAGE_SIZE) { 15162306a36Sopenharmony_ci (void)put_page_testzero(virt_to_page(j)); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 15562306a36Sopenharmony_ci ": Freeing %ld bytes vram area at 0x%08lx\n", 15662306a36Sopenharmony_ci va->size, va->phys); 15762306a36Sopenharmony_ci free_pages(va->logical, va->order); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci va->logical = 0; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Free allocated vram. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void vmlfb_free_vram(struct vml_info *vinfo) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int i; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0; i < vinfo->num_areas; ++i) { 17262306a36Sopenharmony_ci vmlfb_free_vram_area(&vinfo->vram[i]); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci vinfo->num_areas = 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * Allocate vram. Currently we try to allocate contiguous areas from the 17962306a36Sopenharmony_ci * __GFP_DMA zone and puzzle them together. A better approach would be to 18062306a36Sopenharmony_ci * allocate one contiguous area for scanout and use one-page allocations for 18162306a36Sopenharmony_ci * offscreen areas. This requires user-space and GPU virtual mappings. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int vmlfb_alloc_vram(struct vml_info *vinfo, 18562306a36Sopenharmony_ci size_t requested, 18662306a36Sopenharmony_ci size_t min_total, size_t min_contig) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int i, j; 18962306a36Sopenharmony_ci int order; 19062306a36Sopenharmony_ci int contiguous; 19162306a36Sopenharmony_ci int err; 19262306a36Sopenharmony_ci struct vram_area *va; 19362306a36Sopenharmony_ci struct vram_area *va2; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci vinfo->num_areas = 0; 19662306a36Sopenharmony_ci for (i = 0; i < VML_VRAM_AREAS; ++i) { 19762306a36Sopenharmony_ci va = &vinfo->vram[i]; 19862306a36Sopenharmony_ci order = 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci while (requested > (PAGE_SIZE << order) && order <= MAX_ORDER) 20162306a36Sopenharmony_ci order++; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci err = vmlfb_alloc_vram_area(va, order, 0); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (err) 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (i == 0) { 20962306a36Sopenharmony_ci vinfo->vram_start = va->phys; 21062306a36Sopenharmony_ci vinfo->vram_logical = (void __iomem *) va->logical; 21162306a36Sopenharmony_ci vinfo->vram_contig_size = va->size; 21262306a36Sopenharmony_ci vinfo->num_areas = 1; 21362306a36Sopenharmony_ci } else { 21462306a36Sopenharmony_ci contiguous = 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for (j = 0; j < i; ++j) { 21762306a36Sopenharmony_ci va2 = &vinfo->vram[j]; 21862306a36Sopenharmony_ci if (va->phys + va->size == va2->phys || 21962306a36Sopenharmony_ci va2->phys + va2->size == va->phys) { 22062306a36Sopenharmony_ci contiguous = 1; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (contiguous) { 22662306a36Sopenharmony_ci vinfo->num_areas++; 22762306a36Sopenharmony_ci if (va->phys < vinfo->vram_start) { 22862306a36Sopenharmony_ci vinfo->vram_start = va->phys; 22962306a36Sopenharmony_ci vinfo->vram_logical = 23062306a36Sopenharmony_ci (void __iomem *)va->logical; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci vinfo->vram_contig_size += va->size; 23362306a36Sopenharmony_ci } else { 23462306a36Sopenharmony_ci vmlfb_free_vram_area(va); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (requested < va->size) 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci else 24262306a36Sopenharmony_ci requested -= va->size; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (vinfo->vram_contig_size > min_total && 24662306a36Sopenharmony_ci vinfo->vram_contig_size > min_contig) { 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 24962306a36Sopenharmony_ci ": Contiguous vram: %ld bytes at physical 0x%08lx.\n", 25062306a36Sopenharmony_ci (unsigned long)vinfo->vram_contig_size, 25162306a36Sopenharmony_ci (unsigned long)vinfo->vram_start); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME 25762306a36Sopenharmony_ci ": Could not allocate requested minimal amount of vram.\n"); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci vmlfb_free_vram(vinfo); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return -ENOMEM; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* 26562306a36Sopenharmony_ci * Find the GPU to use with our display controller. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int vmlfb_get_gpu(struct vml_par *par) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci mutex_lock(&vml_mutex); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci par->gpu = pci_get_device(PCI_VENDOR_ID_INTEL, VML_DEVICE_GPU, NULL); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!par->gpu) { 27562306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 27662306a36Sopenharmony_ci return -ENODEV; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (pci_enable_device(par->gpu) < 0) { 28262306a36Sopenharmony_ci pci_dev_put(par->gpu); 28362306a36Sopenharmony_ci return -ENODEV; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * Find a contiguous vram area that contains a given offset from vram start. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_cistatic int vmlfb_vram_offset(struct vml_info *vinfo, unsigned long offset) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci unsigned long aoffset; 29562306a36Sopenharmony_ci unsigned i; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci for (i = 0; i < vinfo->num_areas; ++i) { 29862306a36Sopenharmony_ci aoffset = offset - (vinfo->vram[i].phys - vinfo->vram_start); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (aoffset < vinfo->vram[i].size) { 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* 30962306a36Sopenharmony_ci * Remap the MMIO register spaces of the VDC and the GPU. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int vmlfb_enable_mmio(struct vml_par *par) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int err; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci par->vdc_mem_base = pci_resource_start(par->vdc, 0); 31762306a36Sopenharmony_ci par->vdc_mem_size = pci_resource_len(par->vdc, 0); 31862306a36Sopenharmony_ci if (!request_mem_region(par->vdc_mem_base, par->vdc_mem_size, "vmlfb")) { 31962306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME 32062306a36Sopenharmony_ci ": Could not claim display controller MMIO.\n"); 32162306a36Sopenharmony_ci return -EBUSY; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci par->vdc_mem = ioremap(par->vdc_mem_base, par->vdc_mem_size); 32462306a36Sopenharmony_ci if (par->vdc_mem == NULL) { 32562306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME 32662306a36Sopenharmony_ci ": Could not map display controller MMIO.\n"); 32762306a36Sopenharmony_ci err = -ENOMEM; 32862306a36Sopenharmony_ci goto out_err_0; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci par->gpu_mem_base = pci_resource_start(par->gpu, 0); 33262306a36Sopenharmony_ci par->gpu_mem_size = pci_resource_len(par->gpu, 0); 33362306a36Sopenharmony_ci if (!request_mem_region(par->gpu_mem_base, par->gpu_mem_size, "vmlfb")) { 33462306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Could not claim GPU MMIO.\n"); 33562306a36Sopenharmony_ci err = -EBUSY; 33662306a36Sopenharmony_ci goto out_err_1; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci par->gpu_mem = ioremap(par->gpu_mem_base, par->gpu_mem_size); 33962306a36Sopenharmony_ci if (par->gpu_mem == NULL) { 34062306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Could not map GPU MMIO.\n"); 34162306a36Sopenharmony_ci err = -ENOMEM; 34262306a36Sopenharmony_ci goto out_err_2; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciout_err_2: 34862306a36Sopenharmony_ci release_mem_region(par->gpu_mem_base, par->gpu_mem_size); 34962306a36Sopenharmony_ciout_err_1: 35062306a36Sopenharmony_ci iounmap(par->vdc_mem); 35162306a36Sopenharmony_ciout_err_0: 35262306a36Sopenharmony_ci release_mem_region(par->vdc_mem_base, par->vdc_mem_size); 35362306a36Sopenharmony_ci return err; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* 35762306a36Sopenharmony_ci * Unmap the VDC and GPU register spaces. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void vmlfb_disable_mmio(struct vml_par *par) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci iounmap(par->gpu_mem); 36362306a36Sopenharmony_ci release_mem_region(par->gpu_mem_base, par->gpu_mem_size); 36462306a36Sopenharmony_ci iounmap(par->vdc_mem); 36562306a36Sopenharmony_ci release_mem_region(par->vdc_mem_base, par->vdc_mem_size); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * Release and uninit the VDC and GPU. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void vmlfb_release_devices(struct vml_par *par) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci if (atomic_dec_and_test(&par->refcount)) { 37562306a36Sopenharmony_ci pci_disable_device(par->gpu); 37662306a36Sopenharmony_ci pci_disable_device(par->vdc); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* 38162306a36Sopenharmony_ci * Free up allocated resources for a device. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void vml_pci_remove(struct pci_dev *dev) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct fb_info *info; 38762306a36Sopenharmony_ci struct vml_info *vinfo; 38862306a36Sopenharmony_ci struct vml_par *par; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci info = pci_get_drvdata(dev); 39162306a36Sopenharmony_ci if (info) { 39262306a36Sopenharmony_ci vinfo = container_of(info, struct vml_info, info); 39362306a36Sopenharmony_ci par = vinfo->par; 39462306a36Sopenharmony_ci mutex_lock(&vml_mutex); 39562306a36Sopenharmony_ci unregister_framebuffer(info); 39662306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 39762306a36Sopenharmony_ci vmlfb_free_vram(vinfo); 39862306a36Sopenharmony_ci vmlfb_disable_mmio(par); 39962306a36Sopenharmony_ci vmlfb_release_devices(par); 40062306a36Sopenharmony_ci kfree(vinfo); 40162306a36Sopenharmony_ci kfree(par); 40262306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void vmlfb_set_pref_pixel_format(struct fb_var_screeninfo *var) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci switch (var->bits_per_pixel) { 40962306a36Sopenharmony_ci case 16: 41062306a36Sopenharmony_ci var->blue.offset = 0; 41162306a36Sopenharmony_ci var->blue.length = 5; 41262306a36Sopenharmony_ci var->green.offset = 5; 41362306a36Sopenharmony_ci var->green.length = 5; 41462306a36Sopenharmony_ci var->red.offset = 10; 41562306a36Sopenharmony_ci var->red.length = 5; 41662306a36Sopenharmony_ci var->transp.offset = 15; 41762306a36Sopenharmony_ci var->transp.length = 1; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci case 32: 42062306a36Sopenharmony_ci var->blue.offset = 0; 42162306a36Sopenharmony_ci var->blue.length = 8; 42262306a36Sopenharmony_ci var->green.offset = 8; 42362306a36Sopenharmony_ci var->green.length = 8; 42462306a36Sopenharmony_ci var->red.offset = 16; 42562306a36Sopenharmony_ci var->red.length = 8; 42662306a36Sopenharmony_ci var->transp.offset = 24; 42762306a36Sopenharmony_ci var->transp.length = 0; 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci default: 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci var->blue.msb_right = var->green.msb_right = 43462306a36Sopenharmony_ci var->red.msb_right = var->transp.msb_right = 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/* 43862306a36Sopenharmony_ci * Device initialization. 43962306a36Sopenharmony_ci * We initialize one vml_par struct per device and one vml_info 44062306a36Sopenharmony_ci * struct per pipe. Currently we have only one pipe. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int vml_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct vml_info *vinfo; 44662306a36Sopenharmony_ci struct fb_info *info; 44762306a36Sopenharmony_ci struct vml_par *par; 44862306a36Sopenharmony_ci int err; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci err = aperture_remove_conflicting_pci_devices(dev, "vmlfb"); 45162306a36Sopenharmony_ci if (err) 45262306a36Sopenharmony_ci return err; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci par = kzalloc(sizeof(*par), GFP_KERNEL); 45562306a36Sopenharmony_ci if (par == NULL) 45662306a36Sopenharmony_ci return -ENOMEM; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci vinfo = kzalloc(sizeof(*vinfo), GFP_KERNEL); 45962306a36Sopenharmony_ci if (vinfo == NULL) { 46062306a36Sopenharmony_ci err = -ENOMEM; 46162306a36Sopenharmony_ci goto out_err_0; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci vinfo->par = par; 46562306a36Sopenharmony_ci par->vdc = dev; 46662306a36Sopenharmony_ci atomic_set(&par->refcount, 1); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci switch (id->device) { 46962306a36Sopenharmony_ci case VML_DEVICE_VDC: 47062306a36Sopenharmony_ci if ((err = vmlfb_get_gpu(par))) 47162306a36Sopenharmony_ci goto out_err_1; 47262306a36Sopenharmony_ci pci_set_drvdata(dev, &vinfo->info); 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci default: 47562306a36Sopenharmony_ci err = -ENODEV; 47662306a36Sopenharmony_ci goto out_err_1; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci info = &vinfo->info; 48062306a36Sopenharmony_ci info->flags = FBINFO_PARTIAL_PAN_OK; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci err = vmlfb_enable_mmio(par); 48362306a36Sopenharmony_ci if (err) 48462306a36Sopenharmony_ci goto out_err_2; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci err = vmlfb_alloc_vram(vinfo, vml_mem_requested, 48762306a36Sopenharmony_ci vml_mem_contig, vml_mem_min); 48862306a36Sopenharmony_ci if (err) 48962306a36Sopenharmony_ci goto out_err_3; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci strcpy(info->fix.id, "Vermilion Range"); 49262306a36Sopenharmony_ci info->fix.mmio_start = 0; 49362306a36Sopenharmony_ci info->fix.mmio_len = 0; 49462306a36Sopenharmony_ci info->fix.smem_start = vinfo->vram_start; 49562306a36Sopenharmony_ci info->fix.smem_len = vinfo->vram_contig_size; 49662306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 49762306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 49862306a36Sopenharmony_ci info->fix.ypanstep = 1; 49962306a36Sopenharmony_ci info->fix.xpanstep = 1; 50062306a36Sopenharmony_ci info->fix.ywrapstep = 0; 50162306a36Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 50262306a36Sopenharmony_ci info->screen_base = vinfo->vram_logical; 50362306a36Sopenharmony_ci info->pseudo_palette = vinfo->pseudo_palette; 50462306a36Sopenharmony_ci info->par = par; 50562306a36Sopenharmony_ci info->fbops = &vmlfb_ops; 50662306a36Sopenharmony_ci info->device = &dev->dev; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci INIT_LIST_HEAD(&vinfo->head); 50962306a36Sopenharmony_ci vinfo->pipe_disabled = 1; 51062306a36Sopenharmony_ci vinfo->cur_blank_mode = FB_BLANK_UNBLANK; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci info->var.grayscale = 0; 51362306a36Sopenharmony_ci info->var.bits_per_pixel = 16; 51462306a36Sopenharmony_ci vmlfb_set_pref_pixel_format(&info->var); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (!fb_find_mode 51762306a36Sopenharmony_ci (&info->var, info, vml_default_mode, NULL, 0, &defaultmode, 16)) { 51862306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Could not find initial mode\n"); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { 52262306a36Sopenharmony_ci err = -ENOMEM; 52362306a36Sopenharmony_ci goto out_err_4; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci err = register_framebuffer(info); 52762306a36Sopenharmony_ci if (err) { 52862306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Register framebuffer error.\n"); 52962306a36Sopenharmony_ci goto out_err_5; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci printk("Initialized vmlfb\n"); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ciout_err_5: 53762306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 53862306a36Sopenharmony_ciout_err_4: 53962306a36Sopenharmony_ci vmlfb_free_vram(vinfo); 54062306a36Sopenharmony_ciout_err_3: 54162306a36Sopenharmony_ci vmlfb_disable_mmio(par); 54262306a36Sopenharmony_ciout_err_2: 54362306a36Sopenharmony_ci vmlfb_release_devices(par); 54462306a36Sopenharmony_ciout_err_1: 54562306a36Sopenharmony_ci kfree(vinfo); 54662306a36Sopenharmony_ciout_err_0: 54762306a36Sopenharmony_ci kfree(par); 54862306a36Sopenharmony_ci return err; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int vmlfb_open(struct fb_info *info, int user) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * Save registers here? 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int vmlfb_release(struct fb_info *info, int user) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * Restore registers here. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int vml_nearest_clock(int clock) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci int i; 57262306a36Sopenharmony_ci int cur_index; 57362306a36Sopenharmony_ci int cur_diff; 57462306a36Sopenharmony_ci int diff; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci cur_index = 0; 57762306a36Sopenharmony_ci cur_diff = clock - vml_clocks[0]; 57862306a36Sopenharmony_ci cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; 57962306a36Sopenharmony_ci for (i = 1; i < vml_num_clocks; ++i) { 58062306a36Sopenharmony_ci diff = clock - vml_clocks[i]; 58162306a36Sopenharmony_ci diff = (diff < 0) ? -diff : diff; 58262306a36Sopenharmony_ci if (diff < cur_diff) { 58362306a36Sopenharmony_ci cur_index = i; 58462306a36Sopenharmony_ci cur_diff = diff; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci return vml_clocks[cur_index]; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int vmlfb_check_var_locked(struct fb_var_screeninfo *var, 59162306a36Sopenharmony_ci struct vml_info *vinfo) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci u32 pitch; 59462306a36Sopenharmony_ci u64 mem; 59562306a36Sopenharmony_ci int nearest_clock; 59662306a36Sopenharmony_ci int clock; 59762306a36Sopenharmony_ci int clock_diff; 59862306a36Sopenharmony_ci struct fb_var_screeninfo v; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci v = *var; 60162306a36Sopenharmony_ci clock = PICOS2KHZ(var->pixclock); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (subsys && subsys->nearest_clock) { 60462306a36Sopenharmony_ci nearest_clock = subsys->nearest_clock(subsys, clock); 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci nearest_clock = vml_nearest_clock(clock); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * Accept a 20% diff. 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci clock_diff = nearest_clock - clock; 61462306a36Sopenharmony_ci clock_diff = (clock_diff < 0) ? -clock_diff : clock_diff; 61562306a36Sopenharmony_ci if (clock_diff > clock / 5) { 61662306a36Sopenharmony_ci#if 0 61762306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Diff failure. %d %d\n",clock_diff,clock); 61862306a36Sopenharmony_ci#endif 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci v.pixclock = KHZ2PICOS(nearest_clock); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (var->xres > VML_MAX_XRES || var->yres > VML_MAX_YRES) { 62562306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Resolution failure.\n"); 62662306a36Sopenharmony_ci return -EINVAL; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci if (var->xres_virtual > VML_MAX_XRES_VIRTUAL) { 62962306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 63062306a36Sopenharmony_ci ": Virtual resolution failure.\n"); 63162306a36Sopenharmony_ci return -EINVAL; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci switch (v.bits_per_pixel) { 63462306a36Sopenharmony_ci case 0 ... 16: 63562306a36Sopenharmony_ci v.bits_per_pixel = 16; 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci case 17 ... 32: 63862306a36Sopenharmony_ci v.bits_per_pixel = 32; 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci default: 64162306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Invalid bpp: %d.\n", 64262306a36Sopenharmony_ci var->bits_per_pixel); 64362306a36Sopenharmony_ci return -EINVAL; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci pitch = ALIGN((var->xres * var->bits_per_pixel) >> 3, 0x40); 64762306a36Sopenharmony_ci mem = (u64)pitch * var->yres_virtual; 64862306a36Sopenharmony_ci if (mem > vinfo->vram_contig_size) { 64962306a36Sopenharmony_ci return -ENOMEM; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci switch (v.bits_per_pixel) { 65362306a36Sopenharmony_ci case 16: 65462306a36Sopenharmony_ci if (var->blue.offset != 0 || 65562306a36Sopenharmony_ci var->blue.length != 5 || 65662306a36Sopenharmony_ci var->green.offset != 5 || 65762306a36Sopenharmony_ci var->green.length != 5 || 65862306a36Sopenharmony_ci var->red.offset != 10 || 65962306a36Sopenharmony_ci var->red.length != 5 || 66062306a36Sopenharmony_ci var->transp.offset != 15 || var->transp.length != 1) { 66162306a36Sopenharmony_ci vmlfb_set_pref_pixel_format(&v); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case 32: 66562306a36Sopenharmony_ci if (var->blue.offset != 0 || 66662306a36Sopenharmony_ci var->blue.length != 8 || 66762306a36Sopenharmony_ci var->green.offset != 8 || 66862306a36Sopenharmony_ci var->green.length != 8 || 66962306a36Sopenharmony_ci var->red.offset != 16 || 67062306a36Sopenharmony_ci var->red.length != 8 || 67162306a36Sopenharmony_ci (var->transp.length != 0 && var->transp.length != 8) || 67262306a36Sopenharmony_ci (var->transp.length == 8 && var->transp.offset != 24)) { 67362306a36Sopenharmony_ci vmlfb_set_pref_pixel_format(&v); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci default: 67762306a36Sopenharmony_ci return -EINVAL; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci *var = v; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic int vmlfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 68862306a36Sopenharmony_ci int ret; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci mutex_lock(&vml_mutex); 69162306a36Sopenharmony_ci ret = vmlfb_check_var_locked(var, vinfo); 69262306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic void vml_wait_vblank(struct vml_info *vinfo) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ 70062306a36Sopenharmony_ci mdelay(20); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic void vmlfb_disable_pipe(struct vml_info *vinfo) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct vml_par *par = vinfo->par; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Disable the MDVO pad */ 70862306a36Sopenharmony_ci VML_WRITE32(par, VML_RCOMPSTAT, 0); 70962306a36Sopenharmony_ci while (!(VML_READ32(par, VML_RCOMPSTAT) & VML_MDVO_VDC_I_RCOMP)) ; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Disable display planes */ 71262306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPCCNTR, 71362306a36Sopenharmony_ci VML_READ32(par, VML_DSPCCNTR) & ~VML_GFX_ENABLE); 71462306a36Sopenharmony_ci (void)VML_READ32(par, VML_DSPCCNTR); 71562306a36Sopenharmony_ci /* Wait for vblank for the disable to take effect */ 71662306a36Sopenharmony_ci vml_wait_vblank(vinfo); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* Next, disable display pipes */ 71962306a36Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, 0); 72062306a36Sopenharmony_ci (void)VML_READ32(par, VML_PIPEACONF); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci vinfo->pipe_disabled = 1; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci#ifdef VERMILION_DEBUG 72662306a36Sopenharmony_cistatic void vml_dump_regs(struct vml_info *vinfo) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct vml_par *par = vinfo->par; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Modesetting register dump:\n"); 73162306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tHTOTAL_A : 0x%08x\n", 73262306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_HTOTAL_A)); 73362306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tHBLANK_A : 0x%08x\n", 73462306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_HBLANK_A)); 73562306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tHSYNC_A : 0x%08x\n", 73662306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_HSYNC_A)); 73762306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tVTOTAL_A : 0x%08x\n", 73862306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_VTOTAL_A)); 73962306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tVBLANK_A : 0x%08x\n", 74062306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_VBLANK_A)); 74162306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tVSYNC_A : 0x%08x\n", 74262306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_VSYNC_A)); 74362306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCSTRIDE : 0x%08x\n", 74462306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCSTRIDE)); 74562306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCSIZE : 0x%08x\n", 74662306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCSIZE)); 74762306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCPOS : 0x%08x\n", 74862306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCPOS)); 74962306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPARB : 0x%08x\n", 75062306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPARB)); 75162306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCADDR : 0x%08x\n", 75262306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCADDR)); 75362306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tBCLRPAT_A : 0x%08x\n", 75462306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_BCLRPAT_A)); 75562306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tCANVSCLR_A : 0x%08x\n", 75662306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_CANVSCLR_A)); 75762306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tPIPEASRC : 0x%08x\n", 75862306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_PIPEASRC)); 75962306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tPIPEACONF : 0x%08x\n", 76062306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_PIPEACONF)); 76162306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCCNTR : 0x%08x\n", 76262306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCCNTR)); 76362306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tRCOMPSTAT : 0x%08x\n", 76462306a36Sopenharmony_ci (unsigned)VML_READ32(par, VML_RCOMPSTAT)); 76562306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": End of modesetting register dump.\n"); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci#endif 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int vmlfb_set_par_locked(struct vml_info *vinfo) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct vml_par *par = vinfo->par; 77262306a36Sopenharmony_ci struct fb_info *info = &vinfo->info; 77362306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 77462306a36Sopenharmony_ci u32 htotal, hactive, hblank_start, hblank_end, hsync_start, hsync_end; 77562306a36Sopenharmony_ci u32 vtotal, vactive, vblank_start, vblank_end, vsync_start, vsync_end; 77662306a36Sopenharmony_ci u32 dspcntr; 77762306a36Sopenharmony_ci int clock; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci vinfo->bytes_per_pixel = var->bits_per_pixel >> 3; 78062306a36Sopenharmony_ci vinfo->stride = ALIGN(var->xres_virtual * vinfo->bytes_per_pixel, 0x40); 78162306a36Sopenharmony_ci info->fix.line_length = vinfo->stride; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (!subsys) 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci htotal = 78762306a36Sopenharmony_ci var->xres + var->right_margin + var->hsync_len + var->left_margin; 78862306a36Sopenharmony_ci hactive = var->xres; 78962306a36Sopenharmony_ci hblank_start = var->xres; 79062306a36Sopenharmony_ci hblank_end = htotal; 79162306a36Sopenharmony_ci hsync_start = hactive + var->right_margin; 79262306a36Sopenharmony_ci hsync_end = hsync_start + var->hsync_len; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci vtotal = 79562306a36Sopenharmony_ci var->yres + var->lower_margin + var->vsync_len + var->upper_margin; 79662306a36Sopenharmony_ci vactive = var->yres; 79762306a36Sopenharmony_ci vblank_start = var->yres; 79862306a36Sopenharmony_ci vblank_end = vtotal; 79962306a36Sopenharmony_ci vsync_start = vactive + var->lower_margin; 80062306a36Sopenharmony_ci vsync_end = vsync_start + var->vsync_len; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci dspcntr = VML_GFX_ENABLE | VML_GFX_GAMMABYPASS; 80362306a36Sopenharmony_ci clock = PICOS2KHZ(var->pixclock); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (subsys->nearest_clock) { 80662306a36Sopenharmony_ci clock = subsys->nearest_clock(subsys, clock); 80762306a36Sopenharmony_ci } else { 80862306a36Sopenharmony_ci clock = vml_nearest_clock(clock); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 81162306a36Sopenharmony_ci ": Set mode Hfreq : %d kHz, Vfreq : %d Hz.\n", clock / htotal, 81262306a36Sopenharmony_ci ((clock / htotal) * 1000) / vtotal); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci switch (var->bits_per_pixel) { 81562306a36Sopenharmony_ci case 16: 81662306a36Sopenharmony_ci dspcntr |= VML_GFX_ARGB1555; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case 32: 81962306a36Sopenharmony_ci if (var->transp.length == 8) 82062306a36Sopenharmony_ci dspcntr |= VML_GFX_ARGB8888 | VML_GFX_ALPHAMULT; 82162306a36Sopenharmony_ci else 82262306a36Sopenharmony_ci dspcntr |= VML_GFX_RGB0888; 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci default: 82562306a36Sopenharmony_ci return -EINVAL; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci vmlfb_disable_pipe(vinfo); 82962306a36Sopenharmony_ci mb(); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (subsys->set_clock) 83262306a36Sopenharmony_ci subsys->set_clock(subsys, clock); 83362306a36Sopenharmony_ci else 83462306a36Sopenharmony_ci return -EINVAL; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci VML_WRITE32(par, VML_HTOTAL_A, ((htotal - 1) << 16) | (hactive - 1)); 83762306a36Sopenharmony_ci VML_WRITE32(par, VML_HBLANK_A, 83862306a36Sopenharmony_ci ((hblank_end - 1) << 16) | (hblank_start - 1)); 83962306a36Sopenharmony_ci VML_WRITE32(par, VML_HSYNC_A, 84062306a36Sopenharmony_ci ((hsync_end - 1) << 16) | (hsync_start - 1)); 84162306a36Sopenharmony_ci VML_WRITE32(par, VML_VTOTAL_A, ((vtotal - 1) << 16) | (vactive - 1)); 84262306a36Sopenharmony_ci VML_WRITE32(par, VML_VBLANK_A, 84362306a36Sopenharmony_ci ((vblank_end - 1) << 16) | (vblank_start - 1)); 84462306a36Sopenharmony_ci VML_WRITE32(par, VML_VSYNC_A, 84562306a36Sopenharmony_ci ((vsync_end - 1) << 16) | (vsync_start - 1)); 84662306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPCSTRIDE, vinfo->stride); 84762306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPCSIZE, 84862306a36Sopenharmony_ci ((var->yres - 1) << 16) | (var->xres - 1)); 84962306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPCPOS, 0x00000000); 85062306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPARB, VML_FIFO_DEFAULT); 85162306a36Sopenharmony_ci VML_WRITE32(par, VML_BCLRPAT_A, 0x00000000); 85262306a36Sopenharmony_ci VML_WRITE32(par, VML_CANVSCLR_A, 0x00000000); 85362306a36Sopenharmony_ci VML_WRITE32(par, VML_PIPEASRC, 85462306a36Sopenharmony_ci ((var->xres - 1) << 16) | (var->yres - 1)); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci wmb(); 85762306a36Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, VML_PIPE_ENABLE); 85862306a36Sopenharmony_ci wmb(); 85962306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPCCNTR, dspcntr); 86062306a36Sopenharmony_ci wmb(); 86162306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + 86262306a36Sopenharmony_ci var->yoffset * vinfo->stride + 86362306a36Sopenharmony_ci var->xoffset * vinfo->bytes_per_pixel); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci VML_WRITE32(par, VML_RCOMPSTAT, VML_MDVO_PAD_ENABLE); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci while (!(VML_READ32(par, VML_RCOMPSTAT) & 86862306a36Sopenharmony_ci (VML_MDVO_VDC_I_RCOMP | VML_MDVO_PAD_ENABLE))) ; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci vinfo->pipe_disabled = 0; 87162306a36Sopenharmony_ci#ifdef VERMILION_DEBUG 87262306a36Sopenharmony_ci vml_dump_regs(vinfo); 87362306a36Sopenharmony_ci#endif 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int vmlfb_set_par(struct fb_info *info) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 88162306a36Sopenharmony_ci int ret; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci mutex_lock(&vml_mutex); 88462306a36Sopenharmony_ci list_move(&vinfo->head, (subsys) ? &global_has_mode : &global_no_mode); 88562306a36Sopenharmony_ci ret = vmlfb_set_par_locked(vinfo); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 88862306a36Sopenharmony_ci return ret; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int vmlfb_blank_locked(struct vml_info *vinfo) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct vml_par *par = vinfo->par; 89462306a36Sopenharmony_ci u32 cur = VML_READ32(par, VML_PIPEACONF); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci switch (vinfo->cur_blank_mode) { 89762306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 89862306a36Sopenharmony_ci if (vinfo->pipe_disabled) { 89962306a36Sopenharmony_ci vmlfb_set_par_locked(vinfo); 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, cur & ~VML_PIPE_FORCE_BORDER); 90262306a36Sopenharmony_ci (void)VML_READ32(par, VML_PIPEACONF); 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci case FB_BLANK_NORMAL: 90562306a36Sopenharmony_ci if (vinfo->pipe_disabled) { 90662306a36Sopenharmony_ci vmlfb_set_par_locked(vinfo); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, cur | VML_PIPE_FORCE_BORDER); 90962306a36Sopenharmony_ci (void)VML_READ32(par, VML_PIPEACONF); 91062306a36Sopenharmony_ci break; 91162306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 91262306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 91362306a36Sopenharmony_ci if (!vinfo->pipe_disabled) { 91462306a36Sopenharmony_ci vmlfb_disable_pipe(vinfo); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 91862306a36Sopenharmony_ci if (!vinfo->pipe_disabled) { 91962306a36Sopenharmony_ci vmlfb_disable_pipe(vinfo); 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci default: 92362306a36Sopenharmony_ci return -EINVAL; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic int vmlfb_blank(int blank_mode, struct fb_info *info) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 93262306a36Sopenharmony_ci int ret; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci mutex_lock(&vml_mutex); 93562306a36Sopenharmony_ci vinfo->cur_blank_mode = blank_mode; 93662306a36Sopenharmony_ci ret = vmlfb_blank_locked(vinfo); 93762306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 93862306a36Sopenharmony_ci return ret; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int vmlfb_pan_display(struct fb_var_screeninfo *var, 94262306a36Sopenharmony_ci struct fb_info *info) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 94562306a36Sopenharmony_ci struct vml_par *par = vinfo->par; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci mutex_lock(&vml_mutex); 94862306a36Sopenharmony_ci VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + 94962306a36Sopenharmony_ci var->yoffset * vinfo->stride + 95062306a36Sopenharmony_ci var->xoffset * vinfo->bytes_per_pixel); 95162306a36Sopenharmony_ci (void)VML_READ32(par, VML_DSPCADDR); 95262306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 95862306a36Sopenharmony_ci u_int transp, struct fb_info *info) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci u32 v; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (regno >= 16) 96362306a36Sopenharmony_ci return -EINVAL; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (info->var.grayscale) { 96662306a36Sopenharmony_ci red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (info->fix.visual != FB_VISUAL_TRUECOLOR) 97062306a36Sopenharmony_ci return -EINVAL; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci red = VML_TOHW(red, info->var.red.length); 97362306a36Sopenharmony_ci blue = VML_TOHW(blue, info->var.blue.length); 97462306a36Sopenharmony_ci green = VML_TOHW(green, info->var.green.length); 97562306a36Sopenharmony_ci transp = VML_TOHW(transp, info->var.transp.length); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci v = (red << info->var.red.offset) | 97862306a36Sopenharmony_ci (green << info->var.green.offset) | 97962306a36Sopenharmony_ci (blue << info->var.blue.offset) | 98062306a36Sopenharmony_ci (transp << info->var.transp.offset); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 98362306a36Sopenharmony_ci case 16: 98462306a36Sopenharmony_ci ((u32 *) info->pseudo_palette)[regno] = v; 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci case 24: 98762306a36Sopenharmony_ci case 32: 98862306a36Sopenharmony_ci ((u32 *) info->pseudo_palette)[regno] = v; 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci return 0; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 99762306a36Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 99862306a36Sopenharmony_ci int ret; 99962306a36Sopenharmony_ci unsigned long prot; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci ret = vmlfb_vram_offset(vinfo, offset); 100262306a36Sopenharmony_ci if (ret) 100362306a36Sopenharmony_ci return -EINVAL; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci prot = pgprot_val(vma->vm_page_prot) & ~_PAGE_CACHE_MASK; 100662306a36Sopenharmony_ci pgprot_val(vma->vm_page_prot) = 100762306a36Sopenharmony_ci prot | cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return vm_iomap_memory(vma, vinfo->vram_start, 101062306a36Sopenharmony_ci vinfo->vram_contig_size); 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic int vmlfb_sync(struct fb_info *info) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic int vmlfb_cursor(struct fb_info *info, struct fb_cursor *cursor) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci return -EINVAL; /* just to force soft_cursor() call */ 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic struct fb_ops vmlfb_ops = { 102462306a36Sopenharmony_ci .owner = THIS_MODULE, 102562306a36Sopenharmony_ci .fb_open = vmlfb_open, 102662306a36Sopenharmony_ci .fb_release = vmlfb_release, 102762306a36Sopenharmony_ci .fb_check_var = vmlfb_check_var, 102862306a36Sopenharmony_ci .fb_set_par = vmlfb_set_par, 102962306a36Sopenharmony_ci .fb_blank = vmlfb_blank, 103062306a36Sopenharmony_ci .fb_pan_display = vmlfb_pan_display, 103162306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 103262306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 103362306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 103462306a36Sopenharmony_ci .fb_cursor = vmlfb_cursor, 103562306a36Sopenharmony_ci .fb_sync = vmlfb_sync, 103662306a36Sopenharmony_ci .fb_mmap = vmlfb_mmap, 103762306a36Sopenharmony_ci .fb_setcolreg = vmlfb_setcolreg 103862306a36Sopenharmony_ci}; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic const struct pci_device_id vml_ids[] = { 104162306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, VML_DEVICE_VDC)}, 104262306a36Sopenharmony_ci {0} 104362306a36Sopenharmony_ci}; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic struct pci_driver vmlfb_pci_driver = { 104662306a36Sopenharmony_ci .name = "vmlfb", 104762306a36Sopenharmony_ci .id_table = vml_ids, 104862306a36Sopenharmony_ci .probe = vml_pci_probe, 104962306a36Sopenharmony_ci .remove = vml_pci_remove, 105062306a36Sopenharmony_ci}; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic void __exit vmlfb_cleanup(void) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci pci_unregister_driver(&vmlfb_pci_driver); 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic int __init vmlfb_init(void) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci#ifndef MODULE 106162306a36Sopenharmony_ci char *option = NULL; 106262306a36Sopenharmony_ci#endif 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (fb_modesetting_disabled("vmlfb")) 106562306a36Sopenharmony_ci return -ENODEV; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci#ifndef MODULE 106862306a36Sopenharmony_ci if (fb_get_options(MODULE_NAME, &option)) 106962306a36Sopenharmony_ci return -ENODEV; 107062306a36Sopenharmony_ci#endif 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": initializing\n"); 107362306a36Sopenharmony_ci mutex_init(&vml_mutex); 107462306a36Sopenharmony_ci INIT_LIST_HEAD(&global_no_mode); 107562306a36Sopenharmony_ci INIT_LIST_HEAD(&global_has_mode); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci return pci_register_driver(&vmlfb_pci_driver); 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ciint vmlfb_register_subsys(struct vml_sys *sys) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct vml_info *entry; 108362306a36Sopenharmony_ci struct list_head *list; 108462306a36Sopenharmony_ci u32 save_activate; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci mutex_lock(&vml_mutex); 108762306a36Sopenharmony_ci if (subsys != NULL) { 108862306a36Sopenharmony_ci subsys->restore(subsys); 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci subsys = sys; 109162306a36Sopenharmony_ci subsys->save(subsys); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* 109462306a36Sopenharmony_ci * We need to restart list traversal for each item, since we 109562306a36Sopenharmony_ci * release the list mutex in the loop. 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci list = global_no_mode.next; 109962306a36Sopenharmony_ci while (list != &global_no_mode) { 110062306a36Sopenharmony_ci list_del_init(list); 110162306a36Sopenharmony_ci entry = list_entry(list, struct vml_info, head); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* 110462306a36Sopenharmony_ci * First, try the current mode which might not be 110562306a36Sopenharmony_ci * completely validated with respect to the pixel clock. 110662306a36Sopenharmony_ci */ 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (!vmlfb_check_var_locked(&entry->info.var, entry)) { 110962306a36Sopenharmony_ci vmlfb_set_par_locked(entry); 111062306a36Sopenharmony_ci list_add_tail(list, &global_has_mode); 111162306a36Sopenharmony_ci } else { 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* 111462306a36Sopenharmony_ci * Didn't work. Try to find another mode, 111562306a36Sopenharmony_ci * that matches this subsys. 111662306a36Sopenharmony_ci */ 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 111962306a36Sopenharmony_ci save_activate = entry->info.var.activate; 112062306a36Sopenharmony_ci entry->info.var.bits_per_pixel = 16; 112162306a36Sopenharmony_ci vmlfb_set_pref_pixel_format(&entry->info.var); 112262306a36Sopenharmony_ci if (fb_find_mode(&entry->info.var, 112362306a36Sopenharmony_ci &entry->info, 112462306a36Sopenharmony_ci vml_default_mode, NULL, 0, NULL, 16)) { 112562306a36Sopenharmony_ci entry->info.var.activate |= 112662306a36Sopenharmony_ci FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; 112762306a36Sopenharmony_ci fb_set_var(&entry->info, &entry->info.var); 112862306a36Sopenharmony_ci } else { 112962306a36Sopenharmony_ci printk(KERN_ERR MODULE_NAME 113062306a36Sopenharmony_ci ": Sorry. no mode found for this subsys.\n"); 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci entry->info.var.activate = save_activate; 113362306a36Sopenharmony_ci mutex_lock(&vml_mutex); 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci vmlfb_blank_locked(entry); 113662306a36Sopenharmony_ci list = global_no_mode.next; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Registered %s subsystem.\n", 114162306a36Sopenharmony_ci subsys->name ? subsys->name : "unknown"); 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmlfb_register_subsys); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_civoid vmlfb_unregister_subsys(struct vml_sys *sys) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci struct vml_info *entry, *next; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci mutex_lock(&vml_mutex); 115262306a36Sopenharmony_ci if (subsys != sys) { 115362306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 115462306a36Sopenharmony_ci return; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci subsys->restore(subsys); 115762306a36Sopenharmony_ci subsys = NULL; 115862306a36Sopenharmony_ci list_for_each_entry_safe(entry, next, &global_has_mode, head) { 115962306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": subsys disable pipe\n"); 116062306a36Sopenharmony_ci vmlfb_disable_pipe(entry); 116162306a36Sopenharmony_ci list_move_tail(&entry->head, &global_no_mode); 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci mutex_unlock(&vml_mutex); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmlfb_unregister_subsys); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cimodule_init(vmlfb_init); 116962306a36Sopenharmony_cimodule_exit(vmlfb_cleanup); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ciMODULE_AUTHOR("Tungsten Graphics"); 117262306a36Sopenharmony_ciMODULE_DESCRIPTION("Initialization of the Vermilion display devices"); 117362306a36Sopenharmony_ciMODULE_VERSION("1.0.0"); 117462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1175