14ad91750SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2dbe7e429SAlan Hourihane /* 3dbe7e429SAlan Hourihane * Copyright (c) Intel Corp. 2007. 4dbe7e429SAlan Hourihane * All Rights Reserved. 5dbe7e429SAlan Hourihane * 6dbe7e429SAlan Hourihane * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to 7dbe7e429SAlan Hourihane * develop this driver. 8dbe7e429SAlan Hourihane * 9dbe7e429SAlan Hourihane * This file is part of the Carillo Ranch video subsystem driver. 10dbe7e429SAlan Hourihane * 11dbe7e429SAlan Hourihane * Authors: 12dbe7e429SAlan Hourihane * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> 13dbe7e429SAlan Hourihane * Alan Hourihane <alanh-at-tungstengraphics-dot-com> 14dbe7e429SAlan Hourihane */ 15dbe7e429SAlan Hourihane 1631e6432bSJingoo Han #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1731e6432bSJingoo Han 18dbe7e429SAlan Hourihane #include <linux/module.h> 19dbe7e429SAlan Hourihane #include <linux/kernel.h> 20dbe7e429SAlan Hourihane #include <linux/init.h> 21dbe7e429SAlan Hourihane #include <linux/platform_device.h> 22dbe7e429SAlan Hourihane #include <linux/mutex.h> 23dbe7e429SAlan Hourihane #include <linux/fb.h> 24dbe7e429SAlan Hourihane #include <linux/backlight.h> 25dbe7e429SAlan Hourihane #include <linux/lcd.h> 26dbe7e429SAlan Hourihane #include <linux/pci.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 28dbe7e429SAlan Hourihane 29dbe7e429SAlan Hourihane /* The LVDS- and panel power controls sits on the 30dbe7e429SAlan Hourihane * GPIO port of the ISA bridge. 31dbe7e429SAlan Hourihane */ 32dbe7e429SAlan Hourihane 33dbe7e429SAlan Hourihane #define CRVML_DEVICE_LPC 0x27B8 34dbe7e429SAlan Hourihane #define CRVML_REG_GPIOBAR 0x48 35dbe7e429SAlan Hourihane #define CRVML_REG_GPIOEN 0x4C 36dbe7e429SAlan Hourihane #define CRVML_GPIOEN_BIT (1 << 4) 37dbe7e429SAlan Hourihane #define CRVML_PANEL_PORT 0x38 38dbe7e429SAlan Hourihane #define CRVML_LVDS_ON 0x00000001 39dbe7e429SAlan Hourihane #define CRVML_PANEL_ON 0x00000002 40dbe7e429SAlan Hourihane #define CRVML_BACKLIGHT_OFF 0x00000004 41dbe7e429SAlan Hourihane 42dbe7e429SAlan Hourihane /* The PLL Clock register sits on Host bridge */ 43dbe7e429SAlan Hourihane #define CRVML_DEVICE_MCH 0x5001 44dbe7e429SAlan Hourihane #define CRVML_REG_MCHBAR 0x44 45dbe7e429SAlan Hourihane #define CRVML_REG_MCHEN 0x54 46dbe7e429SAlan Hourihane #define CRVML_MCHEN_BIT (1 << 28) 47dbe7e429SAlan Hourihane #define CRVML_MCHMAP_SIZE 4096 48dbe7e429SAlan Hourihane #define CRVML_REG_CLOCK 0xc3c 49dbe7e429SAlan Hourihane #define CRVML_CLOCK_SHIFT 8 50dbe7e429SAlan Hourihane #define CRVML_CLOCK_MASK 0x00000f00 51dbe7e429SAlan Hourihane 52dbe7e429SAlan Hourihane static struct pci_dev *lpc_dev; 53dbe7e429SAlan Hourihane static u32 gpio_bar; 54dbe7e429SAlan Hourihane 55dbe7e429SAlan Hourihane struct cr_panel { 56dbe7e429SAlan Hourihane struct backlight_device *cr_backlight_device; 57dbe7e429SAlan Hourihane struct lcd_device *cr_lcd_device; 58dbe7e429SAlan Hourihane }; 59dbe7e429SAlan Hourihane 60dbe7e429SAlan Hourihane static int cr_backlight_set_intensity(struct backlight_device *bd) 61dbe7e429SAlan Hourihane { 62dbe7e429SAlan Hourihane int intensity = bd->props.brightness; 63dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 64dbe7e429SAlan Hourihane u32 cur = inl(addr); 65dbe7e429SAlan Hourihane 6624d34617SSam Ravnborg if (backlight_get_brightness(bd) == 0) { 6724d34617SSam Ravnborg /* OFF */ 68dbe7e429SAlan Hourihane cur |= CRVML_BACKLIGHT_OFF; 69dbe7e429SAlan Hourihane outl(cur, addr); 7024d34617SSam Ravnborg } else { 7124d34617SSam Ravnborg /* FULL ON */ 7224d34617SSam Ravnborg cur &= ~CRVML_BACKLIGHT_OFF; 7324d34617SSam Ravnborg outl(cur, addr); 7424d34617SSam Ravnborg } 75dbe7e429SAlan Hourihane 76dbe7e429SAlan Hourihane return 0; 77dbe7e429SAlan Hourihane } 78dbe7e429SAlan Hourihane 79dbe7e429SAlan Hourihane static int cr_backlight_get_intensity(struct backlight_device *bd) 80dbe7e429SAlan Hourihane { 81dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 82dbe7e429SAlan Hourihane u32 cur = inl(addr); 83dbe7e429SAlan Hourihane u8 intensity; 84dbe7e429SAlan Hourihane 85dbe7e429SAlan Hourihane if (cur & CRVML_BACKLIGHT_OFF) 8624d34617SSam Ravnborg intensity = 0; 87dbe7e429SAlan Hourihane else 8824d34617SSam Ravnborg intensity = 1; 89dbe7e429SAlan Hourihane 90dbe7e429SAlan Hourihane return intensity; 91dbe7e429SAlan Hourihane } 92dbe7e429SAlan Hourihane 939905a43bSEmese Revfy static const struct backlight_ops cr_backlight_ops = { 94dbe7e429SAlan Hourihane .get_brightness = cr_backlight_get_intensity, 95dbe7e429SAlan Hourihane .update_status = cr_backlight_set_intensity, 96dbe7e429SAlan Hourihane }; 97dbe7e429SAlan Hourihane 98dbe7e429SAlan Hourihane static void cr_panel_on(void) 99dbe7e429SAlan Hourihane { 100dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 101dbe7e429SAlan Hourihane u32 cur = inl(addr); 102dbe7e429SAlan Hourihane 103dbe7e429SAlan Hourihane if (!(cur & CRVML_PANEL_ON)) { 104dbe7e429SAlan Hourihane /* Make sure LVDS controller is down. */ 105dbe7e429SAlan Hourihane if (cur & 0x00000001) { 106dbe7e429SAlan Hourihane cur &= ~CRVML_LVDS_ON; 107dbe7e429SAlan Hourihane outl(cur, addr); 108dbe7e429SAlan Hourihane } 109dbe7e429SAlan Hourihane /* Power up Panel */ 110dbe7e429SAlan Hourihane schedule_timeout(HZ / 10); 111dbe7e429SAlan Hourihane cur |= CRVML_PANEL_ON; 112dbe7e429SAlan Hourihane outl(cur, addr); 113dbe7e429SAlan Hourihane } 114dbe7e429SAlan Hourihane 115dbe7e429SAlan Hourihane /* Power up LVDS controller */ 116dbe7e429SAlan Hourihane 117dbe7e429SAlan Hourihane if (!(cur & CRVML_LVDS_ON)) { 118dbe7e429SAlan Hourihane schedule_timeout(HZ / 10); 119dbe7e429SAlan Hourihane outl(cur | CRVML_LVDS_ON, addr); 120dbe7e429SAlan Hourihane } 121dbe7e429SAlan Hourihane } 122dbe7e429SAlan Hourihane 123dbe7e429SAlan Hourihane static void cr_panel_off(void) 124dbe7e429SAlan Hourihane { 125dbe7e429SAlan Hourihane u32 addr = gpio_bar + CRVML_PANEL_PORT; 126dbe7e429SAlan Hourihane u32 cur = inl(addr); 127dbe7e429SAlan Hourihane 128dbe7e429SAlan Hourihane /* Power down LVDS controller first to avoid high currents */ 129dbe7e429SAlan Hourihane if (cur & CRVML_LVDS_ON) { 130dbe7e429SAlan Hourihane cur &= ~CRVML_LVDS_ON; 131dbe7e429SAlan Hourihane outl(cur, addr); 132dbe7e429SAlan Hourihane } 133dbe7e429SAlan Hourihane if (cur & CRVML_PANEL_ON) { 134dbe7e429SAlan Hourihane schedule_timeout(HZ / 10); 135dbe7e429SAlan Hourihane outl(cur & ~CRVML_PANEL_ON, addr); 136dbe7e429SAlan Hourihane } 137dbe7e429SAlan Hourihane } 138dbe7e429SAlan Hourihane 139dbe7e429SAlan Hourihane static int cr_lcd_set_power(struct lcd_device *ld, int power) 140dbe7e429SAlan Hourihane { 141dbe7e429SAlan Hourihane if (power == FB_BLANK_UNBLANK) 142dbe7e429SAlan Hourihane cr_panel_on(); 143dbe7e429SAlan Hourihane if (power == FB_BLANK_POWERDOWN) 144dbe7e429SAlan Hourihane cr_panel_off(); 145dbe7e429SAlan Hourihane 146dbe7e429SAlan Hourihane return 0; 147dbe7e429SAlan Hourihane } 148dbe7e429SAlan Hourihane 149dbe7e429SAlan Hourihane static struct lcd_ops cr_lcd_ops = { 150dbe7e429SAlan Hourihane .set_power = cr_lcd_set_power, 151dbe7e429SAlan Hourihane }; 152dbe7e429SAlan Hourihane 153dbe7e429SAlan Hourihane static int cr_backlight_probe(struct platform_device *pdev) 154dbe7e429SAlan Hourihane { 155a19a6ee6SMatthew Garrett struct backlight_properties props; 1560b75f2dfSJesper Juhl struct backlight_device *bdp; 1570b75f2dfSJesper Juhl struct lcd_device *ldp; 158dbe7e429SAlan Hourihane struct cr_panel *crp; 159dbe7e429SAlan Hourihane u8 dev_en; 160dbe7e429SAlan Hourihane 161dbe7e429SAlan Hourihane lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 162dbe7e429SAlan Hourihane CRVML_DEVICE_LPC, NULL); 163dbe7e429SAlan Hourihane if (!lpc_dev) { 16431e6432bSJingoo Han pr_err("INTEL CARILLO RANCH LPC not found.\n"); 165dbe7e429SAlan Hourihane return -ENODEV; 166dbe7e429SAlan Hourihane } 167dbe7e429SAlan Hourihane 168dbe7e429SAlan Hourihane pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en); 169dbe7e429SAlan Hourihane if (!(dev_en & CRVML_GPIOEN_BIT)) { 17031e6432bSJingoo Han pr_err("Carillo Ranch GPIO device was not enabled.\n"); 171dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 172dbe7e429SAlan Hourihane return -ENODEV; 173dbe7e429SAlan Hourihane } 174dbe7e429SAlan Hourihane 175a19a6ee6SMatthew Garrett memset(&props, 0, sizeof(struct backlight_properties)); 176bb7ca747SMatthew Garrett props.type = BACKLIGHT_RAW; 177289a65e4SJingoo Han bdp = devm_backlight_device_register(&pdev->dev, "cr-backlight", 178289a65e4SJingoo Han &pdev->dev, NULL, &cr_backlight_ops, 179289a65e4SJingoo Han &props); 1800b75f2dfSJesper Juhl if (IS_ERR(bdp)) { 181dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 1820b75f2dfSJesper Juhl return PTR_ERR(bdp); 183dbe7e429SAlan Hourihane } 184dbe7e429SAlan Hourihane 185289a65e4SJingoo Han ldp = devm_lcd_device_register(&pdev->dev, "cr-lcd", &pdev->dev, NULL, 186289a65e4SJingoo Han &cr_lcd_ops); 1870b75f2dfSJesper Juhl if (IS_ERR(ldp)) { 188dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 1895b0582eaSRoel Kluin return PTR_ERR(ldp); 190dbe7e429SAlan Hourihane } 191dbe7e429SAlan Hourihane 192dbe7e429SAlan Hourihane pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, 193dbe7e429SAlan Hourihane &gpio_bar); 194dbe7e429SAlan Hourihane gpio_bar &= ~0x3F; 195dbe7e429SAlan Hourihane 196ce969228SJulia Lawall crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL); 1970b75f2dfSJesper Juhl if (!crp) { 1980b75f2dfSJesper Juhl pci_dev_put(lpc_dev); 1990b75f2dfSJesper Juhl return -ENOMEM; 2000b75f2dfSJesper Juhl } 2010b75f2dfSJesper Juhl 2020b75f2dfSJesper Juhl crp->cr_backlight_device = bdp; 2030b75f2dfSJesper Juhl crp->cr_lcd_device = ldp; 204dbe7e429SAlan Hourihane crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; 205dbe7e429SAlan Hourihane crp->cr_backlight_device->props.brightness = 0; 206dbe7e429SAlan Hourihane cr_backlight_set_intensity(crp->cr_backlight_device); 207dbe7e429SAlan Hourihane cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK); 208dbe7e429SAlan Hourihane 209dbe7e429SAlan Hourihane platform_set_drvdata(pdev, crp); 210dbe7e429SAlan Hourihane 211dbe7e429SAlan Hourihane return 0; 212dbe7e429SAlan Hourihane } 213dbe7e429SAlan Hourihane 214dbe7e429SAlan Hourihane static int cr_backlight_remove(struct platform_device *pdev) 215dbe7e429SAlan Hourihane { 216dbe7e429SAlan Hourihane struct cr_panel *crp = platform_get_drvdata(pdev); 2177beeee46SJingoo Han 218dbe7e429SAlan Hourihane crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN; 219dbe7e429SAlan Hourihane crp->cr_backlight_device->props.brightness = 0; 220dbe7e429SAlan Hourihane crp->cr_backlight_device->props.max_brightness = 0; 221dbe7e429SAlan Hourihane cr_backlight_set_intensity(crp->cr_backlight_device); 222dbe7e429SAlan Hourihane cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN); 223dbe7e429SAlan Hourihane pci_dev_put(lpc_dev); 224dbe7e429SAlan Hourihane 225dbe7e429SAlan Hourihane return 0; 226dbe7e429SAlan Hourihane } 227dbe7e429SAlan Hourihane 228dbe7e429SAlan Hourihane static struct platform_driver cr_backlight_driver = { 229dbe7e429SAlan Hourihane .probe = cr_backlight_probe, 230dbe7e429SAlan Hourihane .remove = cr_backlight_remove, 231dbe7e429SAlan Hourihane .driver = { 232dbe7e429SAlan Hourihane .name = "cr_backlight", 233dbe7e429SAlan Hourihane }, 234dbe7e429SAlan Hourihane }; 235dbe7e429SAlan Hourihane 236dbe7e429SAlan Hourihane static struct platform_device *crp; 237dbe7e429SAlan Hourihane 238dbe7e429SAlan Hourihane static int __init cr_backlight_init(void) 239dbe7e429SAlan Hourihane { 240dbe7e429SAlan Hourihane int ret = platform_driver_register(&cr_backlight_driver); 241dbe7e429SAlan Hourihane 242b4a11d3dSAkinobu Mita if (ret) 243b4a11d3dSAkinobu Mita return ret; 244dbe7e429SAlan Hourihane 245b4a11d3dSAkinobu Mita crp = platform_device_register_simple("cr_backlight", -1, NULL, 0); 246b4a11d3dSAkinobu Mita if (IS_ERR(crp)) { 247dbe7e429SAlan Hourihane platform_driver_unregister(&cr_backlight_driver); 248b4a11d3dSAkinobu Mita return PTR_ERR(crp); 249dbe7e429SAlan Hourihane } 250dbe7e429SAlan Hourihane 25131e6432bSJingoo Han pr_info("Carillo Ranch Backlight Driver Initialized.\n"); 252dbe7e429SAlan Hourihane 253b4a11d3dSAkinobu Mita return 0; 254dbe7e429SAlan Hourihane } 255dbe7e429SAlan Hourihane 256dbe7e429SAlan Hourihane static void __exit cr_backlight_exit(void) 257dbe7e429SAlan Hourihane { 258dbe7e429SAlan Hourihane platform_device_unregister(crp); 259dbe7e429SAlan Hourihane platform_driver_unregister(&cr_backlight_driver); 260dbe7e429SAlan Hourihane } 261dbe7e429SAlan Hourihane 262dbe7e429SAlan Hourihane module_init(cr_backlight_init); 263dbe7e429SAlan Hourihane module_exit(cr_backlight_exit); 264dbe7e429SAlan Hourihane 265dbe7e429SAlan Hourihane MODULE_AUTHOR("Tungsten Graphics Inc."); 266dbe7e429SAlan Hourihane MODULE_DESCRIPTION("Carillo Ranch Backlight Driver"); 267dbe7e429SAlan Hourihane MODULE_LICENSE("GPL"); 268