e691571a848be352d0a12d7a98dfe5a42e4cf966
[platform/upstream/libdrm.git] / linux-core / i915_opregion.c
1 /*
2  *
3  * Copyright 2008 Intel Corporation <hong.liu@intel.com>
4  * Copyright 2008 Red Hat <mjg@redhat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NON-INFRINGEMENT.  IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS BE
22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  *
27  */
28
29 #include <linux/acpi.h>
30
31 #include "drmP.h"
32 #include "i915_drm.h"
33 #include "i915_drv.h"
34
35 #define PCI_ASLE 0xe4
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 /* _DOD id definitions */
51 #define OUTPUT_CONNECTOR_MSK   0xf000
52 #define OUTPUT_CONNECTOR_OFFSET        12
53
54 #define OUTPUT_PORT_MSK                0x00f0
55 #define OUTPUT_PORT_OFFSET     4
56   #define OUTPUT_PORT_ANALOG   0
57   #define OUTPUT_PORT_LVDS     1
58   #define OUTPUT_PORT_SDVOB    2
59   #define OUTPUT_PORT_SDVOC    3
60   #define OUTPUT_PORT_TV       4
61
62 #define OUTPUT_DISPLAY_MSK     0x0f00
63 #define OUTPUT_DISPLAY_OFFSET  8
64   #define OUTPUT_DISPLAY_OTHER         0
65   #define OUTPUT_DISPLAY_VGA           1
66   #define OUTPUT_DISPLAY_TV            2
67   #define OUTPUT_DISPLAY_DIGI          3
68   #define OUTPUT_DISPLAY_FLAT_PANEL    4
69
70 /* predefined id for integrated LVDS and VGA connector */
71 #define OUTPUT_INT_LVDS        0x00000110
72 #define OUTPUT_INT_VGA 0x80000100
73
74 struct opregion_header {
75        u8 signature[16];
76        u32 size;
77        u32 opregion_ver;
78        u8 bios_ver[32];
79        u8 vbios_ver[16];
80        u8 driver_ver[16];
81        u32 mboxes;
82        u8 reserved[164];
83 } __attribute__((packed));
84
85 /* OpRegion mailbox #1: public ACPI methods */
86 struct opregion_acpi {
87        u32 drdy;       /* driver readiness */
88        u32 csts;       /* notification status */
89        u32 cevt;       /* current event */
90        u8 rsvd1[20];
91        u32 didl[8];    /* supported display devices ID list */
92        u32 cpdl[8];    /* currently presented display list */
93        u32 cadl[8];    /* currently active display list */
94        u32 nadl[8];    /* next active devices list */
95        u32 aslp;       /* ASL sleep time-out */
96        u32 tidx;       /* toggle table index */
97        u32 chpd;       /* current hotplug enable indicator */
98        u32 clid;       /* current lid state*/
99        u32 cdck;       /* current docking state */
100        u32 sxsw;       /* Sx state resume */
101        u32 evts;       /* ASL supported events */
102        u32 cnot;       /* current OS notification */
103        u32 nrdy;       /* driver status */
104        u8 rsvd2[60];
105 } __attribute__((packed));
106
107 /* OpRegion mailbox #2: SWSCI */
108 struct opregion_swsci {
109        u32 scic;       /* SWSCI command|status|data */
110        u32 parm;       /* command parameters */
111        u32 dslp;       /* driver sleep time-out */
112        u8 rsvd[244];
113 } __attribute__((packed));
114
115 /* OpRegion mailbox #3: ASLE */
116 struct opregion_asle {
117        u32 ardy;       /* driver readiness */
118        u32 aslc;       /* ASLE interrupt command */
119        u32 tche;       /* technology enabled indicator */
120        u32 alsi;       /* current ALS illuminance reading */
121        u32 bclp;       /* backlight brightness to set */
122        u32 pfit;       /* panel fitting state */
123        u32 cblv;       /* current brightness level */
124        u16 bclm[20];   /* backlight level duty cycle mapping table */
125        u32 cpfm;       /* current panel fitting mode */
126        u32 epfm;       /* enabled panel fitting modes */
127        u8 plut[74];    /* panel LUT and identifier */
128        u32 pfmb;       /* PWM freq and min brightness */
129        u8 rsvd[102];
130 } __attribute__((packed));
131
132 /* ASLE irq request bits */
133 #define ASLE_SET_ALS_ILLUM     (1 << 0)
134 #define ASLE_SET_BACKLIGHT     (1 << 1)
135 #define ASLE_SET_PFIT          (1 << 2)
136 #define ASLE_SET_PWM_FREQ      (1 << 3)
137 #define ASLE_REQ_MSK           0xf
138
139 /* response bits of ASLE irq request */
140 #define ASLE_ALS_ILLUM_FAIL    (2<<10)
141 #define ASLE_BACKLIGHT_FAIL    (2<<12)
142 #define ASLE_PFIT_FAIL         (2<<14)
143 #define ASLE_PWM_FREQ_FAIL     (2<<16)
144
145 /* ASLE backlight brightness to set */
146 #define ASLE_BCLP_VALID                (1<<31)
147 #define ASLE_BCLP_MSK          (~(1<<31))
148
149 /* ASLE panel fitting request */
150 #define ASLE_PFIT_VALID         (1<<31)
151 #define ASLE_PFIT_CENTER (1<<0)
152 #define ASLE_PFIT_STRETCH_TEXT (1<<1)
153 #define ASLE_PFIT_STRETCH_GFX (1<<2)
154
155 /* PWM frequency and minimum brightness */
156 #define ASLE_PFMB_BRIGHTNESS_MASK (0xff)
157 #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)
158 #define ASLE_PFMB_PWM_MASK (0x7ffffe00)
159 #define ASLE_PFMB_PWM_VALID (1<<31)
160
161 #define ASLE_CBLV_VALID         (1<<31)
162
163 static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
164 {
165         struct drm_i915_private *dev_priv = dev->dev_private;
166         struct opregion_asle *asle = dev_priv->opregion.asle;
167         u32 blc_pwm_ctl;
168         
169         if (!(bclp & ASLE_BCLP_VALID))
170                 return ASLE_BACKLIGHT_FAIL;
171         
172         bclp &= ASLE_BCLP_MSK;
173         if (bclp < 0 || bclp > 255)
174                 return ASLE_BACKLIGHT_FAIL;
175         
176         blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
177         blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK;
178         I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | ((bclp * 0x101) -1));
179         asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
180         
181         return 0;
182 }
183
184 static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi)
185 {
186         return 0;
187 }
188
189 static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb)
190 {
191         struct drm_i915_private *dev_priv = dev->dev_private;
192         if (pfmb & ASLE_PFMB_PWM_VALID) {
193                 u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
194                 u32 pwm = pfmb & ASLE_PFMB_PWM_MASK;
195                 blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK;
196                 pwm = pwm >> 9;
197                 // FIXME - what do we do with the PWM?
198         }
199         return 0;
200 }
201
202 static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
203 {
204         if (!(pfit & ASLE_PFIT_VALID))
205                 return ASLE_PFIT_FAIL;
206         return 0;
207 }
208
209 void opregion_asle_intr(struct drm_device *dev)
210 {
211         struct drm_i915_private *dev_priv = dev->dev_private;
212         struct opregion_asle *asle = dev_priv->opregion.asle;
213         u32 asle_stat = 0;
214         u32 asle_req;
215
216         if (!asle)
217                 return;
218
219         asle_req = asle->aslc & ASLE_REQ_MSK;
220         
221         if (!asle_req) {
222                 DRM_DEBUG("non asle set request??\n");
223                 return;
224         }
225
226         if (asle_req & ASLE_SET_ALS_ILLUM)
227                 asle_stat |= asle_set_als_illum(dev, asle->alsi);
228         
229         if (asle_req & ASLE_SET_BACKLIGHT)
230                 asle_stat |= asle_set_backlight(dev, asle->bclp);
231         
232         if (asle_req & ASLE_SET_PFIT)
233                 asle_stat |= asle_set_pfit(dev, asle->pfit);
234         
235         if (asle_req & ASLE_SET_PWM_FREQ)
236                 asle_stat |= asle_set_pwm_freq(dev, asle->pfmb);
237         
238         asle->aslc = asle_stat;
239 }
240
241 #define ASLE_ALS_EN    (1<<0)
242 #define ASLE_BLC_EN    (1<<1)
243 #define ASLE_PFIT_EN   (1<<2)
244 #define ASLE_PFMB_EN   (1<<3)
245
246 void opregion_enable_asle(struct drm_device *dev)
247 {
248         struct drm_i915_private *dev_priv = dev->dev_private;
249         struct opregion_asle *asle = dev_priv->opregion.asle;
250
251         if (asle) {
252                 u32 pipeb_stats = I915_READ(PIPEBSTAT);
253                 if (IS_MOBILE(dev)) {
254                         /* Some hardware uses the legacy backlight controller
255                            to signal interrupts, so we need to set up pipe B
256                            to generate an IRQ on writes */
257                         I915_WRITE(PIPEBSTAT, pipeb_stats |= 
258                                    I915_LEGACY_BLC_EVENT_ENABLE);
259                         dev_priv->irq_enable_reg |= 
260                                 (I915_ASLE_INTERRUPT 
261                                  | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT);
262                 } else
263                         dev_priv->irq_enable_reg |= I915_ASLE_INTERRUPT;
264                 
265                 asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | 
266                         ASLE_PFMB_EN;
267                 asle->ardy = 1;
268         }
269 }
270
271 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
272 #define ACPI_EV_LID            (1<<1)
273 #define ACPI_EV_DOCK           (1<<2)
274
275 static struct intel_opregion *system_opregion;
276
277 int intel_opregion_video_event(struct notifier_block *nb, unsigned long val,
278                                void *data)
279 {
280         /* The only video events relevant to opregion are 0x80. These indicate
281            either a docking event, lid switch or display switch request. In
282            Linux, these are handled by the dock, button and video drivers.
283            We might want to fix the video driver to be opregion-aware in
284            future, but right now we just indicate to the firmware that the
285            request has been handled */
286         
287         struct opregion_acpi *acpi;
288
289         if (!system_opregion)
290                 return NOTIFY_DONE;
291         
292         acpi = system_opregion->acpi;
293         acpi->csts = 0;
294
295         return NOTIFY_OK;
296 }
297
298 static struct notifier_block intel_opregion_notifier = {
299         .notifier_call = intel_opregion_video_event,
300 };
301
302 int intel_opregion_init(struct drm_device *dev)
303 {
304         struct drm_i915_private *dev_priv = dev->dev_private;
305         struct intel_opregion *opregion = &dev_priv->opregion;
306         void *base;
307         u32 asls, mboxes;
308         int err = 0;
309         
310         pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
311         DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
312         if (asls == 0) {
313                 DRM_DEBUG("ACPI OpRegion not supported!\n");
314                 return -ENOTSUPP;
315         }
316         
317         base = ioremap(asls, OPREGION_SZ);
318         if (!base)
319                 return -ENOMEM;
320         
321         opregion->header = base;
322         if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
323                 DRM_DEBUG("opregion signature mismatch\n");
324                 err = -EINVAL;
325                 goto err_out;
326         }
327         
328         mboxes = opregion->header->mboxes;
329         if (mboxes & MBOX_ACPI) {
330                 DRM_DEBUG("Public ACPI methods supported\n");
331                 opregion->acpi = base + OPREGION_ACPI_OFFSET;
332         } else {
333                 DRM_DEBUG("Public ACPI methods not supported\n");
334                 err = -ENOTSUPP;
335                 goto err_out;
336         }
337         opregion->enabled = 1;
338         
339         if (mboxes & MBOX_SWSCI) {
340                 DRM_DEBUG("SWSCI supported\n");
341                 opregion->swsci = base + OPREGION_SWSCI_OFFSET;
342         }
343         if (mboxes & MBOX_ASLE) {
344                 DRM_DEBUG("ASLE supported\n");
345                 opregion->asle = base + OPREGION_ASLE_OFFSET;
346         }
347         
348         /* Notify BIOS we are ready to handle ACPI video ext notifs.
349          * Right now, all the events are handled by the ACPI video module.
350          * We don't actually need to do anything with them. */
351         opregion->acpi->csts = 0;
352         opregion->acpi->drdy = 1;
353
354         system_opregion = opregion;
355         register_acpi_notifier(&intel_opregion_notifier);
356         
357         return 0;
358         
359 err_out:
360         iounmap(opregion->header);
361         opregion->header = NULL;
362         return err;
363 }
364
365 void intel_opregion_free(struct drm_device *dev)
366 {
367         struct drm_i915_private *dev_priv = dev->dev_private;
368         struct intel_opregion *opregion = &dev_priv->opregion;
369         
370         if (!opregion->enabled)
371                 return;
372         
373         opregion->acpi->drdy = 0;
374         
375         system_opregion = NULL;
376         unregister_acpi_notifier(&intel_opregion_notifier);
377         
378         /* just clear all opregion memory pointers now */
379         iounmap(opregion->header);
380         opregion->header = NULL;
381         opregion->acpi = NULL;
382         opregion->swsci = NULL;
383         opregion->asle = NULL;
384         
385         opregion->enabled = 0;
386 }