Merge branch 'drm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[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                 if (IS_MOBILE(dev)) {
239                         unsigned long irqflags;
240
241                         spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
242                         i915_enable_pipestat(dev_priv, 1,
243                                              I915_LEGACY_BLC_EVENT_ENABLE);
244                         spin_unlock_irqrestore(&dev_priv->user_irq_lock,
245                                                irqflags);
246                 }
247
248                 asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |
249                         ASLE_PFMB_EN;
250                 asle->ardy = 1;
251         }
252 }
253
254 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
255 #define ACPI_EV_LID            (1<<1)
256 #define ACPI_EV_DOCK           (1<<2)
257
258 static struct intel_opregion *system_opregion;
259
260 int intel_opregion_video_event(struct notifier_block *nb, unsigned long val,
261                                void *data)
262 {
263         /* The only video events relevant to opregion are 0x80. These indicate
264            either a docking event, lid switch or display switch request. In
265            Linux, these are handled by the dock, button and video drivers.
266            We might want to fix the video driver to be opregion-aware in
267            future, but right now we just indicate to the firmware that the
268            request has been handled */
269
270         struct opregion_acpi *acpi;
271
272         if (!system_opregion)
273                 return NOTIFY_DONE;
274
275         acpi = system_opregion->acpi;
276         acpi->csts = 0;
277
278         return NOTIFY_OK;
279 }
280
281 static struct notifier_block intel_opregion_notifier = {
282         .notifier_call = intel_opregion_video_event,
283 };
284
285 int intel_opregion_init(struct drm_device *dev)
286 {
287         struct drm_i915_private *dev_priv = dev->dev_private;
288         struct intel_opregion *opregion = &dev_priv->opregion;
289         void *base;
290         u32 asls, mboxes;
291         int err = 0;
292
293         pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
294         DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
295         if (asls == 0) {
296                 DRM_DEBUG("ACPI OpRegion not supported!\n");
297                 return -ENOTSUPP;
298         }
299
300         base = ioremap(asls, OPREGION_SZ);
301         if (!base)
302                 return -ENOMEM;
303
304         opregion->header = base;
305         if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
306                 DRM_DEBUG("opregion signature mismatch\n");
307                 err = -EINVAL;
308                 goto err_out;
309         }
310
311         mboxes = opregion->header->mboxes;
312         if (mboxes & MBOX_ACPI) {
313                 DRM_DEBUG("Public ACPI methods supported\n");
314                 opregion->acpi = base + OPREGION_ACPI_OFFSET;
315         } else {
316                 DRM_DEBUG("Public ACPI methods not supported\n");
317                 err = -ENOTSUPP;
318                 goto err_out;
319         }
320         opregion->enabled = 1;
321
322         if (mboxes & MBOX_SWSCI) {
323                 DRM_DEBUG("SWSCI supported\n");
324                 opregion->swsci = base + OPREGION_SWSCI_OFFSET;
325         }
326         if (mboxes & MBOX_ASLE) {
327                 DRM_DEBUG("ASLE supported\n");
328                 opregion->asle = base + OPREGION_ASLE_OFFSET;
329         }
330
331         /* Notify BIOS we are ready to handle ACPI video ext notifs.
332          * Right now, all the events are handled by the ACPI video module.
333          * We don't actually need to do anything with them. */
334         opregion->acpi->csts = 0;
335         opregion->acpi->drdy = 1;
336
337         system_opregion = opregion;
338         register_acpi_notifier(&intel_opregion_notifier);
339
340         return 0;
341
342 err_out:
343         iounmap(opregion->header);
344         opregion->header = NULL;
345         return err;
346 }
347
348 void intel_opregion_free(struct drm_device *dev)
349 {
350         struct drm_i915_private *dev_priv = dev->dev_private;
351         struct intel_opregion *opregion = &dev_priv->opregion;
352
353         if (!opregion->enabled)
354                 return;
355
356         opregion->acpi->drdy = 0;
357
358         system_opregion = NULL;
359         unregister_acpi_notifier(&intel_opregion_notifier);
360
361         /* just clear all opregion memory pointers now */
362         iounmap(opregion->header);
363         opregion->header = NULL;
364         opregion->acpi = NULL;
365         opregion->swsci = NULL;
366         opregion->asle = NULL;
367
368         opregion->enabled = 0;
369 }