Add Intel ACPI IGD OpRegion support
[safe/jmp/linux-2.6] / 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 }