18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) Intel Corp. 2007. 48c2ecf20Sopenharmony_ci * All Rights Reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to 78c2ecf20Sopenharmony_ci * develop this driver. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is part of the Vermilion Range fb driver. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Authors: 128c2ecf20Sopenharmony_ci * Thomas Hellström <thomas-at-tungstengraphics-dot-com> 138c2ecf20Sopenharmony_ci * Michel Dänzer <michel-at-tungstengraphics-dot-com> 148c2ecf20Sopenharmony_ci * Alan Hourihane <alanh-at-tungstengraphics-dot-com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/mm.h> 248c2ecf20Sopenharmony_ci#include <linux/fb.h> 258c2ecf20Sopenharmony_ci#include <linux/pci.h> 268c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 278c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 288c2ecf20Sopenharmony_ci#include <linux/mmzone.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* #define VERMILION_DEBUG */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "vermilion.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define MODULE_NAME "vmlfb" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define VML_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct mutex vml_mutex; 398c2ecf20Sopenharmony_cistatic struct list_head global_no_mode; 408c2ecf20Sopenharmony_cistatic struct list_head global_has_mode; 418c2ecf20Sopenharmony_cistatic struct fb_ops vmlfb_ops; 428c2ecf20Sopenharmony_cistatic struct vml_sys *subsys = NULL; 438c2ecf20Sopenharmony_cistatic char *vml_default_mode = "1024x768@60"; 448c2ecf20Sopenharmony_cistatic const struct fb_videomode defaultmode = { 458c2ecf20Sopenharmony_ci NULL, 60, 1024, 768, 12896, 144, 24, 29, 3, 136, 6, 468c2ecf20Sopenharmony_ci 0, FB_VMODE_NONINTERLACED 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic u32 vml_mem_requested = (10 * 1024 * 1024); 508c2ecf20Sopenharmony_cistatic u32 vml_mem_contig = (4 * 1024 * 1024); 518c2ecf20Sopenharmony_cistatic u32 vml_mem_min = (4 * 1024 * 1024); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic u32 vml_clocks[] = { 548c2ecf20Sopenharmony_ci 6750, 558c2ecf20Sopenharmony_ci 13500, 568c2ecf20Sopenharmony_ci 27000, 578c2ecf20Sopenharmony_ci 29700, 588c2ecf20Sopenharmony_ci 37125, 598c2ecf20Sopenharmony_ci 54000, 608c2ecf20Sopenharmony_ci 59400, 618c2ecf20Sopenharmony_ci 74250, 628c2ecf20Sopenharmony_ci 120000, 638c2ecf20Sopenharmony_ci 148500 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic u32 vml_num_clocks = ARRAY_SIZE(vml_clocks); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Allocate a contiguous vram area and make its linear kernel map 708c2ecf20Sopenharmony_ci * uncached. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int vmlfb_alloc_vram_area(struct vram_area *va, unsigned max_order, 748c2ecf20Sopenharmony_ci unsigned min_order) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci gfp_t flags; 778c2ecf20Sopenharmony_ci unsigned long i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci max_order++; 808c2ecf20Sopenharmony_ci do { 818c2ecf20Sopenharmony_ci /* 828c2ecf20Sopenharmony_ci * Really try hard to get the needed memory. 838c2ecf20Sopenharmony_ci * We need memory below the first 32MB, so we 848c2ecf20Sopenharmony_ci * add the __GFP_DMA flag that guarantees that we are 858c2ecf20Sopenharmony_ci * below the first 16MB. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci flags = __GFP_DMA | __GFP_HIGH | __GFP_KSWAPD_RECLAIM; 898c2ecf20Sopenharmony_ci va->logical = 908c2ecf20Sopenharmony_ci __get_free_pages(flags, --max_order); 918c2ecf20Sopenharmony_ci } while (va->logical == 0 && max_order > min_order); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!va->logical) 948c2ecf20Sopenharmony_ci return -ENOMEM; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci va->phys = virt_to_phys((void *)va->logical); 978c2ecf20Sopenharmony_ci va->size = PAGE_SIZE << max_order; 988c2ecf20Sopenharmony_ci va->order = max_order; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * It seems like __get_free_pages only ups the usage count 1028c2ecf20Sopenharmony_ci * of the first page. This doesn't work with fault mapping, so 1038c2ecf20Sopenharmony_ci * up the usage count once more (XXX: should use split_page or 1048c2ecf20Sopenharmony_ci * compound page). 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci memset((void *)va->logical, 0x00, va->size); 1088c2ecf20Sopenharmony_ci for (i = va->logical; i < va->logical + va->size; i += PAGE_SIZE) { 1098c2ecf20Sopenharmony_ci get_page(virt_to_page(i)); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * Change caching policy of the linear kernel map to avoid 1148c2ecf20Sopenharmony_ci * mapping type conflicts with user-space mappings. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci set_pages_uc(virt_to_page(va->logical), va->size >> PAGE_SHIFT); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 1198c2ecf20Sopenharmony_ci ": Allocated %ld bytes vram area at 0x%08lx\n", 1208c2ecf20Sopenharmony_ci va->size, va->phys); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * Free a contiguous vram area and reset its linear kernel map 1278c2ecf20Sopenharmony_ci * mapping type. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void vmlfb_free_vram_area(struct vram_area *va) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned long j; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (va->logical) { 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * Reset the linear kernel map caching policy. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci set_pages_wb(virt_to_page(va->logical), 1418c2ecf20Sopenharmony_ci va->size >> PAGE_SHIFT); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * Decrease the usage count on the pages we've used 1458c2ecf20Sopenharmony_ci * to compensate for upping when allocating. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (j = va->logical; j < va->logical + va->size; 1498c2ecf20Sopenharmony_ci j += PAGE_SIZE) { 1508c2ecf20Sopenharmony_ci (void)put_page_testzero(virt_to_page(j)); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 1548c2ecf20Sopenharmony_ci ": Freeing %ld bytes vram area at 0x%08lx\n", 1558c2ecf20Sopenharmony_ci va->size, va->phys); 1568c2ecf20Sopenharmony_ci free_pages(va->logical, va->order); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci va->logical = 0; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * Free allocated vram. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void vmlfb_free_vram(struct vml_info *vinfo) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci int i; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (i = 0; i < vinfo->num_areas; ++i) { 1718c2ecf20Sopenharmony_ci vmlfb_free_vram_area(&vinfo->vram[i]); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci vinfo->num_areas = 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* 1778c2ecf20Sopenharmony_ci * Allocate vram. Currently we try to allocate contiguous areas from the 1788c2ecf20Sopenharmony_ci * __GFP_DMA zone and puzzle them together. A better approach would be to 1798c2ecf20Sopenharmony_ci * allocate one contiguous area for scanout and use one-page allocations for 1808c2ecf20Sopenharmony_ci * offscreen areas. This requires user-space and GPU virtual mappings. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int vmlfb_alloc_vram(struct vml_info *vinfo, 1848c2ecf20Sopenharmony_ci size_t requested, 1858c2ecf20Sopenharmony_ci size_t min_total, size_t min_contig) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int i, j; 1888c2ecf20Sopenharmony_ci int order; 1898c2ecf20Sopenharmony_ci int contiguous; 1908c2ecf20Sopenharmony_ci int err; 1918c2ecf20Sopenharmony_ci struct vram_area *va; 1928c2ecf20Sopenharmony_ci struct vram_area *va2; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci vinfo->num_areas = 0; 1958c2ecf20Sopenharmony_ci for (i = 0; i < VML_VRAM_AREAS; ++i) { 1968c2ecf20Sopenharmony_ci va = &vinfo->vram[i]; 1978c2ecf20Sopenharmony_ci order = 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci while (requested > (PAGE_SIZE << order) && order < MAX_ORDER) 2008c2ecf20Sopenharmony_ci order++; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci err = vmlfb_alloc_vram_area(va, order, 0); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (err) 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (i == 0) { 2088c2ecf20Sopenharmony_ci vinfo->vram_start = va->phys; 2098c2ecf20Sopenharmony_ci vinfo->vram_logical = (void __iomem *) va->logical; 2108c2ecf20Sopenharmony_ci vinfo->vram_contig_size = va->size; 2118c2ecf20Sopenharmony_ci vinfo->num_areas = 1; 2128c2ecf20Sopenharmony_ci } else { 2138c2ecf20Sopenharmony_ci contiguous = 0; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (j = 0; j < i; ++j) { 2168c2ecf20Sopenharmony_ci va2 = &vinfo->vram[j]; 2178c2ecf20Sopenharmony_ci if (va->phys + va->size == va2->phys || 2188c2ecf20Sopenharmony_ci va2->phys + va2->size == va->phys) { 2198c2ecf20Sopenharmony_ci contiguous = 1; 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (contiguous) { 2258c2ecf20Sopenharmony_ci vinfo->num_areas++; 2268c2ecf20Sopenharmony_ci if (va->phys < vinfo->vram_start) { 2278c2ecf20Sopenharmony_ci vinfo->vram_start = va->phys; 2288c2ecf20Sopenharmony_ci vinfo->vram_logical = 2298c2ecf20Sopenharmony_ci (void __iomem *)va->logical; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci vinfo->vram_contig_size += va->size; 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci vmlfb_free_vram_area(va); 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (requested < va->size) 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci else 2418c2ecf20Sopenharmony_ci requested -= va->size; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (vinfo->vram_contig_size > min_total && 2458c2ecf20Sopenharmony_ci vinfo->vram_contig_size > min_contig) { 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 2488c2ecf20Sopenharmony_ci ": Contiguous vram: %ld bytes at physical 0x%08lx.\n", 2498c2ecf20Sopenharmony_ci (unsigned long)vinfo->vram_contig_size, 2508c2ecf20Sopenharmony_ci (unsigned long)vinfo->vram_start); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME 2568c2ecf20Sopenharmony_ci ": Could not allocate requested minimal amount of vram.\n"); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci vmlfb_free_vram(vinfo); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return -ENOMEM; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* 2648c2ecf20Sopenharmony_ci * Find the GPU to use with our display controller. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int vmlfb_get_gpu(struct vml_par *par) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci par->gpu = pci_get_device(PCI_VENDOR_ID_INTEL, VML_DEVICE_GPU, NULL); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (!par->gpu) { 2748c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 2758c2ecf20Sopenharmony_ci return -ENODEV; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (pci_enable_device(par->gpu) < 0) { 2818c2ecf20Sopenharmony_ci pci_dev_put(par->gpu); 2828c2ecf20Sopenharmony_ci return -ENODEV; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* 2898c2ecf20Sopenharmony_ci * Find a contiguous vram area that contains a given offset from vram start. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_cistatic int vmlfb_vram_offset(struct vml_info *vinfo, unsigned long offset) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci unsigned long aoffset; 2948c2ecf20Sopenharmony_ci unsigned i; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci for (i = 0; i < vinfo->num_areas; ++i) { 2978c2ecf20Sopenharmony_ci aoffset = offset - (vinfo->vram[i].phys - vinfo->vram_start); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (aoffset < vinfo->vram[i].size) { 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return -EINVAL; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* 3088c2ecf20Sopenharmony_ci * Remap the MMIO register spaces of the VDC and the GPU. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int vmlfb_enable_mmio(struct vml_par *par) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci int err; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci par->vdc_mem_base = pci_resource_start(par->vdc, 0); 3168c2ecf20Sopenharmony_ci par->vdc_mem_size = pci_resource_len(par->vdc, 0); 3178c2ecf20Sopenharmony_ci if (!request_mem_region(par->vdc_mem_base, par->vdc_mem_size, "vmlfb")) { 3188c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME 3198c2ecf20Sopenharmony_ci ": Could not claim display controller MMIO.\n"); 3208c2ecf20Sopenharmony_ci return -EBUSY; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci par->vdc_mem = ioremap(par->vdc_mem_base, par->vdc_mem_size); 3238c2ecf20Sopenharmony_ci if (par->vdc_mem == NULL) { 3248c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME 3258c2ecf20Sopenharmony_ci ": Could not map display controller MMIO.\n"); 3268c2ecf20Sopenharmony_ci err = -ENOMEM; 3278c2ecf20Sopenharmony_ci goto out_err_0; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci par->gpu_mem_base = pci_resource_start(par->gpu, 0); 3318c2ecf20Sopenharmony_ci par->gpu_mem_size = pci_resource_len(par->gpu, 0); 3328c2ecf20Sopenharmony_ci if (!request_mem_region(par->gpu_mem_base, par->gpu_mem_size, "vmlfb")) { 3338c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Could not claim GPU MMIO.\n"); 3348c2ecf20Sopenharmony_ci err = -EBUSY; 3358c2ecf20Sopenharmony_ci goto out_err_1; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci par->gpu_mem = ioremap(par->gpu_mem_base, par->gpu_mem_size); 3388c2ecf20Sopenharmony_ci if (par->gpu_mem == NULL) { 3398c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Could not map GPU MMIO.\n"); 3408c2ecf20Sopenharmony_ci err = -ENOMEM; 3418c2ecf20Sopenharmony_ci goto out_err_2; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciout_err_2: 3478c2ecf20Sopenharmony_ci release_mem_region(par->gpu_mem_base, par->gpu_mem_size); 3488c2ecf20Sopenharmony_ciout_err_1: 3498c2ecf20Sopenharmony_ci iounmap(par->vdc_mem); 3508c2ecf20Sopenharmony_ciout_err_0: 3518c2ecf20Sopenharmony_ci release_mem_region(par->vdc_mem_base, par->vdc_mem_size); 3528c2ecf20Sopenharmony_ci return err; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* 3568c2ecf20Sopenharmony_ci * Unmap the VDC and GPU register spaces. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void vmlfb_disable_mmio(struct vml_par *par) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci iounmap(par->gpu_mem); 3628c2ecf20Sopenharmony_ci release_mem_region(par->gpu_mem_base, par->gpu_mem_size); 3638c2ecf20Sopenharmony_ci iounmap(par->vdc_mem); 3648c2ecf20Sopenharmony_ci release_mem_region(par->vdc_mem_base, par->vdc_mem_size); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* 3688c2ecf20Sopenharmony_ci * Release and uninit the VDC and GPU. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void vmlfb_release_devices(struct vml_par *par) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&par->refcount)) { 3748c2ecf20Sopenharmony_ci pci_disable_device(par->gpu); 3758c2ecf20Sopenharmony_ci pci_disable_device(par->vdc); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* 3808c2ecf20Sopenharmony_ci * Free up allocated resources for a device. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void vml_pci_remove(struct pci_dev *dev) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct fb_info *info; 3868c2ecf20Sopenharmony_ci struct vml_info *vinfo; 3878c2ecf20Sopenharmony_ci struct vml_par *par; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci info = pci_get_drvdata(dev); 3908c2ecf20Sopenharmony_ci if (info) { 3918c2ecf20Sopenharmony_ci vinfo = container_of(info, struct vml_info, info); 3928c2ecf20Sopenharmony_ci par = vinfo->par; 3938c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 3948c2ecf20Sopenharmony_ci unregister_framebuffer(info); 3958c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 3968c2ecf20Sopenharmony_ci vmlfb_free_vram(vinfo); 3978c2ecf20Sopenharmony_ci vmlfb_disable_mmio(par); 3988c2ecf20Sopenharmony_ci vmlfb_release_devices(par); 3998c2ecf20Sopenharmony_ci kfree(vinfo); 4008c2ecf20Sopenharmony_ci kfree(par); 4018c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void vmlfb_set_pref_pixel_format(struct fb_var_screeninfo *var) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 4088c2ecf20Sopenharmony_ci case 16: 4098c2ecf20Sopenharmony_ci var->blue.offset = 0; 4108c2ecf20Sopenharmony_ci var->blue.length = 5; 4118c2ecf20Sopenharmony_ci var->green.offset = 5; 4128c2ecf20Sopenharmony_ci var->green.length = 5; 4138c2ecf20Sopenharmony_ci var->red.offset = 10; 4148c2ecf20Sopenharmony_ci var->red.length = 5; 4158c2ecf20Sopenharmony_ci var->transp.offset = 15; 4168c2ecf20Sopenharmony_ci var->transp.length = 1; 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci case 32: 4198c2ecf20Sopenharmony_ci var->blue.offset = 0; 4208c2ecf20Sopenharmony_ci var->blue.length = 8; 4218c2ecf20Sopenharmony_ci var->green.offset = 8; 4228c2ecf20Sopenharmony_ci var->green.length = 8; 4238c2ecf20Sopenharmony_ci var->red.offset = 16; 4248c2ecf20Sopenharmony_ci var->red.length = 8; 4258c2ecf20Sopenharmony_ci var->transp.offset = 24; 4268c2ecf20Sopenharmony_ci var->transp.length = 0; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci default: 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci var->blue.msb_right = var->green.msb_right = 4338c2ecf20Sopenharmony_ci var->red.msb_right = var->transp.msb_right = 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * Device initialization. 4388c2ecf20Sopenharmony_ci * We initialize one vml_par struct per device and one vml_info 4398c2ecf20Sopenharmony_ci * struct per pipe. Currently we have only one pipe. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int vml_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct vml_info *vinfo; 4458c2ecf20Sopenharmony_ci struct fb_info *info; 4468c2ecf20Sopenharmony_ci struct vml_par *par; 4478c2ecf20Sopenharmony_ci int err = 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci par = kzalloc(sizeof(*par), GFP_KERNEL); 4508c2ecf20Sopenharmony_ci if (par == NULL) 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci vinfo = kzalloc(sizeof(*vinfo), GFP_KERNEL); 4548c2ecf20Sopenharmony_ci if (vinfo == NULL) { 4558c2ecf20Sopenharmony_ci err = -ENOMEM; 4568c2ecf20Sopenharmony_ci goto out_err_0; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci vinfo->par = par; 4608c2ecf20Sopenharmony_ci par->vdc = dev; 4618c2ecf20Sopenharmony_ci atomic_set(&par->refcount, 1); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci switch (id->device) { 4648c2ecf20Sopenharmony_ci case VML_DEVICE_VDC: 4658c2ecf20Sopenharmony_ci if ((err = vmlfb_get_gpu(par))) 4668c2ecf20Sopenharmony_ci goto out_err_1; 4678c2ecf20Sopenharmony_ci pci_set_drvdata(dev, &vinfo->info); 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci default: 4708c2ecf20Sopenharmony_ci err = -ENODEV; 4718c2ecf20Sopenharmony_ci goto out_err_1; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci info = &vinfo->info; 4758c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci err = vmlfb_enable_mmio(par); 4788c2ecf20Sopenharmony_ci if (err) 4798c2ecf20Sopenharmony_ci goto out_err_2; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci err = vmlfb_alloc_vram(vinfo, vml_mem_requested, 4828c2ecf20Sopenharmony_ci vml_mem_contig, vml_mem_min); 4838c2ecf20Sopenharmony_ci if (err) 4848c2ecf20Sopenharmony_ci goto out_err_3; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci strcpy(info->fix.id, "Vermilion Range"); 4878c2ecf20Sopenharmony_ci info->fix.mmio_start = 0; 4888c2ecf20Sopenharmony_ci info->fix.mmio_len = 0; 4898c2ecf20Sopenharmony_ci info->fix.smem_start = vinfo->vram_start; 4908c2ecf20Sopenharmony_ci info->fix.smem_len = vinfo->vram_contig_size; 4918c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 4928c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 4938c2ecf20Sopenharmony_ci info->fix.ypanstep = 1; 4948c2ecf20Sopenharmony_ci info->fix.xpanstep = 1; 4958c2ecf20Sopenharmony_ci info->fix.ywrapstep = 0; 4968c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 4978c2ecf20Sopenharmony_ci info->screen_base = vinfo->vram_logical; 4988c2ecf20Sopenharmony_ci info->pseudo_palette = vinfo->pseudo_palette; 4998c2ecf20Sopenharmony_ci info->par = par; 5008c2ecf20Sopenharmony_ci info->fbops = &vmlfb_ops; 5018c2ecf20Sopenharmony_ci info->device = &dev->dev; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vinfo->head); 5048c2ecf20Sopenharmony_ci vinfo->pipe_disabled = 1; 5058c2ecf20Sopenharmony_ci vinfo->cur_blank_mode = FB_BLANK_UNBLANK; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci info->var.grayscale = 0; 5088c2ecf20Sopenharmony_ci info->var.bits_per_pixel = 16; 5098c2ecf20Sopenharmony_ci vmlfb_set_pref_pixel_format(&info->var); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!fb_find_mode 5128c2ecf20Sopenharmony_ci (&info->var, info, vml_default_mode, NULL, 0, &defaultmode, 16)) { 5138c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Could not find initial mode\n"); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { 5178c2ecf20Sopenharmony_ci err = -ENOMEM; 5188c2ecf20Sopenharmony_ci goto out_err_4; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci err = register_framebuffer(info); 5228c2ecf20Sopenharmony_ci if (err) { 5238c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME ": Register framebuffer error.\n"); 5248c2ecf20Sopenharmony_ci goto out_err_5; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci printk("Initialized vmlfb\n"); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ciout_err_5: 5328c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5338c2ecf20Sopenharmony_ciout_err_4: 5348c2ecf20Sopenharmony_ci vmlfb_free_vram(vinfo); 5358c2ecf20Sopenharmony_ciout_err_3: 5368c2ecf20Sopenharmony_ci vmlfb_disable_mmio(par); 5378c2ecf20Sopenharmony_ciout_err_2: 5388c2ecf20Sopenharmony_ci vmlfb_release_devices(par); 5398c2ecf20Sopenharmony_ciout_err_1: 5408c2ecf20Sopenharmony_ci kfree(vinfo); 5418c2ecf20Sopenharmony_ciout_err_0: 5428c2ecf20Sopenharmony_ci kfree(par); 5438c2ecf20Sopenharmony_ci return err; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic int vmlfb_open(struct fb_info *info, int user) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * Save registers here? 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int vmlfb_release(struct fb_info *info, int user) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci /* 5578c2ecf20Sopenharmony_ci * Restore registers here. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int vml_nearest_clock(int clock) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci int i; 5678c2ecf20Sopenharmony_ci int cur_index; 5688c2ecf20Sopenharmony_ci int cur_diff; 5698c2ecf20Sopenharmony_ci int diff; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci cur_index = 0; 5728c2ecf20Sopenharmony_ci cur_diff = clock - vml_clocks[0]; 5738c2ecf20Sopenharmony_ci cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; 5748c2ecf20Sopenharmony_ci for (i = 1; i < vml_num_clocks; ++i) { 5758c2ecf20Sopenharmony_ci diff = clock - vml_clocks[i]; 5768c2ecf20Sopenharmony_ci diff = (diff < 0) ? -diff : diff; 5778c2ecf20Sopenharmony_ci if (diff < cur_diff) { 5788c2ecf20Sopenharmony_ci cur_index = i; 5798c2ecf20Sopenharmony_ci cur_diff = diff; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci return vml_clocks[cur_index]; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int vmlfb_check_var_locked(struct fb_var_screeninfo *var, 5868c2ecf20Sopenharmony_ci struct vml_info *vinfo) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci u32 pitch; 5898c2ecf20Sopenharmony_ci u64 mem; 5908c2ecf20Sopenharmony_ci int nearest_clock; 5918c2ecf20Sopenharmony_ci int clock; 5928c2ecf20Sopenharmony_ci int clock_diff; 5938c2ecf20Sopenharmony_ci struct fb_var_screeninfo v; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci v = *var; 5968c2ecf20Sopenharmony_ci clock = PICOS2KHZ(var->pixclock); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (subsys && subsys->nearest_clock) { 5998c2ecf20Sopenharmony_ci nearest_clock = subsys->nearest_clock(subsys, clock); 6008c2ecf20Sopenharmony_ci } else { 6018c2ecf20Sopenharmony_ci nearest_clock = vml_nearest_clock(clock); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* 6058c2ecf20Sopenharmony_ci * Accept a 20% diff. 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci clock_diff = nearest_clock - clock; 6098c2ecf20Sopenharmony_ci clock_diff = (clock_diff < 0) ? -clock_diff : clock_diff; 6108c2ecf20Sopenharmony_ci if (clock_diff > clock / 5) { 6118c2ecf20Sopenharmony_ci#if 0 6128c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Diff failure. %d %d\n",clock_diff,clock); 6138c2ecf20Sopenharmony_ci#endif 6148c2ecf20Sopenharmony_ci return -EINVAL; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci v.pixclock = KHZ2PICOS(nearest_clock); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (var->xres > VML_MAX_XRES || var->yres > VML_MAX_YRES) { 6208c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Resolution failure.\n"); 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci if (var->xres_virtual > VML_MAX_XRES_VIRTUAL) { 6248c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 6258c2ecf20Sopenharmony_ci ": Virtual resolution failure.\n"); 6268c2ecf20Sopenharmony_ci return -EINVAL; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci switch (v.bits_per_pixel) { 6298c2ecf20Sopenharmony_ci case 0 ... 16: 6308c2ecf20Sopenharmony_ci v.bits_per_pixel = 16; 6318c2ecf20Sopenharmony_ci break; 6328c2ecf20Sopenharmony_ci case 17 ... 32: 6338c2ecf20Sopenharmony_ci v.bits_per_pixel = 32; 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci default: 6368c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Invalid bpp: %d.\n", 6378c2ecf20Sopenharmony_ci var->bits_per_pixel); 6388c2ecf20Sopenharmony_ci return -EINVAL; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci pitch = ALIGN((var->xres * var->bits_per_pixel) >> 3, 0x40); 6428c2ecf20Sopenharmony_ci mem = (u64)pitch * var->yres_virtual; 6438c2ecf20Sopenharmony_ci if (mem > vinfo->vram_contig_size) { 6448c2ecf20Sopenharmony_ci return -ENOMEM; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci switch (v.bits_per_pixel) { 6488c2ecf20Sopenharmony_ci case 16: 6498c2ecf20Sopenharmony_ci if (var->blue.offset != 0 || 6508c2ecf20Sopenharmony_ci var->blue.length != 5 || 6518c2ecf20Sopenharmony_ci var->green.offset != 5 || 6528c2ecf20Sopenharmony_ci var->green.length != 5 || 6538c2ecf20Sopenharmony_ci var->red.offset != 10 || 6548c2ecf20Sopenharmony_ci var->red.length != 5 || 6558c2ecf20Sopenharmony_ci var->transp.offset != 15 || var->transp.length != 1) { 6568c2ecf20Sopenharmony_ci vmlfb_set_pref_pixel_format(&v); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci break; 6598c2ecf20Sopenharmony_ci case 32: 6608c2ecf20Sopenharmony_ci if (var->blue.offset != 0 || 6618c2ecf20Sopenharmony_ci var->blue.length != 8 || 6628c2ecf20Sopenharmony_ci var->green.offset != 8 || 6638c2ecf20Sopenharmony_ci var->green.length != 8 || 6648c2ecf20Sopenharmony_ci var->red.offset != 16 || 6658c2ecf20Sopenharmony_ci var->red.length != 8 || 6668c2ecf20Sopenharmony_ci (var->transp.length != 0 && var->transp.length != 8) || 6678c2ecf20Sopenharmony_ci (var->transp.length == 8 && var->transp.offset != 24)) { 6688c2ecf20Sopenharmony_ci vmlfb_set_pref_pixel_format(&v); 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci default: 6728c2ecf20Sopenharmony_ci return -EINVAL; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci *var = v; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic int vmlfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 6838c2ecf20Sopenharmony_ci int ret; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 6868c2ecf20Sopenharmony_ci ret = vmlfb_check_var_locked(var, vinfo); 6878c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return ret; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic void vml_wait_vblank(struct vml_info *vinfo) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ 6958c2ecf20Sopenharmony_ci mdelay(20); 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic void vmlfb_disable_pipe(struct vml_info *vinfo) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct vml_par *par = vinfo->par; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Disable the MDVO pad */ 7038c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_RCOMPSTAT, 0); 7048c2ecf20Sopenharmony_ci while (!(VML_READ32(par, VML_RCOMPSTAT) & VML_MDVO_VDC_I_RCOMP)) ; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* Disable display planes */ 7078c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPCCNTR, 7088c2ecf20Sopenharmony_ci VML_READ32(par, VML_DSPCCNTR) & ~VML_GFX_ENABLE); 7098c2ecf20Sopenharmony_ci (void)VML_READ32(par, VML_DSPCCNTR); 7108c2ecf20Sopenharmony_ci /* Wait for vblank for the disable to take effect */ 7118c2ecf20Sopenharmony_ci vml_wait_vblank(vinfo); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* Next, disable display pipes */ 7148c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, 0); 7158c2ecf20Sopenharmony_ci (void)VML_READ32(par, VML_PIPEACONF); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci vinfo->pipe_disabled = 1; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci#ifdef VERMILION_DEBUG 7218c2ecf20Sopenharmony_cistatic void vml_dump_regs(struct vml_info *vinfo) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct vml_par *par = vinfo->par; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Modesetting register dump:\n"); 7268c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tHTOTAL_A : 0x%08x\n", 7278c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_HTOTAL_A)); 7288c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tHBLANK_A : 0x%08x\n", 7298c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_HBLANK_A)); 7308c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tHSYNC_A : 0x%08x\n", 7318c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_HSYNC_A)); 7328c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tVTOTAL_A : 0x%08x\n", 7338c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_VTOTAL_A)); 7348c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tVBLANK_A : 0x%08x\n", 7358c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_VBLANK_A)); 7368c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tVSYNC_A : 0x%08x\n", 7378c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_VSYNC_A)); 7388c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCSTRIDE : 0x%08x\n", 7398c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCSTRIDE)); 7408c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCSIZE : 0x%08x\n", 7418c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCSIZE)); 7428c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCPOS : 0x%08x\n", 7438c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCPOS)); 7448c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPARB : 0x%08x\n", 7458c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPARB)); 7468c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCADDR : 0x%08x\n", 7478c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCADDR)); 7488c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tBCLRPAT_A : 0x%08x\n", 7498c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_BCLRPAT_A)); 7508c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tCANVSCLR_A : 0x%08x\n", 7518c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_CANVSCLR_A)); 7528c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tPIPEASRC : 0x%08x\n", 7538c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_PIPEASRC)); 7548c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tPIPEACONF : 0x%08x\n", 7558c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_PIPEACONF)); 7568c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tDSPCCNTR : 0x%08x\n", 7578c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_DSPCCNTR)); 7588c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": \tRCOMPSTAT : 0x%08x\n", 7598c2ecf20Sopenharmony_ci (unsigned)VML_READ32(par, VML_RCOMPSTAT)); 7608c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": End of modesetting register dump.\n"); 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci#endif 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic int vmlfb_set_par_locked(struct vml_info *vinfo) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct vml_par *par = vinfo->par; 7678c2ecf20Sopenharmony_ci struct fb_info *info = &vinfo->info; 7688c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 7698c2ecf20Sopenharmony_ci u32 htotal, hactive, hblank_start, hblank_end, hsync_start, hsync_end; 7708c2ecf20Sopenharmony_ci u32 vtotal, vactive, vblank_start, vblank_end, vsync_start, vsync_end; 7718c2ecf20Sopenharmony_ci u32 dspcntr; 7728c2ecf20Sopenharmony_ci int clock; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci vinfo->bytes_per_pixel = var->bits_per_pixel >> 3; 7758c2ecf20Sopenharmony_ci vinfo->stride = ALIGN(var->xres_virtual * vinfo->bytes_per_pixel, 0x40); 7768c2ecf20Sopenharmony_ci info->fix.line_length = vinfo->stride; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (!subsys) 7798c2ecf20Sopenharmony_ci return 0; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci htotal = 7828c2ecf20Sopenharmony_ci var->xres + var->right_margin + var->hsync_len + var->left_margin; 7838c2ecf20Sopenharmony_ci hactive = var->xres; 7848c2ecf20Sopenharmony_ci hblank_start = var->xres; 7858c2ecf20Sopenharmony_ci hblank_end = htotal; 7868c2ecf20Sopenharmony_ci hsync_start = hactive + var->right_margin; 7878c2ecf20Sopenharmony_ci hsync_end = hsync_start + var->hsync_len; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci vtotal = 7908c2ecf20Sopenharmony_ci var->yres + var->lower_margin + var->vsync_len + var->upper_margin; 7918c2ecf20Sopenharmony_ci vactive = var->yres; 7928c2ecf20Sopenharmony_ci vblank_start = var->yres; 7938c2ecf20Sopenharmony_ci vblank_end = vtotal; 7948c2ecf20Sopenharmony_ci vsync_start = vactive + var->lower_margin; 7958c2ecf20Sopenharmony_ci vsync_end = vsync_start + var->vsync_len; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci dspcntr = VML_GFX_ENABLE | VML_GFX_GAMMABYPASS; 7988c2ecf20Sopenharmony_ci clock = PICOS2KHZ(var->pixclock); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (subsys->nearest_clock) { 8018c2ecf20Sopenharmony_ci clock = subsys->nearest_clock(subsys, clock); 8028c2ecf20Sopenharmony_ci } else { 8038c2ecf20Sopenharmony_ci clock = vml_nearest_clock(clock); 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME 8068c2ecf20Sopenharmony_ci ": Set mode Hfreq : %d kHz, Vfreq : %d Hz.\n", clock / htotal, 8078c2ecf20Sopenharmony_ci ((clock / htotal) * 1000) / vtotal); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 8108c2ecf20Sopenharmony_ci case 16: 8118c2ecf20Sopenharmony_ci dspcntr |= VML_GFX_ARGB1555; 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci case 32: 8148c2ecf20Sopenharmony_ci if (var->transp.length == 8) 8158c2ecf20Sopenharmony_ci dspcntr |= VML_GFX_ARGB8888 | VML_GFX_ALPHAMULT; 8168c2ecf20Sopenharmony_ci else 8178c2ecf20Sopenharmony_ci dspcntr |= VML_GFX_RGB0888; 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci default: 8208c2ecf20Sopenharmony_ci return -EINVAL; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci vmlfb_disable_pipe(vinfo); 8248c2ecf20Sopenharmony_ci mb(); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (subsys->set_clock) 8278c2ecf20Sopenharmony_ci subsys->set_clock(subsys, clock); 8288c2ecf20Sopenharmony_ci else 8298c2ecf20Sopenharmony_ci return -EINVAL; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_HTOTAL_A, ((htotal - 1) << 16) | (hactive - 1)); 8328c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_HBLANK_A, 8338c2ecf20Sopenharmony_ci ((hblank_end - 1) << 16) | (hblank_start - 1)); 8348c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_HSYNC_A, 8358c2ecf20Sopenharmony_ci ((hsync_end - 1) << 16) | (hsync_start - 1)); 8368c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_VTOTAL_A, ((vtotal - 1) << 16) | (vactive - 1)); 8378c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_VBLANK_A, 8388c2ecf20Sopenharmony_ci ((vblank_end - 1) << 16) | (vblank_start - 1)); 8398c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_VSYNC_A, 8408c2ecf20Sopenharmony_ci ((vsync_end - 1) << 16) | (vsync_start - 1)); 8418c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPCSTRIDE, vinfo->stride); 8428c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPCSIZE, 8438c2ecf20Sopenharmony_ci ((var->yres - 1) << 16) | (var->xres - 1)); 8448c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPCPOS, 0x00000000); 8458c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPARB, VML_FIFO_DEFAULT); 8468c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_BCLRPAT_A, 0x00000000); 8478c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_CANVSCLR_A, 0x00000000); 8488c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_PIPEASRC, 8498c2ecf20Sopenharmony_ci ((var->xres - 1) << 16) | (var->yres - 1)); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci wmb(); 8528c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, VML_PIPE_ENABLE); 8538c2ecf20Sopenharmony_ci wmb(); 8548c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPCCNTR, dspcntr); 8558c2ecf20Sopenharmony_ci wmb(); 8568c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + 8578c2ecf20Sopenharmony_ci var->yoffset * vinfo->stride + 8588c2ecf20Sopenharmony_ci var->xoffset * vinfo->bytes_per_pixel); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_RCOMPSTAT, VML_MDVO_PAD_ENABLE); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci while (!(VML_READ32(par, VML_RCOMPSTAT) & 8638c2ecf20Sopenharmony_ci (VML_MDVO_VDC_I_RCOMP | VML_MDVO_PAD_ENABLE))) ; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci vinfo->pipe_disabled = 0; 8668c2ecf20Sopenharmony_ci#ifdef VERMILION_DEBUG 8678c2ecf20Sopenharmony_ci vml_dump_regs(vinfo); 8688c2ecf20Sopenharmony_ci#endif 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int vmlfb_set_par(struct fb_info *info) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 8768c2ecf20Sopenharmony_ci int ret; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 8798c2ecf20Sopenharmony_ci list_move(&vinfo->head, (subsys) ? &global_has_mode : &global_no_mode); 8808c2ecf20Sopenharmony_ci ret = vmlfb_set_par_locked(vinfo); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 8838c2ecf20Sopenharmony_ci return ret; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int vmlfb_blank_locked(struct vml_info *vinfo) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct vml_par *par = vinfo->par; 8898c2ecf20Sopenharmony_ci u32 cur = VML_READ32(par, VML_PIPEACONF); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci switch (vinfo->cur_blank_mode) { 8928c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 8938c2ecf20Sopenharmony_ci if (vinfo->pipe_disabled) { 8948c2ecf20Sopenharmony_ci vmlfb_set_par_locked(vinfo); 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, cur & ~VML_PIPE_FORCE_BORDER); 8978c2ecf20Sopenharmony_ci (void)VML_READ32(par, VML_PIPEACONF); 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 9008c2ecf20Sopenharmony_ci if (vinfo->pipe_disabled) { 9018c2ecf20Sopenharmony_ci vmlfb_set_par_locked(vinfo); 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_PIPEACONF, cur | VML_PIPE_FORCE_BORDER); 9048c2ecf20Sopenharmony_ci (void)VML_READ32(par, VML_PIPEACONF); 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 9078c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 9088c2ecf20Sopenharmony_ci if (!vinfo->pipe_disabled) { 9098c2ecf20Sopenharmony_ci vmlfb_disable_pipe(vinfo); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 9138c2ecf20Sopenharmony_ci if (!vinfo->pipe_disabled) { 9148c2ecf20Sopenharmony_ci vmlfb_disable_pipe(vinfo); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci default: 9188c2ecf20Sopenharmony_ci return -EINVAL; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci return 0; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic int vmlfb_blank(int blank_mode, struct fb_info *info) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 9278c2ecf20Sopenharmony_ci int ret; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 9308c2ecf20Sopenharmony_ci vinfo->cur_blank_mode = blank_mode; 9318c2ecf20Sopenharmony_ci ret = vmlfb_blank_locked(vinfo); 9328c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 9338c2ecf20Sopenharmony_ci return ret; 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic int vmlfb_pan_display(struct fb_var_screeninfo *var, 9378c2ecf20Sopenharmony_ci struct fb_info *info) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 9408c2ecf20Sopenharmony_ci struct vml_par *par = vinfo->par; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 9438c2ecf20Sopenharmony_ci VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + 9448c2ecf20Sopenharmony_ci var->yoffset * vinfo->stride + 9458c2ecf20Sopenharmony_ci var->xoffset * vinfo->bytes_per_pixel); 9468c2ecf20Sopenharmony_ci (void)VML_READ32(par, VML_DSPCADDR); 9478c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci return 0; 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 9538c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci u32 v; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (regno >= 16) 9588c2ecf20Sopenharmony_ci return -EINVAL; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (info->var.grayscale) { 9618c2ecf20Sopenharmony_ci red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (info->fix.visual != FB_VISUAL_TRUECOLOR) 9658c2ecf20Sopenharmony_ci return -EINVAL; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci red = VML_TOHW(red, info->var.red.length); 9688c2ecf20Sopenharmony_ci blue = VML_TOHW(blue, info->var.blue.length); 9698c2ecf20Sopenharmony_ci green = VML_TOHW(green, info->var.green.length); 9708c2ecf20Sopenharmony_ci transp = VML_TOHW(transp, info->var.transp.length); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci v = (red << info->var.red.offset) | 9738c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 9748c2ecf20Sopenharmony_ci (blue << info->var.blue.offset) | 9758c2ecf20Sopenharmony_ci (transp << info->var.transp.offset); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 9788c2ecf20Sopenharmony_ci case 16: 9798c2ecf20Sopenharmony_ci ((u32 *) info->pseudo_palette)[regno] = v; 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci case 24: 9828c2ecf20Sopenharmony_ci case 32: 9838c2ecf20Sopenharmony_ci ((u32 *) info->pseudo_palette)[regno] = v; 9848c2ecf20Sopenharmony_ci break; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct vml_info *vinfo = container_of(info, struct vml_info, info); 9928c2ecf20Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 9938c2ecf20Sopenharmony_ci int ret; 9948c2ecf20Sopenharmony_ci unsigned long prot; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci ret = vmlfb_vram_offset(vinfo, offset); 9978c2ecf20Sopenharmony_ci if (ret) 9988c2ecf20Sopenharmony_ci return -EINVAL; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci prot = pgprot_val(vma->vm_page_prot) & ~_PAGE_CACHE_MASK; 10018c2ecf20Sopenharmony_ci pgprot_val(vma->vm_page_prot) = 10028c2ecf20Sopenharmony_ci prot | cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return vm_iomap_memory(vma, vinfo->vram_start, 10058c2ecf20Sopenharmony_ci vinfo->vram_contig_size); 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic int vmlfb_sync(struct fb_info *info) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci return 0; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic int vmlfb_cursor(struct fb_info *info, struct fb_cursor *cursor) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci return -EINVAL; /* just to force soft_cursor() call */ 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic struct fb_ops vmlfb_ops = { 10198c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10208c2ecf20Sopenharmony_ci .fb_open = vmlfb_open, 10218c2ecf20Sopenharmony_ci .fb_release = vmlfb_release, 10228c2ecf20Sopenharmony_ci .fb_check_var = vmlfb_check_var, 10238c2ecf20Sopenharmony_ci .fb_set_par = vmlfb_set_par, 10248c2ecf20Sopenharmony_ci .fb_blank = vmlfb_blank, 10258c2ecf20Sopenharmony_ci .fb_pan_display = vmlfb_pan_display, 10268c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 10278c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 10288c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 10298c2ecf20Sopenharmony_ci .fb_cursor = vmlfb_cursor, 10308c2ecf20Sopenharmony_ci .fb_sync = vmlfb_sync, 10318c2ecf20Sopenharmony_ci .fb_mmap = vmlfb_mmap, 10328c2ecf20Sopenharmony_ci .fb_setcolreg = vmlfb_setcolreg 10338c2ecf20Sopenharmony_ci}; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic const struct pci_device_id vml_ids[] = { 10368c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, VML_DEVICE_VDC)}, 10378c2ecf20Sopenharmony_ci {0} 10388c2ecf20Sopenharmony_ci}; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic struct pci_driver vmlfb_pci_driver = { 10418c2ecf20Sopenharmony_ci .name = "vmlfb", 10428c2ecf20Sopenharmony_ci .id_table = vml_ids, 10438c2ecf20Sopenharmony_ci .probe = vml_pci_probe, 10448c2ecf20Sopenharmony_ci .remove = vml_pci_remove, 10458c2ecf20Sopenharmony_ci}; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic void __exit vmlfb_cleanup(void) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci pci_unregister_driver(&vmlfb_pci_driver); 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic int __init vmlfb_init(void) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci#ifndef MODULE 10568c2ecf20Sopenharmony_ci char *option = NULL; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (fb_get_options(MODULE_NAME, &option)) 10598c2ecf20Sopenharmony_ci return -ENODEV; 10608c2ecf20Sopenharmony_ci#endif 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": initializing\n"); 10638c2ecf20Sopenharmony_ci mutex_init(&vml_mutex); 10648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&global_no_mode); 10658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&global_has_mode); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return pci_register_driver(&vmlfb_pci_driver); 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ciint vmlfb_register_subsys(struct vml_sys *sys) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci struct vml_info *entry; 10738c2ecf20Sopenharmony_ci struct list_head *list; 10748c2ecf20Sopenharmony_ci u32 save_activate; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 10778c2ecf20Sopenharmony_ci if (subsys != NULL) { 10788c2ecf20Sopenharmony_ci subsys->restore(subsys); 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci subsys = sys; 10818c2ecf20Sopenharmony_ci subsys->save(subsys); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* 10848c2ecf20Sopenharmony_ci * We need to restart list traversal for each item, since we 10858c2ecf20Sopenharmony_ci * release the list mutex in the loop. 10868c2ecf20Sopenharmony_ci */ 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci list = global_no_mode.next; 10898c2ecf20Sopenharmony_ci while (list != &global_no_mode) { 10908c2ecf20Sopenharmony_ci list_del_init(list); 10918c2ecf20Sopenharmony_ci entry = list_entry(list, struct vml_info, head); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* 10948c2ecf20Sopenharmony_ci * First, try the current mode which might not be 10958c2ecf20Sopenharmony_ci * completely validated with respect to the pixel clock. 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (!vmlfb_check_var_locked(&entry->info.var, entry)) { 10998c2ecf20Sopenharmony_ci vmlfb_set_par_locked(entry); 11008c2ecf20Sopenharmony_ci list_add_tail(list, &global_has_mode); 11018c2ecf20Sopenharmony_ci } else { 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* 11048c2ecf20Sopenharmony_ci * Didn't work. Try to find another mode, 11058c2ecf20Sopenharmony_ci * that matches this subsys. 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 11098c2ecf20Sopenharmony_ci save_activate = entry->info.var.activate; 11108c2ecf20Sopenharmony_ci entry->info.var.bits_per_pixel = 16; 11118c2ecf20Sopenharmony_ci vmlfb_set_pref_pixel_format(&entry->info.var); 11128c2ecf20Sopenharmony_ci if (fb_find_mode(&entry->info.var, 11138c2ecf20Sopenharmony_ci &entry->info, 11148c2ecf20Sopenharmony_ci vml_default_mode, NULL, 0, NULL, 16)) { 11158c2ecf20Sopenharmony_ci entry->info.var.activate |= 11168c2ecf20Sopenharmony_ci FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; 11178c2ecf20Sopenharmony_ci fb_set_var(&entry->info, &entry->info.var); 11188c2ecf20Sopenharmony_ci } else { 11198c2ecf20Sopenharmony_ci printk(KERN_ERR MODULE_NAME 11208c2ecf20Sopenharmony_ci ": Sorry. no mode found for this subsys.\n"); 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci entry->info.var.activate = save_activate; 11238c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci vmlfb_blank_locked(entry); 11268c2ecf20Sopenharmony_ci list = global_no_mode.next; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Registered %s subsystem.\n", 11318c2ecf20Sopenharmony_ci subsys->name ? subsys->name : "unknown"); 11328c2ecf20Sopenharmony_ci return 0; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmlfb_register_subsys); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_civoid vmlfb_unregister_subsys(struct vml_sys *sys) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct vml_info *entry, *next; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci mutex_lock(&vml_mutex); 11428c2ecf20Sopenharmony_ci if (subsys != sys) { 11438c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 11448c2ecf20Sopenharmony_ci return; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci subsys->restore(subsys); 11478c2ecf20Sopenharmony_ci subsys = NULL; 11488c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, next, &global_has_mode, head) { 11498c2ecf20Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": subsys disable pipe\n"); 11508c2ecf20Sopenharmony_ci vmlfb_disable_pipe(entry); 11518c2ecf20Sopenharmony_ci list_move_tail(&entry->head, &global_no_mode); 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci mutex_unlock(&vml_mutex); 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmlfb_unregister_subsys); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cimodule_init(vmlfb_init); 11598c2ecf20Sopenharmony_cimodule_exit(vmlfb_cleanup); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tungsten Graphics"); 11628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Initialization of the Vermilion display devices"); 11638c2ecf20Sopenharmony_ciMODULE_VERSION("1.0.0"); 11648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1165