1/*************************************************************************** 2 * Silicon Motion VoyagerGX framebuffer driver 3 * 4 * ported to 2.6 by Embedded Alley Solutions, Inc 5 * Copyright (C) 2005 Embedded Alley Solutions, Inc 6 * 7 * based on 8 copyright : (C) 2001 by Szu-Tao Huang 9 email : johuang@siliconmotion.com 10 Updated to SM501 by Eric.Devolder@amd.com and dan@embeddededge.com 11 for the AMD Mirage Portable Tablet. 20 Oct 2003 12 ***************************************************************************/ 13 14/*************************************************************************** 15 * * 16 * This program is free software; you can redistribute it and/or modify * 17 * it under the terms of the GNU General Public License as published by * 18 * the Free Software Foundation; either version 2 of the License, or * 19 * (at your option) any later version. * 20 * * 21 ***************************************************************************/ 22 23#include <linux/module.h> 24#include <linux/kernel.h> 25#include <linux/errno.h> 26#include <linux/string.h> 27#include <linux/mm.h> 28#include <linux/tty.h> 29#include <linux/slab.h> 30#include <linux/delay.h> 31#include <linux/fb.h> 32#include <linux/pci.h> 33#include <linux/init.h> 34 35#include <asm/io.h> 36#include <asm/irq.h> 37#include <asm/pgtable.h> 38#include <asm/system.h> 39#include <asm/uaccess.h> 40 41static char __iomem *SMIRegs; // point to virtual Memory Map IO starting address 42static char __iomem *SMILFB; // point to virtual video memory starting address 43 44static struct fb_fix_screeninfo smifb_fix __devinitdata = { 45 .id = "smivgx", 46 .type = FB_TYPE_PACKED_PIXELS, 47 .visual = FB_VISUAL_TRUECOLOR, 48 .ywrapstep = 0, 49 .line_length = 1024 * 2, /* (bbp * xres)/8 */ 50 .accel = FB_ACCEL_NONE, 51}; 52 53static struct fb_var_screeninfo smifb_var __devinitdata = { 54 .xres = 1024, 55 .yres = 768, 56 .xres_virtual = 1024, 57 .yres_virtual = 768, 58 .bits_per_pixel = 16, 59 .red = { 11, 5, 0 }, 60 .green = { 5, 6, 0 }, 61 .blue = { 0, 5, 0 }, 62 .activate = FB_ACTIVATE_NOW, 63 .height = -1, 64 .width = -1, 65 .vmode = FB_VMODE_NONINTERLACED, 66}; 67 68 69static struct fb_info info; 70 71#define smi_mmiowb(dat,reg) writeb(dat, (SMIRegs + reg)) 72#define smi_mmioww(dat,reg) writew(dat, (SMIRegs + reg)) 73#define smi_mmiowl(dat,reg) writel(dat, (SMIRegs + reg)) 74 75#define smi_mmiorb(reg) readb(SMIRegs + reg) 76#define smi_mmiorw(reg) readw(SMIRegs + reg) 77#define smi_mmiorl(reg) readl(SMIRegs + reg) 78 79/* Address space offsets for various control/status registers. 80*/ 81#define MISC_CTRL 0x000004 82#define GPIO_LO_CTRL 0x000008 83#define GPIO_HI_CTRL 0x00000c 84#define DRAM_CTRL 0x000010 85#define CURRENT_POWER_GATE 0x000038 86#define CURRENT_POWER_CLOCK 0x00003C 87#define POWER_MODE1_GATE 0x000048 88#define POWER_MODE1_CLOCK 0x00004C 89#define POWER_MODE_CTRL 0x000054 90 91#define GPIO_DATA_LO 0x010000 92#define GPIO_DATA_HI 0x010004 93#define GPIO_DATA_DIR_LO 0x010008 94#define GPIO_DATA_DIR_HI 0x01000c 95#define I2C_BYTE_COUNT 0x010040 96#define I2C_CONTROL 0x010041 97#define I2C_STATUS_RESET 0x010042 98#define I2C_SLAVE_ADDRESS 0x010043 99#define I2C_DATA 0x010044 100 101#define DE_COLOR_COMPARE 0x100020 102#define DE_COLOR_COMPARE_MASK 0x100024 103#define DE_MASKS 0x100028 104#define DE_WRAP 0x10004C 105 106#define PANEL_DISPLAY_CTRL 0x080000 107#define PANEL_PAN_CTRL 0x080004 108#define PANEL_COLOR_KEY 0x080008 109#define PANEL_FB_ADDRESS 0x08000C 110#define PANEL_FB_WIDTH 0x080010 111#define PANEL_WINDOW_WIDTH 0x080014 112#define PANEL_WINDOW_HEIGHT 0x080018 113#define PANEL_PLANE_TL 0x08001C 114#define PANEL_PLANE_BR 0x080020 115#define PANEL_HORIZONTAL_TOTAL 0x080024 116#define PANEL_HORIZONTAL_SYNC 0x080028 117#define PANEL_VERTICAL_TOTAL 0x08002C 118#define PANEL_VERTICAL_SYNC 0x080030 119#define PANEL_CURRENT_LINE 0x080034 120#define VIDEO_DISPLAY_CTRL 0x080040 121#define VIDEO_DISPLAY_FB0 0x080044 122#define VIDEO_DISPLAY_FBWIDTH 0x080048 123#define VIDEO_DISPLAY_FB0LAST 0x08004C 124#define VIDEO_DISPLAY_TL 0x080050 125#define VIDEO_DISPLAY_BR 0x080054 126#define VIDEO_SCALE 0x080058 127#define VIDEO_INITIAL_SCALE 0x08005C 128#define VIDEO_YUV_CONSTANTS 0x080060 129#define VIDEO_DISPLAY_FB1 0x080064 130#define VIDEO_DISPLAY_FB1LAST 0x080068 131#define VIDEO_ALPHA_CTRL 0x080080 132#define PANEL_HWC_ADDRESS 0x0800F0 133#define CRT_DISPLAY_CTRL 0x080200 134#define CRT_FB_ADDRESS 0x080204 135#define CRT_FB_WIDTH 0x080208 136#define CRT_HORIZONTAL_TOTAL 0x08020c 137#define CRT_HORIZONTAL_SYNC 0x080210 138#define CRT_VERTICAL_TOTAL 0x080214 139#define CRT_VERTICAL_SYNC 0x080218 140#define CRT_HWC_ADDRESS 0x080230 141#define CRT_HWC_LOCATION 0x080234 142 143#define ZV_CAPTURE_CTRL 0x090000 144#define ZV_CAPTURE_CLIP 0x090004 145#define ZV_CAPTURE_SIZE 0x090008 146#define ZV_CAPTURE_BUF0 0x09000c 147#define ZV_CAPTURE_BUF1 0x090010 148#define ZV_CAPTURE_OFFSET 0x090014 149#define ZV_FIFO_CTRL 0x090018 150 151#define waitforvsync() udelay(400) 152 153static int initdone = 0; 154static int crt_out = 1; 155 156 157static int 158smi_setcolreg(unsigned regno, unsigned red, unsigned green, 159 unsigned blue, unsigned transp, 160 struct fb_info *info) 161{ 162 if (regno > 255) 163 return 1; 164 165 ((u32 *)(info->pseudo_palette))[regno] = 166 ((red & 0xf800) >> 0) | 167 ((green & 0xfc00) >> 5) | 168 ((blue & 0xf800) >> 11); 169 170 return 0; 171} 172 173/* This function still needs lots of work to generically support 174 * different output devices (CRT or LCD) and resolutions. 175 * Currently hard-coded for 1024x768 LCD panel. 176 */ 177static void smi_setmode(void) 178{ 179 if (initdone) 180 return; 181 182 initdone = 1; 183 184 /* Just blast in some control values based upon the chip 185 * documentation. We use the internal memory, I don't know 186 * how to determine the amount available yet. 187 */ 188 smi_mmiowl(0x07F127C2, DRAM_CTRL); 189 smi_mmiowl(0x02000020, PANEL_HWC_ADDRESS); 190 smi_mmiowl(0x007FF800, PANEL_HWC_ADDRESS); 191 smi_mmiowl(0x00021827, POWER_MODE1_GATE); 192 smi_mmiowl(0x011A0A09, POWER_MODE1_CLOCK); 193 smi_mmiowl(0x00000001, POWER_MODE_CTRL); 194 smi_mmiowl(0x80000000, PANEL_FB_ADDRESS); 195 smi_mmiowl(0x08000800, PANEL_FB_WIDTH); 196 smi_mmiowl(0x04000000, PANEL_WINDOW_WIDTH); 197 smi_mmiowl(0x03000000, PANEL_WINDOW_HEIGHT); 198 smi_mmiowl(0x00000000, PANEL_PLANE_TL); 199 smi_mmiowl(0x02FF03FF, PANEL_PLANE_BR); 200 smi_mmiowl(0x05D003FF, PANEL_HORIZONTAL_TOTAL); 201 smi_mmiowl(0x00C80424, PANEL_HORIZONTAL_SYNC); 202 smi_mmiowl(0x032502FF, PANEL_VERTICAL_TOTAL); 203 smi_mmiowl(0x00060302, PANEL_VERTICAL_SYNC); 204 smi_mmiowl(0x00013905, PANEL_DISPLAY_CTRL); 205 smi_mmiowl(0x01013105, PANEL_DISPLAY_CTRL); 206 waitforvsync(); 207 smi_mmiowl(0x03013905, PANEL_DISPLAY_CTRL); 208 waitforvsync(); 209 smi_mmiowl(0x07013905, PANEL_DISPLAY_CTRL); 210 waitforvsync(); 211 smi_mmiowl(0x0F013905, PANEL_DISPLAY_CTRL); 212 smi_mmiowl(0x0002187F, POWER_MODE1_GATE); 213 smi_mmiowl(0x01011801, POWER_MODE1_CLOCK); 214 smi_mmiowl(0x00000001, POWER_MODE_CTRL); 215 216 smi_mmiowl(0x80000000, PANEL_FB_ADDRESS); 217 smi_mmiowl(0x00000000, PANEL_PAN_CTRL); 218 smi_mmiowl(0x00000000, PANEL_COLOR_KEY); 219 220 if (crt_out) { 221 /* Just sent the panel out to the CRT for now. 222 */ 223 smi_mmiowl(0x80000000, CRT_FB_ADDRESS); 224 smi_mmiowl(0x08000800, CRT_FB_WIDTH); 225 smi_mmiowl(0x05D003FF, CRT_HORIZONTAL_TOTAL); 226 smi_mmiowl(0x00C80424, CRT_HORIZONTAL_SYNC); 227 smi_mmiowl(0x032502FF, CRT_VERTICAL_TOTAL); 228 smi_mmiowl(0x00060302, CRT_VERTICAL_SYNC); 229 smi_mmiowl(0x007FF800, CRT_HWC_ADDRESS); 230 smi_mmiowl(0x00010305, CRT_DISPLAY_CTRL); 231 smi_mmiowl(0x00000001, MISC_CTRL); 232 } 233} 234 235/* 236 * Unmap in the memory mapped IO registers 237 * 238 */ 239 240static void __devinit smi_unmap_mmio(void) 241{ 242 if (SMIRegs) { 243 iounmap(SMIRegs); 244 SMIRegs = NULL; 245 } 246} 247 248 249/* 250 * Unmap in the screen memory 251 * 252 */ 253static void __devinit smi_unmap_smem(void) 254{ 255 if (SMILFB) { 256 iounmap(SMILFB); 257 SMILFB = NULL; 258 } 259} 260 261static void vgxfb_setup(char *options) 262{ 263 264 if (!options || !*options) 265 return; 266 267 /* The only thing I'm looking for right now is to disable a 268 * CRT output that mirrors the panel display. 269 */ 270 if (strcmp(options, "no_crt") == 0) 271 crt_out = 0; 272 273 return; 274} 275 276static struct fb_ops smifb_ops = { 277 .owner = THIS_MODULE, 278 .fb_setcolreg = smi_setcolreg, 279 .fb_fillrect = cfb_fillrect, 280 .fb_copyarea = cfb_copyarea, 281 .fb_imageblit = cfb_imageblit, 282}; 283 284static int __devinit vgx_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 285{ 286 int err; 287 288 /* Enable the chip. 289 */ 290 err = pci_enable_device(dev); 291 if (err) 292 return err; 293 294 295 /* Set up MMIO space. 296 */ 297 smifb_fix.mmio_start = pci_resource_start(dev,1); 298 smifb_fix.mmio_len = 0x00200000; 299 SMIRegs = ioremap(smifb_fix.mmio_start, smifb_fix.mmio_len); 300 301 /* Set up framebuffer. It's a 64M space, various amount of 302 * internal memory. I don't know how to determine the real 303 * amount of memory (yet). 304 */ 305 smifb_fix.smem_start = pci_resource_start(dev,0); 306 smifb_fix.smem_len = 0x00800000; 307 SMILFB = ioremap(smifb_fix.smem_start, smifb_fix.smem_len); 308 309 memset_io(SMILFB, 0, smifb_fix.smem_len); 310 311 info.screen_base = SMILFB; 312 info.fbops = &smifb_ops; 313 info.fix = smifb_fix; 314 315 info.flags = FBINFO_FLAG_DEFAULT; 316 317 info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); 318 if (!info.pseudo_palette) { 319 return -ENOMEM; 320 } 321 memset(info.pseudo_palette, 0, sizeof(u32) *16); 322 323 fb_alloc_cmap(&info.cmap,256,0); 324 325 smi_setmode(); 326 327 info.var = smifb_var; 328 329 if (register_framebuffer(&info) < 0) 330 goto failed; 331 332 return 0; 333 334failed: 335 smi_unmap_smem(); 336 smi_unmap_mmio(); 337 338 return err; 339} 340 341static void __devexit vgx_pci_remove(struct pci_dev *dev) 342{ 343 unregister_framebuffer(&info); 344 smi_unmap_smem(); 345 smi_unmap_mmio(); 346} 347 348static struct pci_device_id vgx_devices[] = { 349 {PCI_VENDOR_ID_SILICON_MOTION, PCI_DEVICE_ID_SM501_VOYAGER_GX_REV_AA, 350 PCI_ANY_ID, PCI_ANY_ID}, 351 {PCI_VENDOR_ID_SILICON_MOTION, PCI_DEVICE_ID_SM501_VOYAGER_GX_REV_B, 352 PCI_ANY_ID, PCI_ANY_ID}, 353 {0} 354}; 355 356MODULE_DEVICE_TABLE(pci, vgx_devices); 357 358static struct pci_driver vgxfb_pci_driver = { 359 .name = "vgxfb", 360 .id_table= vgx_devices, 361 .probe = vgx_pci_probe, 362 .remove = __devexit_p(vgx_pci_remove), 363}; 364 365static int __init vgxfb_init(void) 366{ 367 char *option = NULL; 368 369 if (fb_get_options("vgxfb", &option)) 370 return -ENODEV; 371 vgxfb_setup(option); 372 373 printk("Silicon Motion Inc. VOYAGER Init complete.\n"); 374 return pci_module_init(&vgxfb_pci_driver); 375} 376 377static void __exit vgxfb_exit(void) 378{ 379 pci_unregister_driver(&vgxfb_pci_driver); 380} 381 382module_init(vgxfb_init); 383module_exit(vgxfb_exit); 384 385MODULE_AUTHOR(""); 386MODULE_DESCRIPTION("Framebuffer driver for SMI Voyager"); 387MODULE_LICENSE("GPL"); 388