]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/gpu/drm/i915/intel_opregion.c
OMAP: PM: SmartReflex: Fix possible null pointer read access
[mv-sheeva.git] / drivers / gpu / drm / i915 / intel_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 #include <acpi/video.h>
30
31 #include "drmP.h"
32 #include "i915_drm.h"
33 #include "i915_drv.h"
34 #include "intel_drv.h"
35
36 #define PCI_ASLE 0xe4
37 #define PCI_ASLS 0xfc
38
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    0x400
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_FAILED   (1<<10)
117 #define ASLE_BACKLIGHT_FAILED   (1<<12)
118 #define ASLE_PFIT_FAILED        (1<<14)
119 #define ASLE_PWM_FREQ_FAILED    (1<<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 #define ACPI_OTHER_OUTPUT (0<<8)
140 #define ACPI_VGA_OUTPUT (1<<8)
141 #define ACPI_TV_OUTPUT (2<<8)
142 #define ACPI_DIGITAL_OUTPUT (3<<8)
143 #define ACPI_LVDS_OUTPUT (4<<8)
144
145 #ifdef CONFIG_ACPI
146 static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
147 {
148         struct drm_i915_private *dev_priv = dev->dev_private;
149         struct opregion_asle *asle = dev_priv->opregion.asle;
150         u32 max;
151
152         if (!(bclp & ASLE_BCLP_VALID))
153                 return ASLE_BACKLIGHT_FAILED;
154
155         bclp &= ASLE_BCLP_MSK;
156         if (bclp > 255)
157                 return ASLE_BACKLIGHT_FAILED;
158
159         max = intel_panel_get_max_backlight(dev);
160         intel_panel_set_backlight(dev, bclp * max / 255);
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_FAILED;
192         return 0;
193 }
194
195 void intel_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_DRIVER("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 /* Only present on Ironlake+ */
228 void intel_opregion_gse_intr(struct drm_device *dev)
229 {
230         struct drm_i915_private *dev_priv = dev->dev_private;
231         struct opregion_asle *asle = dev_priv->opregion.asle;
232         u32 asle_stat = 0;
233         u32 asle_req;
234
235         if (!asle)
236                 return;
237
238         asle_req = asle->aslc & ASLE_REQ_MSK;
239
240         if (!asle_req) {
241                 DRM_DEBUG_DRIVER("non asle set request??\n");
242                 return;
243         }
244
245         if (asle_req & ASLE_SET_ALS_ILLUM) {
246                 DRM_DEBUG_DRIVER("Illum is not supported\n");
247                 asle_stat |= ASLE_ALS_ILLUM_FAILED;
248         }
249
250         if (asle_req & ASLE_SET_BACKLIGHT)
251                 asle_stat |= asle_set_backlight(dev, asle->bclp);
252
253         if (asle_req & ASLE_SET_PFIT) {
254                 DRM_DEBUG_DRIVER("Pfit is not supported\n");
255                 asle_stat |= ASLE_PFIT_FAILED;
256         }
257
258         if (asle_req & ASLE_SET_PWM_FREQ) {
259                 DRM_DEBUG_DRIVER("PWM freq is not supported\n");
260                 asle_stat |= ASLE_PWM_FREQ_FAILED;
261         }
262
263         asle->aslc = asle_stat;
264 }
265 #define ASLE_ALS_EN    (1<<0)
266 #define ASLE_BLC_EN    (1<<1)
267 #define ASLE_PFIT_EN   (1<<2)
268 #define ASLE_PFMB_EN   (1<<3)
269
270 void intel_opregion_enable_asle(struct drm_device *dev)
271 {
272         struct drm_i915_private *dev_priv = dev->dev_private;
273         struct opregion_asle *asle = dev_priv->opregion.asle;
274
275         if (asle) {
276                 if (IS_MOBILE(dev))
277                         intel_enable_asle(dev);
278
279                 asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |
280                         ASLE_PFMB_EN;
281                 asle->ardy = 1;
282         }
283 }
284
285 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
286 #define ACPI_EV_LID            (1<<1)
287 #define ACPI_EV_DOCK           (1<<2)
288
289 static struct intel_opregion *system_opregion;
290
291 static int intel_opregion_video_event(struct notifier_block *nb,
292                                       unsigned long val, void *data)
293 {
294         /* The only video events relevant to opregion are 0x80. These indicate
295            either a docking event, lid switch or display switch request. In
296            Linux, these are handled by the dock, button and video drivers.
297            We might want to fix the video driver to be opregion-aware in
298            future, but right now we just indicate to the firmware that the
299            request has been handled */
300
301         struct opregion_acpi *acpi;
302
303         if (!system_opregion)
304                 return NOTIFY_DONE;
305
306         acpi = system_opregion->acpi;
307         acpi->csts = 0;
308
309         return NOTIFY_OK;
310 }
311
312 static struct notifier_block intel_opregion_notifier = {
313         .notifier_call = intel_opregion_video_event,
314 };
315
316 /*
317  * Initialise the DIDL field in opregion. This passes a list of devices to
318  * the firmware. Values are defined by section B.4.2 of the ACPI specification
319  * (version 3)
320  */
321
322 static void intel_didl_outputs(struct drm_device *dev)
323 {
324         struct drm_i915_private *dev_priv = dev->dev_private;
325         struct intel_opregion *opregion = &dev_priv->opregion;
326         struct drm_connector *connector;
327         acpi_handle handle;
328         struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL;
329         unsigned long long device_id;
330         acpi_status status;
331         int i = 0;
332
333         handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
334         if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev)))
335                 return;
336
337         if (acpi_is_video_device(acpi_dev))
338                 acpi_video_bus = acpi_dev;
339         else {
340                 list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {
341                         if (acpi_is_video_device(acpi_cdev)) {
342                                 acpi_video_bus = acpi_cdev;
343                                 break;
344                         }
345                 }
346         }
347
348         if (!acpi_video_bus) {
349                 printk(KERN_WARNING "No ACPI video bus found\n");
350                 return;
351         }
352
353         list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
354                 if (i >= 8) {
355                         dev_printk (KERN_ERR, &dev->pdev->dev,
356                                     "More than 8 outputs detected\n");
357                         return;
358                 }
359                 status =
360                         acpi_evaluate_integer(acpi_cdev->handle, "_ADR",
361                                                 NULL, &device_id);
362                 if (ACPI_SUCCESS(status)) {
363                         if (!device_id)
364                                 goto blind_set;
365                         opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f);
366                         i++;
367                 }
368         }
369
370 end:
371         /* If fewer than 8 outputs, the list must be null terminated */
372         if (i < 8)
373                 opregion->acpi->didl[i] = 0;
374         return;
375
376 blind_set:
377         i = 0;
378         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
379                 int output_type = ACPI_OTHER_OUTPUT;
380                 if (i >= 8) {
381                         dev_printk (KERN_ERR, &dev->pdev->dev,
382                                     "More than 8 outputs detected\n");
383                         return;
384                 }
385                 switch (connector->connector_type) {
386                 case DRM_MODE_CONNECTOR_VGA:
387                 case DRM_MODE_CONNECTOR_DVIA:
388                         output_type = ACPI_VGA_OUTPUT;
389                         break;
390                 case DRM_MODE_CONNECTOR_Composite:
391                 case DRM_MODE_CONNECTOR_SVIDEO:
392                 case DRM_MODE_CONNECTOR_Component:
393                 case DRM_MODE_CONNECTOR_9PinDIN:
394                         output_type = ACPI_TV_OUTPUT;
395                         break;
396                 case DRM_MODE_CONNECTOR_DVII:
397                 case DRM_MODE_CONNECTOR_DVID:
398                 case DRM_MODE_CONNECTOR_DisplayPort:
399                 case DRM_MODE_CONNECTOR_HDMIA:
400                 case DRM_MODE_CONNECTOR_HDMIB:
401                         output_type = ACPI_DIGITAL_OUTPUT;
402                         break;
403                 case DRM_MODE_CONNECTOR_LVDS:
404                         output_type = ACPI_LVDS_OUTPUT;
405                         break;
406                 }
407                 opregion->acpi->didl[i] |= (1<<31) | output_type | i;
408                 i++;
409         }
410         goto end;
411 }
412
413 void intel_opregion_init(struct drm_device *dev)
414 {
415         struct drm_i915_private *dev_priv = dev->dev_private;
416         struct intel_opregion *opregion = &dev_priv->opregion;
417
418         if (!opregion->header)
419                 return;
420
421         if (opregion->acpi) {
422                 if (drm_core_check_feature(dev, DRIVER_MODESET))
423                         intel_didl_outputs(dev);
424
425                 /* Notify BIOS we are ready to handle ACPI video ext notifs.
426                  * Right now, all the events are handled by the ACPI video module.
427                  * We don't actually need to do anything with them. */
428                 opregion->acpi->csts = 0;
429                 opregion->acpi->drdy = 1;
430
431                 system_opregion = opregion;
432                 register_acpi_notifier(&intel_opregion_notifier);
433         }
434
435         if (opregion->asle)
436                 intel_opregion_enable_asle(dev);
437 }
438
439 void intel_opregion_fini(struct drm_device *dev)
440 {
441         struct drm_i915_private *dev_priv = dev->dev_private;
442         struct intel_opregion *opregion = &dev_priv->opregion;
443
444         if (!opregion->header)
445                 return;
446
447         if (opregion->acpi) {
448                 opregion->acpi->drdy = 0;
449
450                 system_opregion = NULL;
451                 unregister_acpi_notifier(&intel_opregion_notifier);
452         }
453
454         /* just clear all opregion memory pointers now */
455         iounmap(opregion->header);
456         opregion->header = NULL;
457         opregion->acpi = NULL;
458         opregion->swsci = NULL;
459         opregion->asle = NULL;
460         opregion->vbt = NULL;
461 }
462 #endif
463
464 int intel_opregion_setup(struct drm_device *dev)
465 {
466         struct drm_i915_private *dev_priv = dev->dev_private;
467         struct intel_opregion *opregion = &dev_priv->opregion;
468         void *base;
469         u32 asls, mboxes;
470         int err = 0;
471
472         pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
473         DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);
474         if (asls == 0) {
475                 DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");
476                 return -ENOTSUPP;
477         }
478
479         base = ioremap(asls, OPREGION_SIZE);
480         if (!base)
481                 return -ENOMEM;
482
483         if (memcmp(base, OPREGION_SIGNATURE, 16)) {
484                 DRM_DEBUG_DRIVER("opregion signature mismatch\n");
485                 err = -EINVAL;
486                 goto err_out;
487         }
488         opregion->header = base;
489         opregion->vbt = base + OPREGION_VBT_OFFSET;
490
491         mboxes = opregion->header->mboxes;
492         if (mboxes & MBOX_ACPI) {
493                 DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
494                 opregion->acpi = base + OPREGION_ACPI_OFFSET;
495         }
496
497         if (mboxes & MBOX_SWSCI) {
498                 DRM_DEBUG_DRIVER("SWSCI supported\n");
499                 opregion->swsci = base + OPREGION_SWSCI_OFFSET;
500         }
501         if (mboxes & MBOX_ASLE) {
502                 DRM_DEBUG_DRIVER("ASLE supported\n");
503                 opregion->asle = base + OPREGION_ASLE_OFFSET;
504         }
505
506         return 0;
507
508 err_out:
509         iounmap(base);
510         return err;
511 }