linux/drivers/gpu/drm/i915/i915_opregion.c
<<
>>
Prefs
   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
  35#define PCI_ASLE 0xe4
  36#define PCI_LBPC 0xf4
  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
  51struct opregion_header {
  52       u8 signature[16];
  53       u32 size;
  54       u32 opregion_ver;
  55       u8 bios_ver[32];
  56       u8 vbios_ver[16];
  57       u8 driver_ver[16];
  58       u32 mboxes;
  59       u8 reserved[164];
  60} __attribute__((packed));
  61
  62/* OpRegion mailbox #1: public ACPI methods */
  63struct opregion_acpi {
  64       u32 drdy;       /* driver readiness */
  65       u32 csts;       /* notification status */
  66       u32 cevt;       /* current event */
  67       u8 rsvd1[20];
  68       u32 didl[8];    /* supported display devices ID list */
  69       u32 cpdl[8];    /* currently presented display list */
  70       u32 cadl[8];    /* currently active display list */
  71       u32 nadl[8];    /* next active devices list */
  72       u32 aslp;       /* ASL sleep time-out */
  73       u32 tidx;       /* toggle table index */
  74       u32 chpd;       /* current hotplug enable indicator */
  75       u32 clid;       /* current lid state*/
  76       u32 cdck;       /* current docking state */
  77       u32 sxsw;       /* Sx state resume */
  78       u32 evts;       /* ASL supported events */
  79       u32 cnot;       /* current OS notification */
  80       u32 nrdy;       /* driver status */
  81       u8 rsvd2[60];
  82} __attribute__((packed));
  83
  84/* OpRegion mailbox #2: SWSCI */
  85struct opregion_swsci {
  86       u32 scic;       /* SWSCI command|status|data */
  87       u32 parm;       /* command parameters */
  88       u32 dslp;       /* driver sleep time-out */
  89       u8 rsvd[244];
  90} __attribute__((packed));
  91
  92/* OpRegion mailbox #3: ASLE */
  93struct opregion_asle {
  94       u32 ardy;       /* driver readiness */
  95       u32 aslc;       /* ASLE interrupt command */
  96       u32 tche;       /* technology enabled indicator */
  97       u32 alsi;       /* current ALS illuminance reading */
  98       u32 bclp;       /* backlight brightness to set */
  99       u32 pfit;       /* panel fitting state */
 100       u32 cblv;       /* current brightness level */
 101       u16 bclm[20];   /* backlight level duty cycle mapping table */
 102       u32 cpfm;       /* current panel fitting mode */
 103       u32 epfm;       /* enabled panel fitting modes */
 104       u8 plut[74];    /* panel LUT and identifier */
 105       u32 pfmb;       /* PWM freq and min brightness */
 106       u8 rsvd[102];
 107} __attribute__((packed));
 108
 109/* ASLE irq request bits */
 110#define ASLE_SET_ALS_ILLUM     (1 << 0)
 111#define ASLE_SET_BACKLIGHT     (1 << 1)
 112#define ASLE_SET_PFIT          (1 << 2)
 113#define ASLE_SET_PWM_FREQ      (1 << 3)
 114#define ASLE_REQ_MSK           0xf
 115
 116/* response bits of ASLE irq request */
 117#define ASLE_ALS_ILLUM_FAIL    (2<<10)
 118#define ASLE_BACKLIGHT_FAIL    (2<<12)
 119#define ASLE_PFIT_FAIL         (2<<14)
 120#define ASLE_PWM_FREQ_FAIL     (2<<16)
 121
 122/* ASLE backlight brightness to set */
 123#define ASLE_BCLP_VALID                (1<<31)
 124#define ASLE_BCLP_MSK          (~(1<<31))
 125
 126/* ASLE panel fitting request */
 127#define ASLE_PFIT_VALID         (1<<31)
 128#define ASLE_PFIT_CENTER (1<<0)
 129#define ASLE_PFIT_STRETCH_TEXT (1<<1)
 130#define ASLE_PFIT_STRETCH_GFX (1<<2)
 131
 132/* PWM frequency and minimum brightness */
 133#define ASLE_PFMB_BRIGHTNESS_MASK (0xff)
 134#define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)
 135#define ASLE_PFMB_PWM_MASK (0x7ffffe00)
 136#define ASLE_PFMB_PWM_VALID (1<<31)
 137
 138#define ASLE_CBLV_VALID         (1<<31)
 139
 140#define ACPI_OTHER_OUTPUT (0<<8)
 141#define ACPI_VGA_OUTPUT (1<<8)
 142#define ACPI_TV_OUTPUT (2<<8)
 143#define ACPI_DIGITAL_OUTPUT (3<<8)
 144#define ACPI_LVDS_OUTPUT (4<<8)
 145
 146static 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 blc_pwm_ctl, blc_pwm_ctl2;
 151        u32 max_backlight, level, shift;
 152
 153        if (!(bclp & ASLE_BCLP_VALID))
 154                return ASLE_BACKLIGHT_FAIL;
 155
 156        bclp &= ASLE_BCLP_MSK;
 157        if (bclp < 0 || bclp > 255)
 158                return ASLE_BACKLIGHT_FAIL;
 159
 160        blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
 161        blc_pwm_ctl2 = I915_READ(BLC_PWM_CTL2);
 162
 163        if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE))
 164                pci_write_config_dword(dev->pdev, PCI_LBPC, bclp);
 165        else {
 166                if (IS_IGD(dev)) {
 167                        blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
 168                        max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> 
 169                                        BACKLIGHT_MODULATION_FREQ_SHIFT;
 170                        shift = BACKLIGHT_DUTY_CYCLE_SHIFT + 1;
 171                } else {
 172                        blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK;
 173                        max_backlight = ((blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> 
 174                                        BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
 175                        shift = BACKLIGHT_DUTY_CYCLE_SHIFT;
 176                }
 177                level = (bclp * max_backlight) / 255;
 178                I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | (level << shift));
 179        }
 180        asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
 181
 182        return 0;
 183}
 184
 185static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi)
 186{
 187        /* alsi is the current ALS reading in lux. 0 indicates below sensor
 188           range, 0xffff indicates above sensor range. 1-0xfffe are valid */
 189        return 0;
 190}
 191
 192static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb)
 193{
 194        struct drm_i915_private *dev_priv = dev->dev_private;
 195        if (pfmb & ASLE_PFMB_PWM_VALID) {
 196                u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
 197                u32 pwm = pfmb & ASLE_PFMB_PWM_MASK;
 198                blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK;
 199                pwm = pwm >> 9;
 200                /* FIXME - what do we do with the PWM? */
 201        }
 202        return 0;
 203}
 204
 205static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
 206{
 207        /* Panel fitting is currently controlled by the X code, so this is a
 208           noop until modesetting support works fully */
 209        if (!(pfit & ASLE_PFIT_VALID))
 210                return ASLE_PFIT_FAIL;
 211        return 0;
 212}
 213
 214void opregion_asle_intr(struct drm_device *dev)
 215{
 216        struct drm_i915_private *dev_priv = dev->dev_private;
 217        struct opregion_asle *asle = dev_priv->opregion.asle;
 218        u32 asle_stat = 0;
 219        u32 asle_req;
 220
 221        if (!asle)
 222                return;
 223
 224        asle_req = asle->aslc & ASLE_REQ_MSK;
 225
 226        if (!asle_req) {
 227                DRM_DEBUG("non asle set request??\n");
 228                return;
 229        }
 230
 231        if (asle_req & ASLE_SET_ALS_ILLUM)
 232                asle_stat |= asle_set_als_illum(dev, asle->alsi);
 233
 234        if (asle_req & ASLE_SET_BACKLIGHT)
 235                asle_stat |= asle_set_backlight(dev, asle->bclp);
 236
 237        if (asle_req & ASLE_SET_PFIT)
 238                asle_stat |= asle_set_pfit(dev, asle->pfit);
 239
 240        if (asle_req & ASLE_SET_PWM_FREQ)
 241                asle_stat |= asle_set_pwm_freq(dev, asle->pfmb);
 242
 243        asle->aslc = asle_stat;
 244}
 245
 246#define ASLE_ALS_EN    (1<<0)
 247#define ASLE_BLC_EN    (1<<1)
 248#define ASLE_PFIT_EN   (1<<2)
 249#define ASLE_PFMB_EN   (1<<3)
 250
 251void opregion_enable_asle(struct drm_device *dev)
 252{
 253        struct drm_i915_private *dev_priv = dev->dev_private;
 254        struct opregion_asle *asle = dev_priv->opregion.asle;
 255
 256        if (asle) {
 257                if (IS_MOBILE(dev)) {
 258                        unsigned long irqflags;
 259
 260                        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
 261                        i915_enable_pipestat(dev_priv, 1,
 262                                             I915_LEGACY_BLC_EVENT_ENABLE);
 263                        spin_unlock_irqrestore(&dev_priv->user_irq_lock,
 264                                               irqflags);
 265                }
 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
 277static struct intel_opregion *system_opregion;
 278
 279static int intel_opregion_video_event(struct notifier_block *nb,
 280                                      unsigned long val, 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
 300static struct notifier_block intel_opregion_notifier = {
 301        .notifier_call = intel_opregion_video_event,
 302};
 303
 304/*
 305 * Initialise the DIDL field in opregion. This passes a list of devices to
 306 * the firmware. Values are defined by section B.4.2 of the ACPI specification
 307 * (version 3)
 308 */
 309
 310static void intel_didl_outputs(struct drm_device *dev)
 311{
 312        struct drm_i915_private *dev_priv = dev->dev_private;
 313        struct intel_opregion *opregion = &dev_priv->opregion;
 314        struct drm_connector *connector;
 315        int i = 0;
 316
 317        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 318                int output_type = ACPI_OTHER_OUTPUT;
 319                if (i >= 8) {
 320                        dev_printk (KERN_ERR, &dev->pdev->dev,
 321                                    "More than 8 outputs detected\n");
 322                        return;
 323                }
 324                switch (connector->connector_type) {
 325                case DRM_MODE_CONNECTOR_VGA:
 326                case DRM_MODE_CONNECTOR_DVIA:
 327                        output_type = ACPI_VGA_OUTPUT;
 328                        break;
 329                case DRM_MODE_CONNECTOR_Composite:
 330                case DRM_MODE_CONNECTOR_SVIDEO:
 331                case DRM_MODE_CONNECTOR_Component:
 332                case DRM_MODE_CONNECTOR_9PinDIN:
 333                        output_type = ACPI_TV_OUTPUT;
 334                        break;
 335                case DRM_MODE_CONNECTOR_DVII:
 336                case DRM_MODE_CONNECTOR_DVID:
 337                case DRM_MODE_CONNECTOR_DisplayPort:
 338                case DRM_MODE_CONNECTOR_HDMIA:
 339                case DRM_MODE_CONNECTOR_HDMIB:
 340                        output_type = ACPI_DIGITAL_OUTPUT;
 341                        break;
 342                case DRM_MODE_CONNECTOR_LVDS:
 343                        output_type = ACPI_LVDS_OUTPUT;
 344                        break;
 345                }
 346                opregion->acpi->didl[i] |= (1<<31) | output_type | i;
 347                i++;
 348        }
 349
 350        /* If fewer than 8 outputs, the list must be null terminated */
 351        if (i < 8)
 352                opregion->acpi->didl[i] = 0;
 353}
 354
 355int intel_opregion_init(struct drm_device *dev, int resume)
 356{
 357        struct drm_i915_private *dev_priv = dev->dev_private;
 358        struct intel_opregion *opregion = &dev_priv->opregion;
 359        void *base;
 360        u32 asls, mboxes;
 361        int err = 0;
 362
 363        pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
 364        DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
 365        if (asls == 0) {
 366                DRM_DEBUG("ACPI OpRegion not supported!\n");
 367                return -ENOTSUPP;
 368        }
 369
 370        base = ioremap(asls, OPREGION_SZ);
 371        if (!base)
 372                return -ENOMEM;
 373
 374        opregion->header = base;
 375        if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
 376                DRM_DEBUG("opregion signature mismatch\n");
 377                err = -EINVAL;
 378                goto err_out;
 379        }
 380
 381        mboxes = opregion->header->mboxes;
 382        if (mboxes & MBOX_ACPI) {
 383                DRM_DEBUG("Public ACPI methods supported\n");
 384                opregion->acpi = base + OPREGION_ACPI_OFFSET;
 385                if (drm_core_check_feature(dev, DRIVER_MODESET))
 386                        intel_didl_outputs(dev);
 387        } else {
 388                DRM_DEBUG("Public ACPI methods not supported\n");
 389                err = -ENOTSUPP;
 390                goto err_out;
 391        }
 392        opregion->enabled = 1;
 393
 394        if (mboxes & MBOX_SWSCI) {
 395                DRM_DEBUG("SWSCI supported\n");
 396                opregion->swsci = base + OPREGION_SWSCI_OFFSET;
 397        }
 398        if (mboxes & MBOX_ASLE) {
 399                DRM_DEBUG("ASLE supported\n");
 400                opregion->asle = base + OPREGION_ASLE_OFFSET;
 401                opregion_enable_asle(dev);
 402        }
 403
 404        if (!resume)
 405                acpi_video_register();
 406
 407
 408        /* Notify BIOS we are ready to handle ACPI video ext notifs.
 409         * Right now, all the events are handled by the ACPI video module.
 410         * We don't actually need to do anything with them. */
 411        opregion->acpi->csts = 0;
 412        opregion->acpi->drdy = 1;
 413
 414        system_opregion = opregion;
 415        register_acpi_notifier(&intel_opregion_notifier);
 416
 417        return 0;
 418
 419err_out:
 420        iounmap(opregion->header);
 421        opregion->header = NULL;
 422        return err;
 423}
 424
 425void intel_opregion_free(struct drm_device *dev, int suspend)
 426{
 427        struct drm_i915_private *dev_priv = dev->dev_private;
 428        struct intel_opregion *opregion = &dev_priv->opregion;
 429
 430        if (!opregion->enabled)
 431                return;
 432
 433        if (!suspend)
 434                acpi_video_unregister();
 435
 436        opregion->acpi->drdy = 0;
 437
 438        system_opregion = NULL;
 439        unregister_acpi_notifier(&intel_opregion_notifier);
 440
 441        /* just clear all opregion memory pointers now */
 442        iounmap(opregion->header);
 443        opregion->header = NULL;
 444        opregion->acpi = NULL;
 445        opregion->swsci = NULL;
 446        opregion->asle = NULL;
 447
 448        opregion->enabled = 0;
 449}
 450