📄 savagefb_driver.c
字号:
else par->display_type = DISP_CRT; /* Check LCD panel parrmation */ if (par->display_type == DISP_LCD) { unsigned char cr6b = VGArCR( 0x6b, par); int panelX = (VGArSEQ (0x61, par) + ((VGArSEQ (0x66, par) & 0x02) << 7) + 1) * 8; int panelY = (VGArSEQ (0x69, par) + ((VGArSEQ (0x6e, par) & 0x70) << 4) + 1); char * sTechnology = "Unknown"; /* OK, I admit it. I don't know how to limit the max dot clock * for LCD panels of various sizes. I thought I copied the * formula from the BIOS, but many users have parrmed me of * my folly. * * Instead, I'll abandon any attempt to automatically limit the * clock, and add an LCDClock option to XF86Config. Some day, * I should come back to this. */ enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */ ActiveCRT = 0x01, ActiveLCD = 0x02, ActiveTV = 0x04, ActiveCRT2 = 0x20, ActiveDUO = 0x80 }; if ((VGArSEQ (0x39, par) & 0x03) == 0) { sTechnology = "TFT"; } else if ((VGArSEQ (0x30, par) & 0x01) == 0) { sTechnology = "DSTN"; } else { sTechnology = "STN"; } printk (KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n", panelX, panelY, sTechnology, cr6b & ActiveLCD ? "and active" : "but not active"); if( cr6b & ActiveLCD ) { /* * If the LCD is active and panel expansion is enabled, * we probably want to kill the HW cursor. */ printk (KERN_INFO "savagefb: Limiting video mode to " "%dx%d\n", panelX, panelY ); par->SavagePanelWidth = panelX; par->SavagePanelHeight = panelY; } else par->display_type = DISP_CRT; } savage_get_default_par (par); if( S3_SAVAGE4_SERIES(par->chip) ) { /* * The Savage4 and ProSavage have COB coherency bugs which * render the buffer useless. We disable it. */ par->cob_index = 2; par->cob_size = 0x8000 << par->cob_index; par->cob_offset = videoRambytes; } else { /* We use 128kB for the COB on all chips. */ par->cob_index = 7; par->cob_size = 0x400 << par->cob_index; par->cob_offset = videoRambytes - par->cob_size; } return videoRambytes;}static int __devinit savage_init_fb_info (struct fb_info *info, struct pci_dev *dev, const struct pci_device_id *id){ struct savagefb_par *par = (struct savagefb_par *)info->par; int err = 0; par->pcidev = dev; info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.type_aux = 0; info->fix.ypanstep = 1; info->fix.ywrapstep = 0; info->fix.accel = id->driver_data; switch (info->fix.accel) { case FB_ACCEL_SUPERSAVAGE: par->chip = S3_SUPERSAVAGE; snprintf (info->fix.id, 16, "SuperSavage"); break; case FB_ACCEL_SAVAGE4: par->chip = S3_SAVAGE4; snprintf (info->fix.id, 16, "Savage4"); break; case FB_ACCEL_SAVAGE3D: par->chip = S3_SAVAGE3D; snprintf (info->fix.id, 16, "Savage3D"); break; case FB_ACCEL_SAVAGE3D_MV: par->chip = S3_SAVAGE3D; snprintf (info->fix.id, 16, "Savage3D-MV"); break; case FB_ACCEL_SAVAGE2000: par->chip = S3_SAVAGE2000; snprintf (info->fix.id, 16, "Savage2000"); break; case FB_ACCEL_SAVAGE_MX_MV: par->chip = S3_SAVAGE_MX; snprintf (info->fix.id, 16, "Savage/MX-MV"); break; case FB_ACCEL_SAVAGE_MX: par->chip = S3_SAVAGE_MX; snprintf (info->fix.id, 16, "Savage/MX"); break; case FB_ACCEL_SAVAGE_IX_MV: par->chip = S3_SAVAGE_MX; snprintf (info->fix.id, 16, "Savage/IX-MV"); break; case FB_ACCEL_SAVAGE_IX: par->chip = S3_SAVAGE_MX; snprintf (info->fix.id, 16, "Savage/IX"); break; case FB_ACCEL_PROSAVAGE_PM: par->chip = S3_PROSAVAGE; snprintf (info->fix.id, 16, "ProSavagePM"); break; case FB_ACCEL_PROSAVAGE_KM: par->chip = S3_PROSAVAGE; snprintf (info->fix.id, 16, "ProSavageKM"); break; case FB_ACCEL_S3TWISTER_P: par->chip = S3_PROSAVAGE; snprintf (info->fix.id, 16, "TwisterP"); break; case FB_ACCEL_S3TWISTER_K: par->chip = S3_PROSAVAGE; snprintf (info->fix.id, 16, "TwisterK"); break; case FB_ACCEL_PROSAVAGE_DDR: par->chip = S3_PROSAVAGE; snprintf (info->fix.id, 16, "ProSavageDDR"); break; case FB_ACCEL_PROSAVAGE_DDRK: par->chip = S3_PROSAVAGE; snprintf (info->fix.id, 16, "ProSavage8"); break; } if (S3_SAVAGE3D_SERIES(par->chip)) { par->SavageWaitIdle = savage3D_waitidle; par->SavageWaitFifo = savage3D_waitfifo; } else if (S3_SAVAGE4_SERIES(par->chip) || S3_SUPERSAVAGE == par->chip) { par->SavageWaitIdle = savage4_waitidle; par->SavageWaitFifo = savage4_waitfifo; } else { par->SavageWaitIdle = savage2000_waitidle; par->SavageWaitFifo = savage2000_waitfifo; } info->var.nonstd = 0; info->var.activate = FB_ACTIVATE_NOW; info->var.width = -1; info->var.height = -1; info->var.accel_flags = 0; info->fbops = &savagefb_ops; info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN | FBINFO_HWACCEL_XPAN; info->pseudo_palette = par->pseudo_palette;#if defined(CONFIG_FB_SAVAGE_ACCEL) /* FIFO size + padding for commands */ info->pixmap.addr = kmalloc(8*1024, GFP_KERNEL); err = -ENOMEM; if (info->pixmap.addr) { memset(info->pixmap.addr, 0, 8*1024); info->pixmap.size = 8*1024; info->pixmap.scan_align = 4; info->pixmap.buf_align = 4; info->pixmap.access_align = 32; err = fb_alloc_cmap (&info->cmap, NR_PALETTE, 0); if (!err) info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT; }#endif return err;}/* --------------------------------------------------------------------- */static int __devinit savagefb_probe (struct pci_dev* dev, const struct pci_device_id* id){ struct fb_info *info; struct savagefb_par *par; u_int h_sync, v_sync; int err, lpitch; int video_len; DBG("savagefb_probe"); SavagePrintRegs(); info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev); if (!info) return -ENOMEM; par = info->par; err = pci_enable_device(dev); if (err) goto failed_enable; if ((err = pci_request_regions(dev, "savagefb"))) { printk(KERN_ERR "cannot request PCI regions\n"); goto failed_enable; } err = -ENOMEM; if ((err = savage_init_fb_info(info, dev, id))) goto failed_init; err = savage_map_mmio(info); if (err) goto failed_mmio; video_len = savage_init_hw(par); /* FIXME: cant be negative */ if (video_len < 0) { err = video_len; goto failed_mmio; } err = savage_map_video(info, video_len); if (err) goto failed_video; INIT_LIST_HEAD(&info->modelist);#if defined(CONFIG_FB_SAVAGE_I2C) savagefb_create_i2c_busses(info); savagefb_probe_i2c_connector(info, &par->edid); kfree(par->edid); fb_edid_to_monspecs(par->edid, &info->monspecs); fb_videomode_to_modelist(info->monspecs.modedb, info->monspecs.modedb_len, &info->modelist);#endif info->var = savagefb_var800x600x8; if (mode_option) { fb_find_mode(&info->var, info, mode_option, info->monspecs.modedb, info->monspecs.modedb_len, NULL, 8); } else if (info->monspecs.modedb != NULL) { struct fb_videomode *modedb; modedb = fb_find_best_display(&info->monspecs, &info->modelist); savage_update_var(&info->var, modedb); } /* maximize virtual vertical length */ lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3); info->var.yres_virtual = info->fix.smem_len/lpitch; if (info->var.yres_virtual < info->var.yres) goto failed;#if defined(CONFIG_FB_SAVAGE_ACCEL) /* * The clipping coordinates are masked with 0xFFF, so limit our * virtual resolutions to these sizes. */ if (info->var.yres_virtual > 0x1000) info->var.yres_virtual = 0x1000; if (info->var.xres_virtual > 0x1000) info->var.xres_virtual = 0x1000;#endif savagefb_check_var(&info->var, info); savagefb_set_fix(info); /* * Calculate the hsync and vsync frequencies. Note that * we split the 1e12 constant up so that we can preserve * the precision and fit the results into 32-bit registers. * (1953125000 * 512 = 1e12) */ h_sync = 1953125000 / info->var.pixclock; h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin + info->var.right_margin + info->var.hsync_len); v_sync = h_sync / (info->var.yres + info->var.upper_margin + info->var.lower_margin + info->var.vsync_len); printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": " "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n", info->fix.smem_len >> 10, info->var.xres, info->var.yres, h_sync / 1000, h_sync % 1000, v_sync); fb_destroy_modedb(info->monspecs.modedb); info->monspecs.modedb = NULL; err = register_framebuffer (info); if (err < 0) goto failed; printk (KERN_INFO "fb: S3 %s frame buffer device\n", info->fix.id); /* * Our driver data */ pci_set_drvdata(dev, info); return 0; failed:#ifdef CONFIG_FB_SAVAGE_I2C savagefb_delete_i2c_busses(info);#endif fb_alloc_cmap (&info->cmap, 0, 0); savage_unmap_video(info); failed_video: savage_unmap_mmio (info); failed_mmio: kfree(info->pixmap.addr); failed_init: pci_release_regions(dev); failed_enable: framebuffer_release(info); return err;}static void __devexit savagefb_remove (struct pci_dev *dev){ struct fb_info *info = (struct fb_info *)pci_get_drvdata(dev); DBG("savagefb_remove"); if (info) { /* * If unregister_framebuffer fails, then * we will be leaving hooks that could cause * oopsen laying around. */ if (unregister_framebuffer (info)) printk (KERN_WARNING "savagefb: danger danger! " "Oopsen imminent!\n");#ifdef CONFIG_FB_SAVAGE_I2C savagefb_delete_i2c_busses(info);#endif fb_alloc_cmap (&info->cmap, 0, 0); savage_unmap_video (info); savage_unmap_mmio (info); kfree(info->pixmap.addr); pci_release_regions(dev); framebuffer_release(info); /* * Ensure that the driver data is no longer * valid. */ pci_set_drvdata(dev, NULL); }}static int savagefb_suspend (struct pci_dev* dev, pm_message_t state){ struct fb_info *info = (struct fb_info *)pci_get_drvdata(dev); struct savagefb_par *par = (struct savagefb_par *)info->par; DBG("savagefb_suspend"); par->pm_state = state.event; /* * For PM_EVENT_FREEZE, do not power down so the console * can remain active. */ if (state.event == PM_EVENT_FREEZE) { dev->dev.power.power_state = state; return 0; } acquire_console_sem(); fb_set_suspend(info, 1); if (info->fbops->fb_sync) info->fbops->fb_sync(info); savagefb_blank(FB_BLANK_POWERDOWN, info); savage_disable_mmio(par); pci_save_state(dev); pci_disable_device(dev); pci_set_power_state(dev, pci_choose_state(dev, state)); release_console_sem(); return 0;}static int savagefb_resume (struct pci_dev* dev){ struct fb_info *info = (struct fb_info *)pci_get_drvdata(dev); struct savagefb_par *par = (struct savagefb_par *)info->par; int cur_state = par->pm_state; DBG("savage_resume"); par->pm_state = PM_EVENT_ON; /* * The adapter was not powered down coming back from a * PM_EVENT_FREEZE. */ if (cur_state == PM_EVENT_FREEZE) { pci_set_power_state(dev, PCI_D0); return 0; } acquire_console_sem(); pci_set_power_state(dev, PCI_D0); pci_restore_state(dev); if(pci_enable_device(dev)) DBG("err"); pci_set_master(dev); savage_enable_mmio(par); savage_init_hw(par); savagefb_set_par (info); savagefb_blank(FB_BLANK_UNBLANK, info); fb_set_suspend (info, 0); release_console_sem(); return 0;}static struct pci_device_id savagefb_devices[] __devinitdata = { {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV}, {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX}, {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM}, {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM}, {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P}, {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K}, {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR}, {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK}, {0, 0, 0, 0, 0, 0, 0}};MODULE_DEVICE_TABLE(pci, savagefb_devices);static struct pci_driver savagefb_driver = { .name = "savagefb", .id_table = savagefb_devices, .probe = savagefb_probe, .suspend = savagefb_suspend, .resume = savagefb_resume, .remove = __devexit_p(savagefb_remove)};/* **************************** exit-time only **************************** */static void __exit savage_done (void){ DBG("savage_done"); pci_unregister_driver (&savagefb_driver);}/* ************************* init in-kernel code ************************** */static int __init savagefb_setup(char *options){#ifndef MODULE char *this_opt; if (!options || !*options) return 0; while ((this_opt = strsep(&options, ",")) != NULL) { mode_option = this_opt; }#endif /* !MODULE */ return 0;}static int __init savagefb_init(void){ char *option; DBG("savagefb_init"); if (fb_get_options("savagefb", &option)) return -ENODEV; savagefb_setup(option); return pci_register_driver (&savagefb_driver);}module_init(savagefb_init);module_exit(savage_done);module_param(mode_option, charp, 0);MODULE_PARM_DESC(mode_option, "Specify initial video mode");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -