Merge commit 'origin/master' into modesetting-gem
[profile/ivi/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 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
36 #define PCI_ASLE 0xe4
37 #define PCI_ASLS 0xfc
38
39 #define OPREGION_SZ            (8*1024)
40 #define OPREGION_HEADER_OFFSET 0
41 #define OPREGION_ACPI_OFFSET   0x100
42 #define OPREGION_SWSCI_OFFSET  0x200
43 #define OPREGION_ASLE_OFFSET   0x300
44 #define OPREGION_VBT_OFFSET    0x1000
45
46 #define OPREGION_SIGNATURE "IntelGraphicsMem"
47 #define MBOX_ACPI      (1<<0)
48 #define MBOX_SWSCI     (1<<1)
49 #define MBOX_ASLE      (1<<2)
50
51 /* _DOD id definitions */
52 #define OUTPUT_CONNECTOR_MSK   0xf000
53 #define OUTPUT_CONNECTOR_OFFSET        12
54
55 #define OUTPUT_PORT_MSK                0x00f0
56 #define OUTPUT_PORT_OFFSET     4
57   #define OUTPUT_PORT_ANALOG   0
58   #define OUTPUT_PORT_LVDS     1
59   #define OUTPUT_PORT_SDVOB    2
60   #define OUTPUT_PORT_SDVOC    3
61   #define OUTPUT_PORT_TV       4
62
63 #define OUTPUT_DISPLAY_MSK     0x0f00
64 #define OUTPUT_DISPLAY_OFFSET  8
65   #define OUTPUT_DISPLAY_OTHER         0
66   #define OUTPUT_DISPLAY_VGA           1
67   #define OUTPUT_DISPLAY_TV            2
68   #define OUTPUT_DISPLAY_DIGI          3
69   #define OUTPUT_DISPLAY_FLAT_PANEL    4
70
71 /* predefined id for integrated LVDS and VGA connector */
72 #define OUTPUT_INT_LVDS        0x00000110
73 #define OUTPUT_INT_VGA 0x80000100
74
75 struct opregion_header {
76        u8 signature[16];
77        u32 size;
78        u32 opregion_ver;
79        u8 bios_ver[32];
80        u8 vbios_ver[16];
81        u8 driver_ver[16];
82        u32 mboxes;
83        u8 reserved[164];
84 } __attribute__((packed));
85
86 /* OpRegion mailbox #1: public ACPI methods */
87 struct opregion_acpi {
88        u32 drdy;       /* driver readiness */
89        u32 csts;       /* notification status */
90        u32 cevt;       /* current event */
91        u8 rsvd1[20];
92        u32 didl[8];    /* supported display devices ID list */
93        u32 cpdl[8];    /* currently presented display list */
94        u32 cadl[8];    /* currently active display list */
95        u32 nadl[8];    /* next active devices list */
96        u32 aslp;       /* ASL sleep time-out */
97        u32 tidx;       /* toggle table index */
98        u32 chpd;       /* current hotplug enable indicator */
99        u32 clid;       /* current lid state*/
100        u32 cdck;       /* current docking state */
101        u32 sxsw;       /* Sx state resume */
102        u32 evts;       /* ASL supported events */
103        u32 cnot;       /* current OS notification */
104        u32 nrdy;       /* driver status */
105        u8 rsvd2[60];
106 } __attribute__((packed));
107
108 /* OpRegion mailbox #2: SWSCI */
109 struct opregion_swsci {
110        u32 scic;       /* SWSCI command|status|data */
111        u32 parm;       /* command parameters */
112        u32 dslp;       /* driver sleep time-out */
113        u8 rsvd[244];
114 } __attribute__((packed));
115
116 /* OpRegion mailbox #3: ASLE */
117 struct opregion_asle {
118        u32 ardy;       /* driver readiness */
119        u32 aslc;       /* ASLE interrupt command */
120        u32 tche;       /* technology enabled indicator */
121        u32 alsi;       /* current ALS illuminance reading */
122        u32 bclp;       /* backlight brightness to set */
123        u32 pfit;       /* panel fitting state */
124        u32 cblv;       /* current brightness level */
125        u16 bclm[20];   /* backlight level duty cycle mapping table */
126        u32 cpfm;       /* current panel fitting mode */
127        u32 epfm;       /* enabled panel fitting modes */
128        u8 plut[74];    /* panel LUT and identifier */
129        u32 pfmb;       /* PWM freq and min brightness */
130        u8 rsvd[102];
131 } __attribute__((packed));
132
133 /* ASLE irq request bits */
134 #define ASLE_SET_ALS_ILLUM     (1 << 0)
135 #define ASLE_SET_BACKLIGHT     (1 << 1)
136 #define ASLE_SET_PFIT          (1 << 2)
137 #define ASLE_SET_PWM_FREQ      (1 << 3)
138 #define ASLE_REQ_MSK           0xf
139
140 /* response bits of ASLE irq request */
141 #define ASLE_ALS_ILLUM_FAIL    (2<<10)
142 #define ASLE_BACKLIGHT_FAIL    (2<<12)
143 #define ASLE_PFIT_FAIL         (2<<14)
144 #define ASLE_PWM_FREQ_FAIL     (2<<16)
145
146 /* ASLE backlight brightness to set */
147 #define ASLE_BCLP_VALID                (1<<31)
148 #define ASLE_BCLP_MSK          (~(1<<31))
149
150 /* ASLE panel fitting request */
151 #define ASLE_PFIT_VALID         (1<<31)
152 #define ASLE_PFIT_CENTER (1<<0)
153 #define ASLE_PFIT_STRETCH_TEXT (1<<1)
154 #define ASLE_PFIT_STRETCH_GFX (1<<2)
155
156 /* PWM frequency and minimum brightness */
157 #define ASLE_PFMB_BRIGHTNESS_MASK (0xff)
158 #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)
159 #define ASLE_PFMB_PWM_MASK (0x7ffffe00)
160 #define ASLE_PFMB_PWM_VALID (1<<31)
161
162 #define ASLE_CBLV_VALID         (1<<31)
163
164 static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
165 {
166         struct drm_i915_private *dev_priv = dev->dev_private;
167         struct opregion_asle *asle = dev_priv->opregion.asle;
168         u32 blc_pwm_ctl;
169         
170         if (!(bclp & ASLE_BCLP_VALID))
171                 return ASLE_BACKLIGHT_FAIL;
172         
173         bclp &= ASLE_BCLP_MSK;
174         if (bclp < 0 || bclp > 255)
175                 return ASLE_BACKLIGHT_FAIL;
176         
177         blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
178         blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK;
179         I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | ((bclp * 0x101) -1));
180         asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
181         
182         return 0;
183 }
184
185 static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi)
186 {
187         return 0;
188 }
189
190 static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb)
191 {
192         struct drm_i915_private *dev_priv = dev->dev_private;
193         if (pfmb & ASLE_PFMB_PWM_VALID) {
194                 u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
195                 u32 pwm = pfmb & ASLE_PFMB_PWM_MASK;
196                 blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK;
197                 pwm = pwm >> 9;
198                 // FIXME - what do we do with the PWM?
199         }
200         return 0;
201 }
202
203 static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
204 {
205         if (!(pfit & ASLE_PFIT_VALID))
206                 return ASLE_PFIT_FAIL;
207         return 0;
208 }
209
210 void opregion_asle_intr(struct drm_device *dev)
211 {
212         struct drm_i915_private *dev_priv = dev->dev_private;
213         struct opregion_asle *asle = dev_priv->opregion.asle;
214         u32 asle_stat = 0;
215         u32 asle_req;
216
217         if (!asle)
218                 return;
219
220         asle_req = asle->aslc & ASLE_REQ_MSK;
221         
222         if (!asle_req) {
223                 DRM_DEBUG("non asle set request??\n");
224                 return;
225         }
226
227         if (asle_req & ASLE_SET_ALS_ILLUM)
228                 asle_stat |= asle_set_als_illum(dev, asle->alsi);
229         
230         if (asle_req & ASLE_SET_BACKLIGHT)
231                 asle_stat |= asle_set_backlight(dev, asle->bclp);
232         
233         if (asle_req & ASLE_SET_PFIT)
234                 asle_stat |= asle_set_pfit(dev, asle->pfit);
235         
236         if (asle_req & ASLE_SET_PWM_FREQ)
237                 asle_stat |= asle_set_pwm_freq(dev, asle->pfmb);
238         
239         asle->aslc = asle_stat;
240 }
241
242 #define ASLE_ALS_EN    (1<<0)
243 #define ASLE_BLC_EN    (1<<1)
244 #define ASLE_PFIT_EN   (1<<2)
245 #define ASLE_PFMB_EN   (1<<3)
246
247 void opregion_enable_asle(struct drm_device *dev)
248 {
249         struct drm_i915_private *dev_priv = dev->dev_private;
250         struct opregion_asle *asle = dev_priv->opregion.asle;
251         u32 mask = 0;
252
253         if (asle) {
254                 u32 pipeb_stats = I915_READ(PIPEBSTAT);
255                 if (IS_MOBILE(dev)) {
256                         /* Some hardware uses the legacy backlight controller
257                            to signal interrupts, so we need to set up pipe B
258                            to generate an IRQ on writes */
259                         I915_WRITE(PIPEBSTAT, pipeb_stats |= 
260                                    I915_LEGACY_BLC_EVENT_ENABLE);
261                         mask = I915_ASLE_INTERRUPT |
262                                 I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
263                 } else
264                         mask = I915_ASLE_INTERRUPT;
265                 
266                 dev_priv->irq_mask_reg &= ~mask;
267
268                 asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | 
269                         ASLE_PFMB_EN;
270                 asle->ardy = 1;
271         }
272 }
273
274 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
275 #define ACPI_EV_LID            (1<<1)
276 #define ACPI_EV_DOCK           (1<<2)
277
278 static struct intel_opregion *system_opregion;
279
280 int intel_opregion_video_event(struct notifier_block *nb, unsigned long val,
281                                void *data)
282 {
283         /* The only video events relevant to opregion are 0x80. These indicate
284            either a docking event, lid switch or display switch request. In
285            Linux, these are handled by the dock, button and video drivers.
286            We might want to fix the video driver to be opregion-aware in
287            future, but right now we just indicate to the firmware that the
288            request has been handled */
289         
290         struct opregion_acpi *acpi;
291
292         if (!system_opregion)
293                 return NOTIFY_DONE;
294         
295         acpi = system_opregion->acpi;
296         acpi->csts = 0;
297
298         return NOTIFY_OK;
299 }
300
301 static struct notifier_block intel_opregion_notifier = {
302         .notifier_call = intel_opregion_video_event,
303 };
304
305 int intel_opregion_init(struct drm_device *dev)
306 {
307         struct drm_i915_private *dev_priv = dev->dev_private;
308         struct intel_opregion *opregion = &dev_priv->opregion;
309         void *base;
310         u32 asls, mboxes;
311         int err = 0;
312         
313         pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
314         DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
315         if (asls == 0) {
316                 DRM_DEBUG("ACPI OpRegion not supported!\n");
317                 return -ENOTSUPP;
318         }
319         
320         base = ioremap(asls, OPREGION_SZ);
321         if (!base)
322                 return -ENOMEM;
323         
324         opregion->header = base;
325         if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
326                 DRM_DEBUG("opregion signature mismatch\n");
327                 err = -EINVAL;
328                 goto err_out;
329         }
330         
331         mboxes = opregion->header->mboxes;
332         if (mboxes & MBOX_ACPI) {
333                 DRM_DEBUG("Public ACPI methods supported\n");
334                 opregion->acpi = base + OPREGION_ACPI_OFFSET;
335         } else {
336                 DRM_DEBUG("Public ACPI methods not supported\n");
337                 err = -ENOTSUPP;
338                 goto err_out;
339         }
340         opregion->enabled = 1;
341         
342         if (mboxes & MBOX_SWSCI) {
343                 DRM_DEBUG("SWSCI supported\n");
344                 opregion->swsci = base + OPREGION_SWSCI_OFFSET;
345         }
346         if (mboxes & MBOX_ASLE) {
347                 DRM_DEBUG("ASLE supported\n");
348                 opregion->asle = base + OPREGION_ASLE_OFFSET;
349         }
350         
351         /* Notify BIOS we are ready to handle ACPI video ext notifs.
352          * Right now, all the events are handled by the ACPI video module.
353          * We don't actually need to do anything with them. */
354         opregion->acpi->csts = 0;
355         opregion->acpi->drdy = 1;
356
357         system_opregion = opregion;
358         register_acpi_notifier(&intel_opregion_notifier);
359         
360         return 0;
361         
362 err_out:
363         iounmap(opregion->header);
364         opregion->header = NULL;
365         return err;
366 }
367
368 void intel_opregion_free(struct drm_device *dev)
369 {
370         struct drm_i915_private *dev_priv = dev->dev_private;
371         struct intel_opregion *opregion = &dev_priv->opregion;
372         
373         if (!opregion->enabled)
374                 return;
375         
376         opregion->acpi->drdy = 0;
377         
378         system_opregion = NULL;
379         unregister_acpi_notifier(&intel_opregion_notifier);
380         
381         /* just clear all opregion memory pointers now */
382         iounmap(opregion->header);
383         opregion->header = NULL;
384         opregion->acpi = NULL;
385         opregion->swsci = NULL;
386         opregion->asle = NULL;
387         
388         opregion->enabled = 0;
389 }
390 #endif