/* * nvbacklight.c: Backlight driver for nVidia graphics cards * * Copyright 2008 Andy Wingo * Copyright 2004 Antonino Daplas * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PMAC_BACKLIGHT #include #endif struct nvbacklight_par { struct pci_dev *pci_dev; struct fb_info *info; struct backlight_device *bd; volatile u32 __iomem *REGS; volatile u32 __iomem *PCRTC0; volatile u32 __iomem *PCRTC; volatile u32 __iomem *PRAMDAC0; volatile u32 __iomem *PFB; volatile u32 __iomem *PFIFO; volatile u32 __iomem *PGRAPH; volatile u32 __iomem *PEXTDEV; volatile u32 __iomem *PTIMER; volatile u32 __iomem *PMC; volatile u32 __iomem *PRAMIN; volatile u32 __iomem *FIFO; volatile u32 __iomem *CURSOR; volatile u8 __iomem *PCIO0; volatile u8 __iomem *PCIO; volatile u8 __iomem *PVIO; volatile u8 __iomem *PDIO0; volatile u8 __iomem *PDIO; volatile u32 __iomem *PRAMDAC; u32 fpSyncs; }; /* We do not have any information about which values are allowed, thus * we used safe values. */ #define MIN_LEVEL 0x158 #define MAX_LEVEL 0x534 #define LEVEL_STEP ((MAX_LEVEL - MIN_LEVEL) / FB_BACKLIGHT_MAX) #define NV_WR32(p,i,d) (__raw_writel((d), (void __iomem *)(p) + (i))) #define NV_RD32(p,i) (__raw_readl((void __iomem *)(p) + (i))) static int nvidia_bl_get_level_brightness(struct fb_info *info, int level) { int nlevel; /* Get and convert the value */ /* No locking of bl_curve since we read a single value */ nlevel = MIN_LEVEL + info->bl_curve[level] * LEVEL_STEP; if (nlevel < 0) nlevel = 0; else if (nlevel < MIN_LEVEL) nlevel = MIN_LEVEL; else if (nlevel > MAX_LEVEL) nlevel = MAX_LEVEL; return nlevel; } static int nvidia_bl_update_status(struct backlight_device *bd) { struct nvbacklight_par *par = bl_get_data(bd); u32 tmp_pcrt, tmp_pmc, fpcontrol; int level; if (bd->props.power != FB_BLANK_UNBLANK || bd->props.fb_blank != FB_BLANK_UNBLANK) level = 0; else level = bd->props.brightness; tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF; tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC; fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC; if (level > 0) { tmp_pcrt |= 0x1; tmp_pmc |= (1 << 31); /* backlight bit */ tmp_pmc |= nvidia_bl_get_level_brightness(par->info, level) << 16; fpcontrol |= par->fpSyncs; } else fpcontrol |= 0x20000022; NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt); NV_WR32(par->PMC, 0x10F0, tmp_pmc); NV_WR32(par->PRAMDAC, 0x848, fpcontrol); return 0; } static int nvidia_bl_get_brightness(struct backlight_device *bd) { return bd->props.brightness; } static struct backlight_ops nvidia_bl_ops = { .get_brightness = nvidia_bl_get_brightness, .update_status = nvidia_bl_update_status, }; static struct fb_info *nvbacklight_attach(struct pci_dev *pd) { struct nvbacklight_par *par; struct fb_info *info; struct backlight_device *bd; info = framebuffer_alloc(sizeof(struct nvbacklight_par), &pd->dev); if (!info) goto framebuffer_alloc_failed; par = info->par; par->pci_dev = pd; par->info = info; par->REGS = ioremap(pci_resource_start(pd, 0), pci_resource_len(pd, 0)); if (!par->REGS) { printk(KERN_ERR "nvbacklight: cannot ioremap MMIO base\n"); goto regs_map_failed; } par->PRAMIN = par->REGS + (0x00710000 / 4); par->PCRTC0 = par->REGS + (0x00600000 / 4); par->PRAMDAC0 = par->REGS + (0x00680000 / 4); par->PFB = par->REGS + (0x00100000 / 4); par->PFIFO = par->REGS + (0x00002000 / 4); par->PGRAPH = par->REGS + (0x00400000 / 4); par->PEXTDEV = par->REGS + (0x00101000 / 4); par->PTIMER = par->REGS + (0x00009000 / 4); par->PMC = par->REGS + (0x00000000 / 4); par->FIFO = par->REGS + (0x00800000 / 4); /* 8 bit registers */ par->PCIO0 = (u8 __iomem *) par->REGS + 0x00601000; par->PDIO0 = (u8 __iomem *) par->REGS + 0x00681000; par->PVIO = (u8 __iomem *) par->REGS + 0x000C0000; /* see nv_setup.c:NVSelectHeadRegisters */ par->PCIO = par->PCIO0; par->PCRTC = par->PCRTC0; par->PRAMDAC = par->PRAMDAC0; par->PDIO = par->PDIO0; par->fpSyncs = NV_RD32(par->PRAMDAC, 0x0848) & 0x30000033; bd = backlight_device_register("nvbacklight", &pd->dev, par, &nvidia_bl_ops); if (IS_ERR(bd)) { printk(KERN_WARNING "nvbacklight: registration failed\n"); goto bl_device_register_failed; } par->bd = bd; fb_bl_default_curve(info, 0, 0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL, 0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL); bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; bd->props.brightness = bd->props.max_brightness; bd->props.power = FB_BLANK_UNBLANK; backlight_update_status(bd); printk("nvbacklight: initialized\n"); return info; bl_device_register_failed: iounmap(par->REGS); regs_map_failed: framebuffer_release (info); framebuffer_alloc_failed: return NULL; } static void __devexit nvbacklight_detach(struct fb_info *info) { struct nvbacklight_par *par = info->par; backlight_device_unregister(par->bd); iounmap(par->REGS); pci_dev_put(par->pci_dev); framebuffer_release(info); printk("nvbacklight: Backlight unloaded\n"); } static struct fb_info *bl_fb_info; static int __init nvbacklight_init(void) { struct pci_dev *pd = NULL; pd = pci_get_device (PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, pd); if (pd) bl_fb_info = nvbacklight_attach(pd); return 0; } static void __exit nvbacklight_exit(void) { if (bl_fb_info) { nvbacklight_detach (bl_fb_info); bl_fb_info = NULL; } } module_init(nvbacklight_init); module_exit(nvbacklight_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andy Wingo"); MODULE_DESCRIPTION("Backlight control for nVidia graphics chipset");