radeon: remove unused legacy state
[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 #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
252         if (asle) {
253                 if (IS_MOBILE(dev)) {
254                         u32 pipeb_stats = I915_READ(PIPEBSTAT);
255                         /* Some hardware uses the legacy backlight controller
256                            to signal interrupts, so we need to set up pipe B
257                            to generate an IRQ on writes */
258                         pipeb_stats |= I915_LEGACY_BLC_EVENT_ENABLE;
259                         I915_WRITE(PIPEBSTAT, pipeb_stats);
260
261                         dev_priv->irq_mask_reg &=
262                                 ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
263                 }
264
265                 dev_priv->irq_mask_reg &= ~I915_ASLE_INTERRUPT;
266
267                 asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | 
268                         ASLE_PFMB_EN;
269                 asle->ardy = 1;
270         }
271 }
272
273 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
274 #define ACPI_EV_LID            (1<<1)
275 #define ACPI_EV_DOCK           (1<<2)
276
277 static struct intel_opregion *system_opregion;
278
279 int intel_opregion_video_event(struct notifier_block *nb, unsigned long val,
280                                void *data)
281 {
282         /* The only video events relevant to opregion are 0x80. These indicate
283            either a docking event, lid switch or display switch request. In
284            Linux, these are handled by the dock, button and video drivers.
285            We might want to fix the video driver to be opregion-aware in
286            future, but right now we just indicate to the firmware that the
287            request has been handled */
288         
289         struct opregion_acpi *acpi;
290
291         if (!system_opregion)
292                 return NOTIFY_DONE;
293         
294         acpi = system_opregion->acpi;
295         acpi->csts = 0;
296
297         return NOTIFY_OK;
298 }
299
300 static struct notifier_block intel_opregion_notifier = {
301         .notifier_call = intel_opregion_video_event,
302 };
303
304 int intel_opregion_init(struct drm_device *dev)
305 {
306         struct drm_i915_private *dev_priv = dev->dev_private;
307         struct intel_opregion *opregion = &dev_priv->opregion;
308         void *base;
309         u32 asls, mboxes;
310         int err = 0;
311         
312         pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
313         DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
314         if (asls == 0) {
315                 DRM_DEBUG("ACPI OpRegion not supported!\n");
316                 return -ENOTSUPP;
317         }
318         
319         base = ioremap(asls, OPREGION_SZ);
320         if (!base)
321                 return -ENOMEM;
322         
323         opregion->header = base;
324         if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
325                 DRM_DEBUG("opregion signature mismatch\n");
326                 err = -EINVAL;
327                 goto err_out;
328         }
329         
330         mboxes = opregion->header->mboxes;
331         if (mboxes & MBOX_ACPI) {
332                 DRM_DEBUG("Public ACPI methods supported\n");
333                 opregion->acpi = base + OPREGION_ACPI_OFFSET;
334         } else {
335                 DRM_DEBUG("Public ACPI methods not supported\n");
336                 err = -ENOTSUPP;
337                 goto err_out;
338         }
339         opregion->enabled = 1;
340         
341         if (mboxes & MBOX_SWSCI) {
342                 DRM_DEBUG("SWSCI supported\n");
343                 opregion->swsci = base + OPREGION_SWSCI_OFFSET;
344         }
345         if (mboxes & MBOX_ASLE) {
346                 DRM_DEBUG("ASLE supported\n");
347                 opregion->asle = base + OPREGION_ASLE_OFFSET;
348         }
349         
350         /* Notify BIOS we are ready to handle ACPI video ext notifs.
351          * Right now, all the events are handled by the ACPI video module.
352          * We don't actually need to do anything with them. */
353         opregion->acpi->csts = 0;
354         opregion->acpi->drdy = 1;
355
356         system_opregion = opregion;
357         register_acpi_notifier(&intel_opregion_notifier);
358         
359         return 0;
360         
361 err_out:
362         iounmap(opregion->header);
363         opregion->header = NULL;
364         return err;
365 }
366
367 void intel_opregion_free(struct drm_device *dev)
368 {
369         struct drm_i915_private *dev_priv = dev->dev_private;
370         struct intel_opregion *opregion = &dev_priv->opregion;
371         
372         if (!opregion->enabled)
373                 return;
374         
375         opregion->acpi->drdy = 0;
376         
377         system_opregion = NULL;
378         unregister_acpi_notifier(&intel_opregion_notifier);
379         
380         /* just clear all opregion memory pointers now */
381         iounmap(opregion->header);
382         opregion->header = NULL;
383         opregion->acpi = NULL;
384         opregion->swsci = NULL;
385         opregion->asle = NULL;
386         
387         opregion->enabled = 0;
388 }
389 #endif