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