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 (bd->props.power == FB_BLANK_UNBLANK) 67 intensity = FB_BLANK_UNBLANK; 68 if (bd->props.fb_blank == FB_BLANK_UNBLANK) 69 intensity = FB_BLANK_UNBLANK; 70 if (bd->props.power == FB_BLANK_POWERDOWN) 71 intensity = FB_BLANK_POWERDOWN; 72 if (bd->props.fb_blank == FB_BLANK_POWERDOWN) 73 intensity = FB_BLANK_POWERDOWN; 74 75 if (intensity == FB_BLANK_UNBLANK) { /* FULL ON */ 76 cur &= ~CRVML_BACKLIGHT_OFF; 77 outl(cur, addr); 78 } else if (intensity == FB_BLANK_POWERDOWN) { /* OFF */ 79 cur |= CRVML_BACKLIGHT_OFF; 80 outl(cur, addr); 81 } /* anything else, don't bother */ 82 83 return 0; 84 } 85 86 static int cr_backlight_get_intensity(struct backlight_device *bd) 87 { 88 u32 addr = gpio_bar + CRVML_PANEL_PORT; 89 u32 cur = inl(addr); 90 u8 intensity; 91 92 if (cur & CRVML_BACKLIGHT_OFF) 93 intensity = FB_BLANK_POWERDOWN; 94 else 95 intensity = FB_BLANK_UNBLANK; 96 97 return intensity; 98 } 99 100 static const struct backlight_ops cr_backlight_ops = { 101 .get_brightness = cr_backlight_get_intensity, 102 .update_status = cr_backlight_set_intensity, 103 }; 104 105 static void cr_panel_on(void) 106 { 107 u32 addr = gpio_bar + CRVML_PANEL_PORT; 108 u32 cur = inl(addr); 109 110 if (!(cur & CRVML_PANEL_ON)) { 111 /* Make sure LVDS controller is down. */ 112 if (cur & 0x00000001) { 113 cur &= ~CRVML_LVDS_ON; 114 outl(cur, addr); 115 } 116 /* Power up Panel */ 117 schedule_timeout(HZ / 10); 118 cur |= CRVML_PANEL_ON; 119 outl(cur, addr); 120 } 121 122 /* Power up LVDS controller */ 123 124 if (!(cur & CRVML_LVDS_ON)) { 125 schedule_timeout(HZ / 10); 126 outl(cur | CRVML_LVDS_ON, addr); 127 } 128 } 129 130 static void cr_panel_off(void) 131 { 132 u32 addr = gpio_bar + CRVML_PANEL_PORT; 133 u32 cur = inl(addr); 134 135 /* Power down LVDS controller first to avoid high currents */ 136 if (cur & CRVML_LVDS_ON) { 137 cur &= ~CRVML_LVDS_ON; 138 outl(cur, addr); 139 } 140 if (cur & CRVML_PANEL_ON) { 141 schedule_timeout(HZ / 10); 142 outl(cur & ~CRVML_PANEL_ON, addr); 143 } 144 } 145 146 static int cr_lcd_set_power(struct lcd_device *ld, int power) 147 { 148 if (power == FB_BLANK_UNBLANK) 149 cr_panel_on(); 150 if (power == FB_BLANK_POWERDOWN) 151 cr_panel_off(); 152 153 return 0; 154 } 155 156 static struct lcd_ops cr_lcd_ops = { 157 .set_power = cr_lcd_set_power, 158 }; 159 160 static int cr_backlight_probe(struct platform_device *pdev) 161 { 162 struct backlight_properties props; 163 struct backlight_device *bdp; 164 struct lcd_device *ldp; 165 struct cr_panel *crp; 166 u8 dev_en; 167 168 lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 169 CRVML_DEVICE_LPC, NULL); 170 if (!lpc_dev) { 171 pr_err("INTEL CARILLO RANCH LPC not found.\n"); 172 return -ENODEV; 173 } 174 175 pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en); 176 if (!(dev_en & CRVML_GPIOEN_BIT)) { 177 pr_err("Carillo Ranch GPIO device was not enabled.\n"); 178 pci_dev_put(lpc_dev); 179 return -ENODEV; 180 } 181 182 memset(&props, 0, sizeof(struct backlight_properties)); 183 props.type = BACKLIGHT_RAW; 184 bdp = devm_backlight_device_register(&pdev->dev, "cr-backlight", 185 &pdev->dev, NULL, &cr_backlight_ops, 186 &props); 187 if (IS_ERR(bdp)) { 188 pci_dev_put(lpc_dev); 189 return PTR_ERR(bdp); 190 } 191 192 ldp = devm_lcd_device_register(&pdev->dev, "cr-lcd", &pdev->dev, NULL, 193 &cr_lcd_ops); 194 if (IS_ERR(ldp)) { 195 pci_dev_put(lpc_dev); 196 return PTR_ERR(ldp); 197 } 198 199 pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, 200 &gpio_bar); 201 gpio_bar &= ~0x3F; 202 203 crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL); 204 if (!crp) { 205 pci_dev_put(lpc_dev); 206 return -ENOMEM; 207 } 208 209 crp->cr_backlight_device = bdp; 210 crp->cr_lcd_device = ldp; 211 crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; 212 crp->cr_backlight_device->props.brightness = 0; 213 cr_backlight_set_intensity(crp->cr_backlight_device); 214 cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK); 215 216 platform_set_drvdata(pdev, crp); 217 218 return 0; 219 } 220 221 static int cr_backlight_remove(struct platform_device *pdev) 222 { 223 struct cr_panel *crp = platform_get_drvdata(pdev); 224 225 crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN; 226 crp->cr_backlight_device->props.brightness = 0; 227 crp->cr_backlight_device->props.max_brightness = 0; 228 cr_backlight_set_intensity(crp->cr_backlight_device); 229 cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN); 230 pci_dev_put(lpc_dev); 231 232 return 0; 233 } 234 235 static struct platform_driver cr_backlight_driver = { 236 .probe = cr_backlight_probe, 237 .remove = cr_backlight_remove, 238 .driver = { 239 .name = "cr_backlight", 240 }, 241 }; 242 243 static struct platform_device *crp; 244 245 static int __init cr_backlight_init(void) 246 { 247 int ret = platform_driver_register(&cr_backlight_driver); 248 249 if (ret) 250 return ret; 251 252 crp = platform_device_register_simple("cr_backlight", -1, NULL, 0); 253 if (IS_ERR(crp)) { 254 platform_driver_unregister(&cr_backlight_driver); 255 return PTR_ERR(crp); 256 } 257 258 pr_info("Carillo Ranch Backlight Driver Initialized.\n"); 259 260 return 0; 261 } 262 263 static void __exit cr_backlight_exit(void) 264 { 265 platform_device_unregister(crp); 266 platform_driver_unregister(&cr_backlight_driver); 267 } 268 269 module_init(cr_backlight_init); 270 module_exit(cr_backlight_exit); 271 272 MODULE_AUTHOR("Tungsten Graphics Inc."); 273 MODULE_DESCRIPTION("Carillo Ranch Backlight Driver"); 274 MODULE_LICENSE("GPL"); 275