linux/drivers/gpu/drm/i915/intel_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 <linux/acpi_io.h>
  30#include <acpi/video.h>
  31
  32#include "drmP.h"
  33#include "i915_drm.h"
  34#include "i915_drv.h"
  35#include "intel_drv.h"
  36
  37#define PCI_ASLE 0xe4
  38#define PCI_ASLS 0xfc
  39
  40#define OPREGION_HEADER_OFFSET 0
  41#define OPREGION_ACPI_OFFSET   0x100
  42#define   ACPI_CLID 0x01ac /* current lid state indicator */
  43#define   ACPI_CDCK 0x01b0 /* current docking state indicator */
  44#define OPREGION_SWSCI_OFFSET  0x200
  45#define OPREGION_ASLE_OFFSET   0x300
  46#define OPREGION_VBT_OFFSET    0x400
  47
  48#define OPREGION_SIGNATURE "IntelGraphicsMem"
  49#define MBOX_ACPI      (1<<0)
  50#define MBOX_SWSCI     (1<<1)
  51#define MBOX_ASLE      (1<<2)
  52
  53struct opregion_header {
  54        u8 signature[16];
  55        u32 size;
  56        u32 opregion_ver;
  57        u8 bios_ver[32];
  58        u8 vbios_ver[16];
  59        u8 driver_ver[16];
  60        u32 mboxes;
  61        u8 reserved[164];
  62} __attribute__((packed));
  63
  64/* OpRegion mailbox #1: public ACPI methods */
  65struct opregion_acpi {
  66        u32 drdy;       /* driver readiness */
  67        u32 csts;       /* notification status */
  68        u32 cevt;       /* current event */
  69        u8 rsvd1[20];
  70        u32 didl[8];    /* supported display devices ID list */
  71        u32 cpdl[8];    /* currently presented display list */
  72        u32 cadl[8];    /* currently active display list */
  73        u32 nadl[8];    /* next active devices list */
  74        u32 aslp;       /* ASL sleep time-out */
  75        u32 tidx;       /* toggle table index */
  76        u32 chpd;       /* current hotplug enable indicator */
  77        u32 clid;       /* current lid state*/
  78        u32 cdck;       /* current docking state */
  79        u32 sxsw;       /* Sx state resume */
  80        u32 evts;       /* ASL supported events */
  81        u32 cnot;       /* current OS notification */
  82        u32 nrdy;       /* driver status */
  83        u8 rsvd2[60];
  84} __attribute__((packed));
  85
  86/* OpRegion mailbox #2: SWSCI */
  87struct opregion_swsci {
  88        u32 scic;       /* SWSCI command|status|data */
  89        u32 parm;       /* command parameters */
  90        u32 dslp;       /* driver sleep time-out */
  91        u8 rsvd[244];
  92} __attribute__((packed));
  93
  94/* OpRegion mailbox #3: ASLE */
  95struct opregion_asle {
  96        u32 ardy;       /* driver readiness */
  97        u32 aslc;       /* ASLE interrupt command */
  98        u32 tche;       /* technology enabled indicator */
  99        u32 alsi;       /* current ALS illuminance reading */
 100        u32 bclp;       /* backlight brightness to set */
 101        u32 pfit;       /* panel fitting state */
 102        u32 cblv;       /* current brightness level */
 103        u16 bclm[20];   /* backlight level duty cycle mapping table */
 104        u32 cpfm;       /* current panel fitting mode */
 105        u32 epfm;       /* enabled panel fitting modes */
 106        u8 plut[74];    /* panel LUT and identifier */
 107        u32 pfmb;       /* PWM freq and min brightness */
 108        u8 rsvd[102];
 109} __attribute__((packed));
 110
 111/* ASLE irq request bits */
 112#define ASLE_SET_ALS_ILLUM     (1 << 0)
 113#define ASLE_SET_BACKLIGHT     (1 << 1)
 114#define ASLE_SET_PFIT          (1 << 2)
 115#define ASLE_SET_PWM_FREQ      (1 << 3)
 116#define ASLE_REQ_MSK           0xf
 117
 118/* response bits of ASLE irq request */
 119#define ASLE_ALS_ILLUM_FAILED   (1<<10)
 120#define ASLE_BACKLIGHT_FAILED   (1<<12)
 121#define ASLE_PFIT_FAILED        (1<<14)
 122#define ASLE_PWM_FREQ_FAILED    (1<<16)
 123
 124/* ASLE backlight brightness to set */
 125#define ASLE_BCLP_VALID                (1<<31)
 126#define ASLE_BCLP_MSK          (~(1<<31))
 127
 128/* ASLE panel fitting request */
 129#define ASLE_PFIT_VALID         (1<<31)
 130#define ASLE_PFIT_CENTER (1<<0)
 131#define ASLE_PFIT_STRETCH_TEXT (1<<1)
 132#define ASLE_PFIT_STRETCH_GFX (1<<2)
 133
 134/* PWM frequency and minimum brightness */
 135#define ASLE_PFMB_BRIGHTNESS_MASK (0xff)
 136#define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)
 137#define ASLE_PFMB_PWM_MASK (0x7ffffe00)
 138#define ASLE_PFMB_PWM_VALID (1<<31)
 139
 140#define ASLE_CBLV_VALID         (1<<31)
 141
 142#define ACPI_OTHER_OUTPUT (0<<8)
 143#define ACPI_VGA_OUTPUT (1<<8)
 144#define ACPI_TV_OUTPUT (2<<8)
 145#define ACPI_DIGITAL_OUTPUT (3<<8)
 146#define ACPI_LVDS_OUTPUT (4<<8)
 147
 148#ifdef CONFIG_ACPI
 149static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 150{
 151        struct drm_i915_private *dev_priv = dev->dev_private;
 152        struct opregion_asle *asle = dev_priv->opregion.asle;
 153        u32 max;
 154
 155        if (!(bclp & ASLE_BCLP_VALID))
 156                return ASLE_BACKLIGHT_FAILED;
 157
 158        bclp &= ASLE_BCLP_MSK;
 159        if (bclp > 255)
 160                return ASLE_BACKLIGHT_FAILED;
 161
 162        max = intel_panel_get_max_backlight(dev);
 163        intel_panel_set_backlight(dev, bclp * max / 255);
 164        asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
 165
 166        return 0;
 167}
 168
 169static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi)
 170{
 171        /* alsi is the current ALS reading in lux. 0 indicates below sensor
 172           range, 0xffff indicates above sensor range. 1-0xfffe are valid */
 173        return 0;
 174}
 175
 176static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb)
 177{
 178        struct drm_i915_private *dev_priv = dev->dev_private;
 179        if (pfmb & ASLE_PFMB_PWM_VALID) {
 180                u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
 181                u32 pwm = pfmb & ASLE_PFMB_PWM_MASK;
 182                blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK;
 183                pwm = pwm >> 9;
 184                /* FIXME - what do we do with the PWM? */
 185        }
 186        return 0;
 187}
 188
 189static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)
 190{
 191        /* Panel fitting is currently controlled by the X code, so this is a
 192           noop until modesetting support works fully */
 193        if (!(pfit & ASLE_PFIT_VALID))
 194                return ASLE_PFIT_FAILED;
 195        return 0;
 196}
 197
 198void intel_opregion_asle_intr(struct drm_device *dev)
 199{
 200        struct drm_i915_private *dev_priv = dev->dev_private;
 201        struct opregion_asle *asle = dev_priv->opregion.asle;
 202        u32 asle_stat = 0;
 203        u32 asle_req;
 204
 205        if (!asle)
 206                return;
 207
 208        asle_req = asle->aslc & ASLE_REQ_MSK;
 209
 210        if (!asle_req) {
 211                DRM_DEBUG_DRIVER("non asle set request??\n");
 212                return;
 213        }
 214
 215        if (asle_req & ASLE_SET_ALS_ILLUM)
 216                asle_stat |= asle_set_als_illum(dev, asle->alsi);
 217
 218        if (asle_req & ASLE_SET_BACKLIGHT)
 219                asle_stat |= asle_set_backlight(dev, asle->bclp);
 220
 221        if (asle_req & ASLE_SET_PFIT)
 222                asle_stat |= asle_set_pfit(dev, asle->pfit);
 223
 224        if (asle_req & ASLE_SET_PWM_FREQ)
 225                asle_stat |= asle_set_pwm_freq(dev, asle->pfmb);
 226
 227        asle->aslc = asle_stat;
 228}
 229
 230void intel_opregion_gse_intr(struct drm_device *dev)
 231{
 232        struct drm_i915_private *dev_priv = dev->dev_private;
 233        struct opregion_asle *asle = dev_priv->opregion.asle;
 234        u32 asle_stat = 0;
 235        u32 asle_req;
 236
 237        if (!asle)
 238                return;
 239
 240        asle_req = asle->aslc & ASLE_REQ_MSK;
 241
 242        if (!asle_req) {
 243                DRM_DEBUG_DRIVER("non asle set request??\n");
 244                return;
 245        }
 246
 247        if (asle_req & ASLE_SET_ALS_ILLUM) {
 248                DRM_DEBUG_DRIVER("Illum is not supported\n");
 249                asle_stat |= ASLE_ALS_ILLUM_FAILED;
 250        }
 251
 252        if (asle_req & ASLE_SET_BACKLIGHT)
 253                asle_stat |= asle_set_backlight(dev, asle->bclp);
 254
 255        if (asle_req & ASLE_SET_PFIT) {
 256                DRM_DEBUG_DRIVER("Pfit is not supported\n");
 257                asle_stat |= ASLE_PFIT_FAILED;
 258        }
 259
 260        if (asle_req & ASLE_SET_PWM_FREQ) {
 261                DRM_DEBUG_DRIVER("PWM freq is not supported\n");
 262                asle_stat |= ASLE_PWM_FREQ_FAILED;
 263        }
 264
 265        asle->aslc = asle_stat;
 266}
 267#define ASLE_ALS_EN    (1<<0)
 268#define ASLE_BLC_EN    (1<<1)
 269#define ASLE_PFIT_EN   (1<<2)
 270#define ASLE_PFMB_EN   (1<<3)
 271
 272void intel_opregion_enable_asle(struct drm_device *dev)
 273{
 274        struct drm_i915_private *dev_priv = dev->dev_private;
 275        struct opregion_asle *asle = dev_priv->opregion.asle;
 276
 277        if (asle) {
 278                if (IS_MOBILE(dev))
 279                        intel_enable_asle(dev);
 280
 281                asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |
 282                        ASLE_PFMB_EN;
 283                asle->ardy = 1;
 284        }
 285}
 286
 287#define ACPI_EV_DISPLAY_SWITCH (1<<0)
 288#define ACPI_EV_LID            (1<<1)
 289#define ACPI_EV_DOCK           (1<<2)
 290
 291static struct intel_opregion *system_opregion;
 292
 293static int intel_opregion_video_event(struct notifier_block *nb,
 294                                      unsigned long val, void *data)
 295{
 296        /* The only video events relevant to opregion are 0x80. These indicate
 297           either a docking event, lid switch or display switch request. In
 298           Linux, these are handled by the dock, button and video drivers.
 299        */
 300
 301        struct opregion_acpi *acpi;
 302        struct acpi_bus_event *event = data;
 303        int ret = NOTIFY_OK;
 304
 305        if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0)
 306                return NOTIFY_DONE;
 307
 308        if (!system_opregion)
 309                return NOTIFY_DONE;
 310
 311        acpi = system_opregion->acpi;
 312
 313        if (event->type == 0x80 && !(acpi->cevt & 0x1))
 314                ret = NOTIFY_BAD;
 315
 316        acpi->csts = 0;
 317
 318        return ret;
 319}
 320
 321static struct notifier_block intel_opregion_notifier = {
 322        .notifier_call = intel_opregion_video_event,
 323};
 324
 325/*
 326 * Initialise the DIDL field in opregion. This passes a list of devices to
 327 * the firmware. Values are defined by section B.4.2 of the ACPI specification
 328 * (version 3)
 329 */
 330
 331static void intel_didl_outputs(struct drm_device *dev)
 332{
 333        struct drm_i915_private *dev_priv = dev->dev_private;
 334        struct intel_opregion *opregion = &dev_priv->opregion;
 335        struct drm_connector *connector;
 336        acpi_handle handle;
 337        struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL;
 338        unsigned long long device_id;
 339        acpi_status status;
 340        int i = 0;
 341
 342        handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
 343        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev)))
 344                return;
 345
 346        if (acpi_is_video_device(acpi_dev))
 347                acpi_video_bus = acpi_dev;
 348        else {
 349                list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {
 350                        if (acpi_is_video_device(acpi_cdev)) {
 351                                acpi_video_bus = acpi_cdev;
 352                                break;
 353                        }
 354                }
 355        }
 356
 357        if (!acpi_video_bus) {
 358                printk(KERN_WARNING "No ACPI video bus found\n");
 359                return;
 360        }
 361
 362        list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
 363                if (i >= 8) {
 364                        dev_printk(KERN_ERR, &dev->pdev->dev,
 365                                    "More than 8 outputs detected\n");
 366                        return;
 367                }
 368                status =
 369                        acpi_evaluate_integer(acpi_cdev->handle, "_ADR",
 370                                                NULL, &device_id);
 371                if (ACPI_SUCCESS(status)) {
 372                        if (!device_id)
 373                                goto blind_set;
 374                        opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f);
 375                        i++;
 376                }
 377        }
 378
 379end:
 380        /* If fewer than 8 outputs, the list must be null terminated */
 381        if (i < 8)
 382                opregion->acpi->didl[i] = 0;
 383        return;
 384
 385blind_set:
 386        i = 0;
 387        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 388                int output_type = ACPI_OTHER_OUTPUT;
 389                if (i >= 8) {
 390                        dev_printk(KERN_ERR, &dev->pdev->dev,
 391                                    "More than 8 outputs detected\n");
 392                        return;
 393                }
 394                switch (connector->connector_type) {
 395                case DRM_MODE_CONNECTOR_VGA:
 396                case DRM_MODE_CONNECTOR_DVIA:
 397                        output_type = ACPI_VGA_OUTPUT;
 398                        break;
 399                case DRM_MODE_CONNECTOR_Composite:
 400                case DRM_MODE_CONNECTOR_SVIDEO:
 401                case DRM_MODE_CONNECTOR_Component:
 402                case DRM_MODE_CONNECTOR_9PinDIN:
 403                        output_type = ACPI_TV_OUTPUT;
 404                        break;
 405                case DRM_MODE_CONNECTOR_DVII:
 406                case DRM_MODE_CONNECTOR_DVID:
 407                case DRM_MODE_CONNECTOR_DisplayPort:
 408                case DRM_MODE_CONNECTOR_HDMIA:
 409                case DRM_MODE_CONNECTOR_HDMIB:
 410                        output_type = ACPI_DIGITAL_OUTPUT;
 411                        break;
 412                case DRM_MODE_CONNECTOR_LVDS:
 413                        output_type = ACPI_LVDS_OUTPUT;
 414                        break;
 415                }
 416                opregion->acpi->didl[i] |= (1<<31) | output_type | i;
 417                i++;
 418        }
 419        goto end;
 420}
 421
 422void intel_opregion_init(struct drm_device *dev)
 423{
 424        struct drm_i915_private *dev_priv = dev->dev_private;
 425        struct intel_opregion *opregion = &dev_priv->opregion;
 426
 427        if (!opregion->header)
 428                return;
 429
 430        if (opregion->acpi) {
 431                if (drm_core_check_feature(dev, DRIVER_MODESET))
 432                        intel_didl_outputs(dev);
 433
 434                /* Notify BIOS we are ready to handle ACPI video ext notifs.
 435                 * Right now, all the events are handled by the ACPI video module.
 436                 * We don't actually need to do anything with them. */
 437                opregion->acpi->csts = 0;
 438                opregion->acpi->drdy = 1;
 439
 440                system_opregion = opregion;
 441                register_acpi_notifier(&intel_opregion_notifier);
 442        }
 443
 444        if (opregion->asle)
 445                intel_opregion_enable_asle(dev);
 446}
 447
 448void intel_opregion_fini(struct drm_device *dev)
 449{
 450        struct drm_i915_private *dev_priv = dev->dev_private;
 451        struct intel_opregion *opregion = &dev_priv->opregion;
 452
 453        if (!opregion->header)
 454                return;
 455
 456        if (opregion->acpi) {
 457                opregion->acpi->drdy = 0;
 458
 459                system_opregion = NULL;
 460                unregister_acpi_notifier(&intel_opregion_notifier);
 461        }
 462
 463        /* just clear all opregion memory pointers now */
 464        iounmap(opregion->header);
 465        opregion->header = NULL;
 466        opregion->acpi = NULL;
 467        opregion->swsci = NULL;
 468        opregion->asle = NULL;
 469        opregion->vbt = NULL;
 470}
 471#endif
 472
 473int intel_opregion_setup(struct drm_device *dev)
 474{
 475        struct drm_i915_private *dev_priv = dev->dev_private;
 476        struct intel_opregion *opregion = &dev_priv->opregion;
 477        void *base;
 478        u32 asls, mboxes;
 479        int err = 0;
 480
 481        pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
 482        DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);
 483        if (asls == 0) {
 484                DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");
 485                return -ENOTSUPP;
 486        }
 487
 488        base = acpi_os_ioremap(asls, OPREGION_SIZE);
 489        if (!base)
 490                return -ENOMEM;
 491
 492        if (memcmp(base, OPREGION_SIGNATURE, 16)) {
 493                DRM_DEBUG_DRIVER("opregion signature mismatch\n");
 494                err = -EINVAL;
 495                goto err_out;
 496        }
 497        opregion->header = base;
 498        opregion->vbt = base + OPREGION_VBT_OFFSET;
 499
 500        opregion->lid_state = base + ACPI_CLID;
 501
 502        mboxes = opregion->header->mboxes;
 503        if (mboxes & MBOX_ACPI) {
 504                DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
 505                opregion->acpi = base + OPREGION_ACPI_OFFSET;
 506        }
 507
 508        if (mboxes & MBOX_SWSCI) {
 509                DRM_DEBUG_DRIVER("SWSCI supported\n");
 510                opregion->swsci = base + OPREGION_SWSCI_OFFSET;
 511        }
 512        if (mboxes & MBOX_ASLE) {
 513                DRM_DEBUG_DRIVER("ASLE supported\n");
 514                opregion->asle = base + OPREGION_ASLE_OFFSET;
 515        }
 516
 517        return 0;
 518
 519err_out:
 520        iounmap(base);
 521        return err;
 522}
 523