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