1dbe7e429SAlan Hourihane /* 2dbe7e429SAlan Hourihane * Copyright (c) Intel Corp. 2007. 3dbe7e429SAlan Hourihane * All Rights Reserved. 4dbe7e429SAlan Hourihane * 5dbe7e429SAlan Hourihane * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to 6dbe7e429SAlan Hourihane * develop this driver. 7dbe7e429SAlan Hourihane * 8dbe7e429SAlan Hourihane * This file is part of the Carillo Ranch video subsystem driver. 9dbe7e429SAlan Hourihane * The Carillo Ranch video subsystem driver is free software; 10dbe7e429SAlan Hourihane * you can redistribute it and/or modify 11dbe7e429SAlan Hourihane * it under the terms of the GNU General Public License as published by 12dbe7e429SAlan Hourihane * the Free Software Foundation; either version 2 of the License, or 13dbe7e429SAlan Hourihane * (at your option) any later version. 14dbe7e429SAlan Hourihane * 15dbe7e429SAlan Hourihane * The Carillo Ranch video subsystem driver is distributed 16dbe7e429SAlan Hourihane * in the hope that it will be useful, 17dbe7e429SAlan Hourihane * but WITHOUT ANY WARRANTY; without even the implied warranty of 18dbe7e429SAlan Hourihane * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19dbe7e429SAlan Hourihane * GNU General Public License for more details. 20dbe7e429SAlan Hourihane * 21dbe7e429SAlan Hourihane * You should have received a copy of the GNU General Public License 22dbe7e429SAlan Hourihane * along with this driver; if not, write to the Free Software 23dbe7e429SAlan Hourihane * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24dbe7e429SAlan Hourihane * 25dbe7e429SAlan Hourihane * Authors: 26dbe7e429SAlan Hourihane * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> 27dbe7e429SAlan Hourihane * Alan Hourihane <alanh-at-tungstengraphics-dot-com> 28dbe7e429SAlan Hourihane */ 29dbe7e429SAlan Hourihane 30dbe7e429SAlan Hourihane #include <linux/module.h> 31dbe7e429SAlan Hourihane #include <linux/kernel.h> 32dbe7e429SAlan Hourihane #include <linux/init.h> 33dbe7e429SAlan Hourihane #include <linux/platform_device.h> 34dbe7e429SAlan Hourihane #include <linux/mutex.h> 35dbe7e429SAlan Hourihane #include <linux/fb.h> 36dbe7e429SAlan Hourihane #include <linux/backlight.h> 37dbe7e429SAlan Hourihane #include <linux/lcd.h> 38dbe7e429SAlan Hourihane #include <linux/pci.h> 395a0e3ad6STejun Heo #include <linux/slab.h> 40dbe7e429SAlan Hourihane 41dbe7e429SAlan Hourihane /* The LVDS- and panel power controls sits on the 42dbe7e429SAlan Hourihane * GPIO port of the ISA bridge. 43dbe7e429SAlan Hourihane */ 44dbe7e429SAlan Hourihane 45dbe7e429SAlan Hourihane #define CRVML_DEVICE_LPC 0x27B8 46dbe7e429SAlan Hourihane #define CRVML_REG_GPIOBAR 0x48 47dbe7e429SAlan Hourihane #define CRVML_REG_GPIOEN 0x4C 48dbe7e429SAlan Hourihane #define CRVML_GPIOEN_BIT (1 << 4) 49dbe7e429SAlan Hourihane #define CRVML_PANEL_PORT 0x38 50dbe7e429SAlan Hourihane #define CRVML_LVDS_ON 0x00000001 51dbe7e429SAlan Hourihane #define CRVML_PANEL_ON 0x00000002 52dbe7e429SAlan Hourihane #define CRVML_BACKLIGHT_OFF 0x00000004 53dbe7e429SAlan Hourihane 54dbe7e429SAlan Hourihane /* The PLL Clock register sits on Host bridge */ 55dbe7e429SAlan Hourihane #define CRVML_DEVICE_MCH 0x5001 56dbe7e429SAlan Hourihane #define CRVML_REG_MCHBAR 0x44 57dbe7e429SAlan Hourihane #define CRVML_REG_MCHEN 0x54 58dbe7e429SAlan Hourihane #define CRVML_MCHEN_BIT (1 << 28) 59dbe7e429SAlan Hourihane #define CRVML_MCHMAP_SIZE 4096 60dbe7e429SAlan Hourihane #define CRVML_REG_CLOCK 0xc3c 61dbe7e429SAlan Hourihane #define CRVML_CLOCK_SHIFT 8 62dbe7e429SAlan Hourihane #define CRVML_CLOCK_MASK 0x00000f00 63dbe7e429SAlan Hourihane 64dbe7e429SAlan Hourihane static struct pci_dev *lpc_dev; 65dbe7e429SAlan Hourihane static u32 gpio_bar; 66dbe7e429SAlan Hourihane 67dbe7e429SAlan Hourihane struct cr_panel { 68dbe7e429SAlan Hourihane struct backlight_device *cr_backlight_device; 69dbe7e429SAlan Hourihane struct lcd_device *cr_lcd_device; 70dbe7e429SAlan Hourihane }; 71dbe7e429SAlan Hourihane 72dbe7e429SAlan Hourihane static int cr_backlight_set_intensity(struct backlight_device *bd) 73dbe7e429SAlan Hourihane { 74dbe7e429SAlan Hourihane int intensity = bd->props.brightness; 75dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 76dbe7e429SAlan Hourihane u32 cur = inl(addr); 77dbe7e429SAlan Hourihane 78dbe7e429SAlan Hourihane if (bd->props.power == FB_BLANK_UNBLANK) 79dbe7e429SAlan Hourihane intensity = FB_BLANK_UNBLANK; 80dbe7e429SAlan Hourihane if (bd->props.fb_blank == FB_BLANK_UNBLANK) 81dbe7e429SAlan Hourihane intensity = FB_BLANK_UNBLANK; 82dbe7e429SAlan Hourihane if (bd->props.power == FB_BLANK_POWERDOWN) 83dbe7e429SAlan Hourihane intensity = FB_BLANK_POWERDOWN; 84dbe7e429SAlan Hourihane if (bd->props.fb_blank == FB_BLANK_POWERDOWN) 85dbe7e429SAlan Hourihane intensity = FB_BLANK_POWERDOWN; 86dbe7e429SAlan Hourihane 87dbe7e429SAlan Hourihane if (intensity == FB_BLANK_UNBLANK) { /* FULL ON */ 88dbe7e429SAlan Hourihane cur &= ~CRVML_BACKLIGHT_OFF; 89dbe7e429SAlan Hourihane outl(cur, addr); 90dbe7e429SAlan Hourihane } else if (intensity == FB_BLANK_POWERDOWN) { /* OFF */ 91dbe7e429SAlan Hourihane cur |= CRVML_BACKLIGHT_OFF; 92dbe7e429SAlan Hourihane outl(cur, addr); 93dbe7e429SAlan Hourihane } /* anything else, don't bother */ 94dbe7e429SAlan Hourihane 95dbe7e429SAlan Hourihane return 0; 96dbe7e429SAlan Hourihane } 97dbe7e429SAlan Hourihane 98dbe7e429SAlan Hourihane static int cr_backlight_get_intensity(struct backlight_device *bd) 99dbe7e429SAlan Hourihane { 100dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 101dbe7e429SAlan Hourihane u32 cur = inl(addr); 102dbe7e429SAlan Hourihane u8 intensity; 103dbe7e429SAlan Hourihane 104dbe7e429SAlan Hourihane if (cur & CRVML_BACKLIGHT_OFF) 105dbe7e429SAlan Hourihane intensity = FB_BLANK_POWERDOWN; 106dbe7e429SAlan Hourihane else 107dbe7e429SAlan Hourihane intensity = FB_BLANK_UNBLANK; 108dbe7e429SAlan Hourihane 109dbe7e429SAlan Hourihane return intensity; 110dbe7e429SAlan Hourihane } 111dbe7e429SAlan Hourihane 1129905a43bSEmese Revfy static const struct backlight_ops cr_backlight_ops = { 113dbe7e429SAlan Hourihane .get_brightness = cr_backlight_get_intensity, 114dbe7e429SAlan Hourihane .update_status = cr_backlight_set_intensity, 115dbe7e429SAlan Hourihane }; 116dbe7e429SAlan Hourihane 117dbe7e429SAlan Hourihane static void cr_panel_on(void) 118dbe7e429SAlan Hourihane { 119dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 120dbe7e429SAlan Hourihane u32 cur = inl(addr); 121dbe7e429SAlan Hourihane 122dbe7e429SAlan Hourihane if (!(cur & CRVML_PANEL_ON)) { 123dbe7e429SAlan Hourihane /* Make sure LVDS controller is down. */ 124dbe7e429SAlan Hourihane if (cur & 0x00000001) { 125dbe7e429SAlan Hourihane cur &= ~CRVML_LVDS_ON; 126dbe7e429SAlan Hourihane outl(cur, addr); 127dbe7e429SAlan Hourihane } 128dbe7e429SAlan Hourihane /* Power up Panel */ 129dbe7e429SAlan Hourihane schedule_timeout(HZ / 10); 130dbe7e429SAlan Hourihane cur |= CRVML_PANEL_ON; 131dbe7e429SAlan Hourihane outl(cur, addr); 132dbe7e429SAlan Hourihane } 133dbe7e429SAlan Hourihane 134dbe7e429SAlan Hourihane /* Power up LVDS controller */ 135dbe7e429SAlan Hourihane 136dbe7e429SAlan Hourihane if (!(cur & CRVML_LVDS_ON)) { 137dbe7e429SAlan Hourihane schedule_timeout(HZ / 10); 138dbe7e429SAlan Hourihane outl(cur | CRVML_LVDS_ON, addr); 139dbe7e429SAlan Hourihane } 140dbe7e429SAlan Hourihane } 141dbe7e429SAlan Hourihane 142dbe7e429SAlan Hourihane static void cr_panel_off(void) 143dbe7e429SAlan Hourihane { 144dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 145dbe7e429SAlan Hourihane u32 cur = inl(addr); 146dbe7e429SAlan Hourihane 147dbe7e429SAlan Hourihane /* Power down LVDS controller first to avoid high currents */ 148dbe7e429SAlan Hourihane if (cur & CRVML_LVDS_ON) { 149dbe7e429SAlan Hourihane cur &= ~CRVML_LVDS_ON; 150dbe7e429SAlan Hourihane outl(cur, addr); 151dbe7e429SAlan Hourihane } 152dbe7e429SAlan Hourihane if (cur & CRVML_PANEL_ON) { 153dbe7e429SAlan Hourihane schedule_timeout(HZ / 10); 154dbe7e429SAlan Hourihane outl(cur & ~CRVML_PANEL_ON, addr); 155dbe7e429SAlan Hourihane } 156dbe7e429SAlan Hourihane } 157dbe7e429SAlan Hourihane 158dbe7e429SAlan Hourihane static int cr_lcd_set_power(struct lcd_device *ld, int power) 159dbe7e429SAlan Hourihane { 160dbe7e429SAlan Hourihane if (power == FB_BLANK_UNBLANK) 161dbe7e429SAlan Hourihane cr_panel_on(); 162dbe7e429SAlan Hourihane if (power == FB_BLANK_POWERDOWN) 163dbe7e429SAlan Hourihane cr_panel_off(); 164dbe7e429SAlan Hourihane 165dbe7e429SAlan Hourihane return 0; 166dbe7e429SAlan Hourihane } 167dbe7e429SAlan Hourihane 168dbe7e429SAlan Hourihane static struct lcd_ops cr_lcd_ops = { 169dbe7e429SAlan Hourihane .set_power = cr_lcd_set_power, 170dbe7e429SAlan Hourihane }; 171dbe7e429SAlan Hourihane 172dbe7e429SAlan Hourihane static int cr_backlight_probe(struct platform_device *pdev) 173dbe7e429SAlan Hourihane { 174a19a6ee6SMatthew Garrett struct backlight_properties props; 1750b75f2dfSJesper Juhl struct backlight_device *bdp; 1760b75f2dfSJesper Juhl struct lcd_device *ldp; 177dbe7e429SAlan Hourihane struct cr_panel *crp; 178dbe7e429SAlan Hourihane u8 dev_en; 179dbe7e429SAlan Hourihane 180dbe7e429SAlan Hourihane lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 181dbe7e429SAlan Hourihane CRVML_DEVICE_LPC, NULL); 182dbe7e429SAlan Hourihane if (!lpc_dev) { 183dbe7e429SAlan Hourihane printk("INTEL CARILLO RANCH LPC not found.\n"); 184dbe7e429SAlan Hourihane return -ENODEV; 185dbe7e429SAlan Hourihane } 186dbe7e429SAlan Hourihane 187dbe7e429SAlan Hourihane pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en); 188dbe7e429SAlan Hourihane if (!(dev_en & CRVML_GPIOEN_BIT)) { 189dbe7e429SAlan Hourihane printk(KERN_ERR 190dbe7e429SAlan Hourihane "Carillo Ranch GPIO device was not enabled.\n"); 191dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 192dbe7e429SAlan Hourihane return -ENODEV; 193dbe7e429SAlan Hourihane } 194dbe7e429SAlan Hourihane 195a19a6ee6SMatthew Garrett memset(&props, 0, sizeof(struct backlight_properties)); 196bb7ca747SMatthew Garrett props.type = BACKLIGHT_RAW; 197a19a6ee6SMatthew Garrett bdp = backlight_device_register("cr-backlight", &pdev->dev, NULL, 198a19a6ee6SMatthew Garrett &cr_backlight_ops, &props); 1990b75f2dfSJesper Juhl if (IS_ERR(bdp)) { 200dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 2010b75f2dfSJesper Juhl return PTR_ERR(bdp); 202dbe7e429SAlan Hourihane } 203dbe7e429SAlan Hourihane 2040b75f2dfSJesper Juhl ldp = lcd_device_register("cr-lcd", &pdev->dev, NULL, &cr_lcd_ops); 2050b75f2dfSJesper Juhl if (IS_ERR(ldp)) { 2060b75f2dfSJesper Juhl backlight_device_unregister(bdp); 207dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 2085b0582eaSRoel Kluin return PTR_ERR(ldp); 209dbe7e429SAlan Hourihane } 210dbe7e429SAlan Hourihane 211dbe7e429SAlan Hourihane pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, 212dbe7e429SAlan Hourihane &gpio_bar); 213dbe7e429SAlan Hourihane gpio_bar &= ~0x3F; 214dbe7e429SAlan Hourihane 2150b75f2dfSJesper Juhl crp = kzalloc(sizeof(*crp), GFP_KERNEL); 2160b75f2dfSJesper Juhl if (!crp) { 2170b75f2dfSJesper Juhl lcd_device_unregister(ldp); 2180b75f2dfSJesper Juhl backlight_device_unregister(bdp); 2190b75f2dfSJesper Juhl pci_dev_put(lpc_dev); 2200b75f2dfSJesper Juhl return -ENOMEM; 2210b75f2dfSJesper Juhl } 2220b75f2dfSJesper Juhl 2230b75f2dfSJesper Juhl crp->cr_backlight_device = bdp; 2240b75f2dfSJesper Juhl crp->cr_lcd_device = ldp; 225dbe7e429SAlan Hourihane crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; 226dbe7e429SAlan Hourihane crp->cr_backlight_device->props.brightness = 0; 227dbe7e429SAlan Hourihane cr_backlight_set_intensity(crp->cr_backlight_device); 228dbe7e429SAlan Hourihane cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK); 229dbe7e429SAlan Hourihane 230dbe7e429SAlan Hourihane platform_set_drvdata(pdev, crp); 231dbe7e429SAlan Hourihane 232dbe7e429SAlan Hourihane return 0; 233dbe7e429SAlan Hourihane } 234dbe7e429SAlan Hourihane 235dbe7e429SAlan Hourihane static int cr_backlight_remove(struct platform_device *pdev) 236dbe7e429SAlan Hourihane { 237dbe7e429SAlan Hourihane struct cr_panel *crp = platform_get_drvdata(pdev); 238dbe7e429SAlan Hourihane crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN; 239dbe7e429SAlan Hourihane crp->cr_backlight_device->props.brightness = 0; 240dbe7e429SAlan Hourihane crp->cr_backlight_device->props.max_brightness = 0; 241dbe7e429SAlan Hourihane cr_backlight_set_intensity(crp->cr_backlight_device); 242dbe7e429SAlan Hourihane cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN); 243dbe7e429SAlan Hourihane backlight_device_unregister(crp->cr_backlight_device); 244dbe7e429SAlan Hourihane lcd_device_unregister(crp->cr_lcd_device); 245dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 2465a2d6e31SAxel Lin kfree(crp); 247dbe7e429SAlan Hourihane 248dbe7e429SAlan Hourihane return 0; 249dbe7e429SAlan Hourihane } 250dbe7e429SAlan Hourihane 251dbe7e429SAlan Hourihane static struct platform_driver cr_backlight_driver = { 252dbe7e429SAlan Hourihane .probe = cr_backlight_probe, 253dbe7e429SAlan Hourihane .remove = cr_backlight_remove, 254dbe7e429SAlan Hourihane .driver = { 255dbe7e429SAlan Hourihane .name = "cr_backlight", 256dbe7e429SAlan Hourihane }, 257dbe7e429SAlan Hourihane }; 258dbe7e429SAlan Hourihane 259dbe7e429SAlan Hourihane static struct platform_device *crp; 260dbe7e429SAlan Hourihane 261dbe7e429SAlan Hourihane static int __init cr_backlight_init(void) 262dbe7e429SAlan Hourihane { 263dbe7e429SAlan Hourihane int ret = platform_driver_register(&cr_backlight_driver); 264dbe7e429SAlan Hourihane 265b4a11d3dSAkinobu Mita if (ret) 266b4a11d3dSAkinobu Mita return ret; 267dbe7e429SAlan Hourihane 268b4a11d3dSAkinobu Mita crp = platform_device_register_simple("cr_backlight", -1, NULL, 0); 269b4a11d3dSAkinobu Mita if (IS_ERR(crp)) { 270dbe7e429SAlan Hourihane platform_driver_unregister(&cr_backlight_driver); 271b4a11d3dSAkinobu Mita return PTR_ERR(crp); 272dbe7e429SAlan Hourihane } 273dbe7e429SAlan Hourihane 274dbe7e429SAlan Hourihane printk("Carillo Ranch Backlight Driver Initialized.\n"); 275dbe7e429SAlan Hourihane 276b4a11d3dSAkinobu Mita return 0; 277dbe7e429SAlan Hourihane } 278dbe7e429SAlan Hourihane 279dbe7e429SAlan Hourihane static void __exit cr_backlight_exit(void) 280dbe7e429SAlan Hourihane { 281dbe7e429SAlan Hourihane platform_device_unregister(crp); 282dbe7e429SAlan Hourihane platform_driver_unregister(&cr_backlight_driver); 283dbe7e429SAlan Hourihane } 284dbe7e429SAlan Hourihane 285dbe7e429SAlan Hourihane module_init(cr_backlight_init); 286dbe7e429SAlan Hourihane module_exit(cr_backlight_exit); 287dbe7e429SAlan Hourihane 288dbe7e429SAlan Hourihane MODULE_AUTHOR("Tungsten Graphics Inc."); 289dbe7e429SAlan Hourihane MODULE_DESCRIPTION("Carillo Ranch Backlight Driver"); 290dbe7e429SAlan Hourihane MODULE_LICENSE("GPL"); 291