Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Add Intel ACPI IGD OpRegion support

This adds the support necessary for allowing ACPI backlight control to
work on some newer Intel-based graphics systems. Tested on Thinkpad T61
and HP 2510p hardware.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Dave Airlie <airlied@linux.ie>

authored by

Matthew Garrett and committed by
Dave Airlie
8ee1c3db 398c9cb2

+415 -9
+1 -1
drivers/gpu/drm/i915/Makefile
··· 3 3 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. 4 4 5 5 ccflags-y := -Iinclude/drm 6 - i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o 6 + i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_opregion.o 7 7 8 8 i915-$(CONFIG_COMPAT) += i915_ioc32.o 9 9
+4
drivers/gpu/drm/i915/i915_dma.c
··· 810 810 if (!IS_I945G(dev) && !IS_I945GM(dev)) 811 811 pci_enable_msi(dev->pdev); 812 812 813 + intel_opregion_init(dev); 814 + 813 815 spin_lock_init(&dev_priv->user_irq_lock); 814 816 815 817 return ret; ··· 828 826 829 827 if (dev_priv->mmio_map) 830 828 drm_rmmap(dev, dev_priv->mmio_map); 829 + 830 + intel_opregion_free(dev); 831 831 832 832 drm_free(dev->dev_private, sizeof(drm_i915_private_t), 833 833 DRM_MEM_DRIVER);
+4
drivers/gpu/drm/i915/i915_drv.c
··· 371 371 372 372 i915_save_vga(dev); 373 373 374 + intel_opregion_free(dev); 375 + 374 376 if (state.event == PM_EVENT_SUSPEND) { 375 377 /* Shut down the device */ 376 378 pci_disable_device(dev->pdev); ··· 533 531 I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]); 534 532 535 533 i915_restore_vga(dev); 534 + 535 + intel_opregion_init(dev); 536 536 537 537 return 0; 538 538 }
+17
drivers/gpu/drm/i915/i915_drv.h
··· 82 82 unsigned int sequence; 83 83 } drm_i915_vbl_swap_t; 84 84 85 + struct intel_opregion { 86 + struct opregion_header *header; 87 + struct opregion_acpi *acpi; 88 + struct opregion_swsci *swsci; 89 + struct opregion_asle *asle; 90 + int enabled; 91 + }; 92 + 85 93 typedef struct drm_i915_private { 86 94 drm_local_map_t *sarea; 87 95 drm_local_map_t *mmio_map; ··· 129 121 spinlock_t swaps_lock; 130 122 drm_i915_vbl_swap_t vbl_swaps; 131 123 unsigned int swaps_pending; 124 + 125 + struct intel_opregion opregion; 132 126 133 127 /* Register state */ 134 128 u8 saveLBB; ··· 254 244 struct drm_file *file_priv); 255 245 extern int i915_vblank_swap(struct drm_device *dev, void *data, 256 246 struct drm_file *file_priv); 247 + extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask); 257 248 258 249 /* i915_mem.c */ 259 250 extern int i915_mem_alloc(struct drm_device *dev, void *data, ··· 268 257 extern void i915_mem_takedown(struct mem_block **heap); 269 258 extern void i915_mem_release(struct drm_device * dev, 270 259 struct drm_file *file_priv, struct mem_block *heap); 260 + 261 + /* i915_opregion.c */ 262 + extern int intel_opregion_init(struct drm_device *dev); 263 + extern void intel_opregion_free(struct drm_device *dev); 264 + extern void opregion_asle_intr(struct drm_device *dev); 265 + extern void opregion_enable_asle(struct drm_device *dev); 271 266 272 267 #define I915_READ(reg) DRM_READ32(dev_priv->mmio_map, (reg)) 273 268 #define I915_WRITE(reg,val) DRM_WRITE32(dev_priv->mmio_map, (reg), (val))
+17 -8
drivers/gpu/drm/i915/i915_irq.c
··· 36 36 /** These are the interrupts used by the driver */ 37 37 #define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ 38 38 I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \ 39 - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) 39 + I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT | \ 40 + I915_ASLE_INTERRUPT | \ 41 + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) 40 42 41 - static inline void 43 + void 42 44 i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) 43 45 { 44 46 if ((dev_priv->irq_mask_reg & mask) != 0) { ··· 276 274 return IRQ_NONE; 277 275 } 278 276 277 + I915_WRITE(PIPEASTAT, pipea_stats); 278 + I915_WRITE(PIPEBSTAT, pipeb_stats); 279 + 279 280 I915_WRITE(IIR, iir); 280 281 if (dev->pdev->msi_enabled) 281 282 I915_WRITE(IMR, dev_priv->irq_mask_reg); ··· 311 306 312 307 if (dev_priv->swaps_pending > 0) 313 308 drm_locked_tasklet(dev, i915_vblank_tasklet); 314 - I915_WRITE(PIPEASTAT, 315 - pipea_stats|I915_VBLANK_INTERRUPT_ENABLE| 316 - PIPE_VBLANK_INTERRUPT_STATUS); 317 - I915_WRITE(PIPEBSTAT, 318 - pipeb_stats|I915_VBLANK_INTERRUPT_ENABLE| 319 - PIPE_VBLANK_INTERRUPT_STATUS); 320 309 } 310 + 311 + if (iir & I915_ASLE_INTERRUPT) 312 + opregion_asle_intr(dev); 313 + 314 + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) 315 + opregion_asle_intr(dev); 321 316 322 317 return IRQ_HANDLED; 323 318 } ··· 666 661 if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) 667 662 dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; 668 663 664 + dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK; 665 + 669 666 I915_WRITE(IMR, dev_priv->irq_mask_reg); 670 667 I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); 671 668 (void) I915_READ(IER); 669 + 670 + opregion_enable_asle(dev); 672 671 673 672 DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); 674 673 }
+371
drivers/gpu/drm/i915/i915_opregion.c
··· 1 + /* 2 + * Copyright 2008 Intel Corporation <hong.liu@intel.com> 3 + * Copyright 2008 Red Hat <mjg@redhat.com> 4 + * 5 + * Permission is hereby granted, free of charge, to any person obtaining 6 + * a copy of this software and associated documentation files (the 7 + * "Software"), to deal in the Software without restriction, including 8 + * without limitation the rights to use, copy, modify, merge, publish, 9 + * distribute, sub license, and/or sell copies of the Software, and to 10 + * permit persons to whom the Software is furnished to do so, subject to 11 + * the following conditions: 12 + * 13 + * The above copyright notice and this permission notice (including the 14 + * next paragraph) shall be included in all copies or substantial 15 + * portions of the Software. 16 + * 17 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 + * NON-INFRINGEMENT. IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS BE 21 + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 + * SOFTWARE. 25 + * 26 + */ 27 + 28 + #include <linux/acpi.h> 29 + 30 + #include "drmP.h" 31 + #include "i915_drm.h" 32 + #include "i915_drv.h" 33 + 34 + #define PCI_ASLE 0xe4 35 + #define PCI_LBPC 0xf4 36 + #define PCI_ASLS 0xfc 37 + 38 + #define OPREGION_SZ (8*1024) 39 + #define OPREGION_HEADER_OFFSET 0 40 + #define OPREGION_ACPI_OFFSET 0x100 41 + #define OPREGION_SWSCI_OFFSET 0x200 42 + #define OPREGION_ASLE_OFFSET 0x300 43 + #define OPREGION_VBT_OFFSET 0x1000 44 + 45 + #define OPREGION_SIGNATURE "IntelGraphicsMem" 46 + #define MBOX_ACPI (1<<0) 47 + #define MBOX_SWSCI (1<<1) 48 + #define MBOX_ASLE (1<<2) 49 + 50 + struct opregion_header { 51 + u8 signature[16]; 52 + u32 size; 53 + u32 opregion_ver; 54 + u8 bios_ver[32]; 55 + u8 vbios_ver[16]; 56 + u8 driver_ver[16]; 57 + u32 mboxes; 58 + u8 reserved[164]; 59 + } __attribute__((packed)); 60 + 61 + /* OpRegion mailbox #1: public ACPI methods */ 62 + struct opregion_acpi { 63 + u32 drdy; /* driver readiness */ 64 + u32 csts; /* notification status */ 65 + u32 cevt; /* current event */ 66 + u8 rsvd1[20]; 67 + u32 didl[8]; /* supported display devices ID list */ 68 + u32 cpdl[8]; /* currently presented display list */ 69 + u32 cadl[8]; /* currently active display list */ 70 + u32 nadl[8]; /* next active devices list */ 71 + u32 aslp; /* ASL sleep time-out */ 72 + u32 tidx; /* toggle table index */ 73 + u32 chpd; /* current hotplug enable indicator */ 74 + u32 clid; /* current lid state*/ 75 + u32 cdck; /* current docking state */ 76 + u32 sxsw; /* Sx state resume */ 77 + u32 evts; /* ASL supported events */ 78 + u32 cnot; /* current OS notification */ 79 + u32 nrdy; /* driver status */ 80 + u8 rsvd2[60]; 81 + } __attribute__((packed)); 82 + 83 + /* OpRegion mailbox #2: SWSCI */ 84 + struct opregion_swsci { 85 + u32 scic; /* SWSCI command|status|data */ 86 + u32 parm; /* command parameters */ 87 + u32 dslp; /* driver sleep time-out */ 88 + u8 rsvd[244]; 89 + } __attribute__((packed)); 90 + 91 + /* OpRegion mailbox #3: ASLE */ 92 + struct opregion_asle { 93 + u32 ardy; /* driver readiness */ 94 + u32 aslc; /* ASLE interrupt command */ 95 + u32 tche; /* technology enabled indicator */ 96 + u32 alsi; /* current ALS illuminance reading */ 97 + u32 bclp; /* backlight brightness to set */ 98 + u32 pfit; /* panel fitting state */ 99 + u32 cblv; /* current brightness level */ 100 + u16 bclm[20]; /* backlight level duty cycle mapping table */ 101 + u32 cpfm; /* current panel fitting mode */ 102 + u32 epfm; /* enabled panel fitting modes */ 103 + u8 plut[74]; /* panel LUT and identifier */ 104 + u32 pfmb; /* PWM freq and min brightness */ 105 + u8 rsvd[102]; 106 + } __attribute__((packed)); 107 + 108 + /* ASLE irq request bits */ 109 + #define ASLE_SET_ALS_ILLUM (1 << 0) 110 + #define ASLE_SET_BACKLIGHT (1 << 1) 111 + #define ASLE_SET_PFIT (1 << 2) 112 + #define ASLE_SET_PWM_FREQ (1 << 3) 113 + #define ASLE_REQ_MSK 0xf 114 + 115 + /* response bits of ASLE irq request */ 116 + #define ASLE_ALS_ILLUM_FAIL (2<<10) 117 + #define ASLE_BACKLIGHT_FAIL (2<<12) 118 + #define ASLE_PFIT_FAIL (2<<14) 119 + #define ASLE_PWM_FREQ_FAIL (2<<16) 120 + 121 + /* ASLE backlight brightness to set */ 122 + #define ASLE_BCLP_VALID (1<<31) 123 + #define ASLE_BCLP_MSK (~(1<<31)) 124 + 125 + /* ASLE panel fitting request */ 126 + #define ASLE_PFIT_VALID (1<<31) 127 + #define ASLE_PFIT_CENTER (1<<0) 128 + #define ASLE_PFIT_STRETCH_TEXT (1<<1) 129 + #define ASLE_PFIT_STRETCH_GFX (1<<2) 130 + 131 + /* PWM frequency and minimum brightness */ 132 + #define ASLE_PFMB_BRIGHTNESS_MASK (0xff) 133 + #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8) 134 + #define ASLE_PFMB_PWM_MASK (0x7ffffe00) 135 + #define ASLE_PFMB_PWM_VALID (1<<31) 136 + 137 + #define ASLE_CBLV_VALID (1<<31) 138 + 139 + static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) 140 + { 141 + struct drm_i915_private *dev_priv = dev->dev_private; 142 + struct opregion_asle *asle = dev_priv->opregion.asle; 143 + u32 blc_pwm_ctl, blc_pwm_ctl2; 144 + 145 + if (!(bclp & ASLE_BCLP_VALID)) 146 + return ASLE_BACKLIGHT_FAIL; 147 + 148 + bclp &= ASLE_BCLP_MSK; 149 + if (bclp < 0 || bclp > 255) 150 + return ASLE_BACKLIGHT_FAIL; 151 + 152 + blc_pwm_ctl = I915_READ(BLC_PWM_CTL); 153 + blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; 154 + blc_pwm_ctl2 = I915_READ(BLC_PWM_CTL2); 155 + 156 + if (blc_pwm_ctl2 & BLM_COMBINATION_MODE) 157 + pci_write_config_dword(dev->pdev, PCI_LBPC, bclp); 158 + else 159 + I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | ((bclp * 0x101)-1)); 160 + 161 + asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; 162 + 163 + return 0; 164 + } 165 + 166 + static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi) 167 + { 168 + /* alsi is the current ALS reading in lux. 0 indicates below sensor 169 + range, 0xffff indicates above sensor range. 1-0xfffe are valid */ 170 + return 0; 171 + } 172 + 173 + static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb) 174 + { 175 + struct drm_i915_private *dev_priv = dev->dev_private; 176 + if (pfmb & ASLE_PFMB_PWM_VALID) { 177 + u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL); 178 + u32 pwm = pfmb & ASLE_PFMB_PWM_MASK; 179 + blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK; 180 + pwm = pwm >> 9; 181 + /* FIXME - what do we do with the PWM? */ 182 + } 183 + return 0; 184 + } 185 + 186 + static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) 187 + { 188 + /* Panel fitting is currently controlled by the X code, so this is a 189 + noop until modesetting support works fully */ 190 + if (!(pfit & ASLE_PFIT_VALID)) 191 + return ASLE_PFIT_FAIL; 192 + return 0; 193 + } 194 + 195 + void opregion_asle_intr(struct drm_device *dev) 196 + { 197 + struct drm_i915_private *dev_priv = dev->dev_private; 198 + struct opregion_asle *asle = dev_priv->opregion.asle; 199 + u32 asle_stat = 0; 200 + u32 asle_req; 201 + 202 + if (!asle) 203 + return; 204 + 205 + asle_req = asle->aslc & ASLE_REQ_MSK; 206 + 207 + if (!asle_req) { 208 + DRM_DEBUG("non asle set request??\n"); 209 + return; 210 + } 211 + 212 + if (asle_req & ASLE_SET_ALS_ILLUM) 213 + asle_stat |= asle_set_als_illum(dev, asle->alsi); 214 + 215 + if (asle_req & ASLE_SET_BACKLIGHT) 216 + asle_stat |= asle_set_backlight(dev, asle->bclp); 217 + 218 + if (asle_req & ASLE_SET_PFIT) 219 + asle_stat |= asle_set_pfit(dev, asle->pfit); 220 + 221 + if (asle_req & ASLE_SET_PWM_FREQ) 222 + asle_stat |= asle_set_pwm_freq(dev, asle->pfmb); 223 + 224 + asle->aslc = asle_stat; 225 + } 226 + 227 + #define ASLE_ALS_EN (1<<0) 228 + #define ASLE_BLC_EN (1<<1) 229 + #define ASLE_PFIT_EN (1<<2) 230 + #define ASLE_PFMB_EN (1<<3) 231 + 232 + void opregion_enable_asle(struct drm_device *dev) 233 + { 234 + struct drm_i915_private *dev_priv = dev->dev_private; 235 + struct opregion_asle *asle = dev_priv->opregion.asle; 236 + 237 + if (asle) { 238 + u32 pipeb_stats = I915_READ(PIPEBSTAT); 239 + if (IS_MOBILE(dev)) { 240 + /* Many devices trigger events with a write to the 241 + legacy backlight controller, so we need to ensure 242 + that it's able to generate interrupts */ 243 + I915_WRITE(PIPEBSTAT, pipeb_stats |= 244 + I915_LEGACY_BLC_EVENT_ENABLE); 245 + i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT | 246 + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); 247 + } else 248 + i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT); 249 + 250 + asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | 251 + ASLE_PFMB_EN; 252 + asle->ardy = 1; 253 + } 254 + } 255 + 256 + #define ACPI_EV_DISPLAY_SWITCH (1<<0) 257 + #define ACPI_EV_LID (1<<1) 258 + #define ACPI_EV_DOCK (1<<2) 259 + 260 + static struct intel_opregion *system_opregion; 261 + 262 + int intel_opregion_video_event(struct notifier_block *nb, unsigned long val, 263 + void *data) 264 + { 265 + /* The only video events relevant to opregion are 0x80. These indicate 266 + either a docking event, lid switch or display switch request. In 267 + Linux, these are handled by the dock, button and video drivers. 268 + We might want to fix the video driver to be opregion-aware in 269 + future, but right now we just indicate to the firmware that the 270 + request has been handled */ 271 + 272 + struct opregion_acpi *acpi; 273 + 274 + if (!system_opregion) 275 + return NOTIFY_DONE; 276 + 277 + acpi = system_opregion->acpi; 278 + acpi->csts = 0; 279 + 280 + return NOTIFY_OK; 281 + } 282 + 283 + static struct notifier_block intel_opregion_notifier = { 284 + .notifier_call = intel_opregion_video_event, 285 + }; 286 + 287 + int intel_opregion_init(struct drm_device *dev) 288 + { 289 + struct drm_i915_private *dev_priv = dev->dev_private; 290 + struct intel_opregion *opregion = &dev_priv->opregion; 291 + void *base; 292 + u32 asls, mboxes; 293 + int err = 0; 294 + 295 + pci_read_config_dword(dev->pdev, PCI_ASLS, &asls); 296 + DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls); 297 + if (asls == 0) { 298 + DRM_DEBUG("ACPI OpRegion not supported!\n"); 299 + return -ENOTSUPP; 300 + } 301 + 302 + base = ioremap(asls, OPREGION_SZ); 303 + if (!base) 304 + return -ENOMEM; 305 + 306 + opregion->header = base; 307 + if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) { 308 + DRM_DEBUG("opregion signature mismatch\n"); 309 + err = -EINVAL; 310 + goto err_out; 311 + } 312 + 313 + mboxes = opregion->header->mboxes; 314 + if (mboxes & MBOX_ACPI) { 315 + DRM_DEBUG("Public ACPI methods supported\n"); 316 + opregion->acpi = base + OPREGION_ACPI_OFFSET; 317 + } else { 318 + DRM_DEBUG("Public ACPI methods not supported\n"); 319 + err = -ENOTSUPP; 320 + goto err_out; 321 + } 322 + opregion->enabled = 1; 323 + 324 + if (mboxes & MBOX_SWSCI) { 325 + DRM_DEBUG("SWSCI supported\n"); 326 + opregion->swsci = base + OPREGION_SWSCI_OFFSET; 327 + } 328 + if (mboxes & MBOX_ASLE) { 329 + DRM_DEBUG("ASLE supported\n"); 330 + opregion->asle = base + OPREGION_ASLE_OFFSET; 331 + } 332 + 333 + /* Notify BIOS we are ready to handle ACPI video ext notifs. 334 + * Right now, all the events are handled by the ACPI video module. 335 + * We don't actually need to do anything with them. */ 336 + opregion->acpi->csts = 0; 337 + opregion->acpi->drdy = 1; 338 + 339 + system_opregion = opregion; 340 + register_acpi_notifier(&intel_opregion_notifier); 341 + 342 + return 0; 343 + 344 + err_out: 345 + iounmap(opregion->header); 346 + opregion->header = NULL; 347 + return err; 348 + } 349 + 350 + void intel_opregion_free(struct drm_device *dev) 351 + { 352 + struct drm_i915_private *dev_priv = dev->dev_private; 353 + struct intel_opregion *opregion = &dev_priv->opregion; 354 + 355 + if (!opregion->enabled) 356 + return; 357 + 358 + opregion->acpi->drdy = 0; 359 + 360 + system_opregion = NULL; 361 + unregister_acpi_notifier(&intel_opregion_notifier); 362 + 363 + /* just clear all opregion memory pointers now */ 364 + iounmap(opregion->header); 365 + opregion->header = NULL; 366 + opregion->acpi = NULL; 367 + opregion->swsci = NULL; 368 + opregion->asle = NULL; 369 + 370 + opregion->enabled = 0; 371 + }
+1
drivers/gpu/drm/i915/i915_reg.h
··· 740 740 #define BLC_PWM_CTL 0x61254 741 741 #define BACKLIGHT_MODULATION_FREQ_SHIFT (17) 742 742 #define BLC_PWM_CTL2 0x61250 /* 965+ only */ 743 + #define BLM_COMBINATION_MODE (1 << 30) 743 744 /* 744 745 * This is the most significant 15 bits of the number of backlight cycles in a 745 746 * complete cycle of the modulated backlight control.