Path: blob/master/drivers/gpu/drm/i915/intel_opregion.c
15113 views
/*1* Copyright 2008 Intel Corporation <[email protected]>2* Copyright 2008 Red Hat <[email protected]>3*4* Permission is hereby granted, free of charge, to any person obtaining5* a copy of this software and associated documentation files (the6* "Software"), to deal in the Software without restriction, including7* without limitation the rights to use, copy, modify, merge, publish,8* distribute, sub license, and/or sell copies of the Software, and to9* permit persons to whom the Software is furnished to do so, subject to10* the following conditions:11*12* The above copyright notice and this permission notice (including the13* next paragraph) shall be included in all copies or substantial14* portions of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND19* NON-INFRINGEMENT. IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS BE20* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN21* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN22* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE23* SOFTWARE.24*25*/2627#include <linux/acpi.h>28#include <linux/acpi_io.h>29#include <acpi/video.h>3031#include "drmP.h"32#include "i915_drm.h"33#include "i915_drv.h"34#include "intel_drv.h"3536#define PCI_ASLE 0xe437#define PCI_ASLS 0xfc3839#define OPREGION_HEADER_OFFSET 040#define OPREGION_ACPI_OFFSET 0x10041#define ACPI_CLID 0x01ac /* current lid state indicator */42#define ACPI_CDCK 0x01b0 /* current docking state indicator */43#define OPREGION_SWSCI_OFFSET 0x20044#define OPREGION_ASLE_OFFSET 0x30045#define OPREGION_VBT_OFFSET 0x4004647#define OPREGION_SIGNATURE "IntelGraphicsMem"48#define MBOX_ACPI (1<<0)49#define MBOX_SWSCI (1<<1)50#define MBOX_ASLE (1<<2)5152struct opregion_header {53u8 signature[16];54u32 size;55u32 opregion_ver;56u8 bios_ver[32];57u8 vbios_ver[16];58u8 driver_ver[16];59u32 mboxes;60u8 reserved[164];61} __attribute__((packed));6263/* OpRegion mailbox #1: public ACPI methods */64struct opregion_acpi {65u32 drdy; /* driver readiness */66u32 csts; /* notification status */67u32 cevt; /* current event */68u8 rsvd1[20];69u32 didl[8]; /* supported display devices ID list */70u32 cpdl[8]; /* currently presented display list */71u32 cadl[8]; /* currently active display list */72u32 nadl[8]; /* next active devices list */73u32 aslp; /* ASL sleep time-out */74u32 tidx; /* toggle table index */75u32 chpd; /* current hotplug enable indicator */76u32 clid; /* current lid state*/77u32 cdck; /* current docking state */78u32 sxsw; /* Sx state resume */79u32 evts; /* ASL supported events */80u32 cnot; /* current OS notification */81u32 nrdy; /* driver status */82u8 rsvd2[60];83} __attribute__((packed));8485/* OpRegion mailbox #2: SWSCI */86struct opregion_swsci {87u32 scic; /* SWSCI command|status|data */88u32 parm; /* command parameters */89u32 dslp; /* driver sleep time-out */90u8 rsvd[244];91} __attribute__((packed));9293/* OpRegion mailbox #3: ASLE */94struct opregion_asle {95u32 ardy; /* driver readiness */96u32 aslc; /* ASLE interrupt command */97u32 tche; /* technology enabled indicator */98u32 alsi; /* current ALS illuminance reading */99u32 bclp; /* backlight brightness to set */100u32 pfit; /* panel fitting state */101u32 cblv; /* current brightness level */102u16 bclm[20]; /* backlight level duty cycle mapping table */103u32 cpfm; /* current panel fitting mode */104u32 epfm; /* enabled panel fitting modes */105u8 plut[74]; /* panel LUT and identifier */106u32 pfmb; /* PWM freq and min brightness */107u8 rsvd[102];108} __attribute__((packed));109110/* ASLE irq request bits */111#define ASLE_SET_ALS_ILLUM (1 << 0)112#define ASLE_SET_BACKLIGHT (1 << 1)113#define ASLE_SET_PFIT (1 << 2)114#define ASLE_SET_PWM_FREQ (1 << 3)115#define ASLE_REQ_MSK 0xf116117/* response bits of ASLE irq request */118#define ASLE_ALS_ILLUM_FAILED (1<<10)119#define ASLE_BACKLIGHT_FAILED (1<<12)120#define ASLE_PFIT_FAILED (1<<14)121#define ASLE_PWM_FREQ_FAILED (1<<16)122123/* ASLE backlight brightness to set */124#define ASLE_BCLP_VALID (1<<31)125#define ASLE_BCLP_MSK (~(1<<31))126127/* ASLE panel fitting request */128#define ASLE_PFIT_VALID (1<<31)129#define ASLE_PFIT_CENTER (1<<0)130#define ASLE_PFIT_STRETCH_TEXT (1<<1)131#define ASLE_PFIT_STRETCH_GFX (1<<2)132133/* PWM frequency and minimum brightness */134#define ASLE_PFMB_BRIGHTNESS_MASK (0xff)135#define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)136#define ASLE_PFMB_PWM_MASK (0x7ffffe00)137#define ASLE_PFMB_PWM_VALID (1<<31)138139#define ASLE_CBLV_VALID (1<<31)140141#define ACPI_OTHER_OUTPUT (0<<8)142#define ACPI_VGA_OUTPUT (1<<8)143#define ACPI_TV_OUTPUT (2<<8)144#define ACPI_DIGITAL_OUTPUT (3<<8)145#define ACPI_LVDS_OUTPUT (4<<8)146147#ifdef CONFIG_ACPI148static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)149{150struct drm_i915_private *dev_priv = dev->dev_private;151struct opregion_asle *asle = dev_priv->opregion.asle;152u32 max;153154if (!(bclp & ASLE_BCLP_VALID))155return ASLE_BACKLIGHT_FAILED;156157bclp &= ASLE_BCLP_MSK;158if (bclp > 255)159return ASLE_BACKLIGHT_FAILED;160161max = intel_panel_get_max_backlight(dev);162intel_panel_set_backlight(dev, bclp * max / 255);163asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;164165return 0;166}167168static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi)169{170/* alsi is the current ALS reading in lux. 0 indicates below sensor171range, 0xffff indicates above sensor range. 1-0xfffe are valid */172return 0;173}174175static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb)176{177struct drm_i915_private *dev_priv = dev->dev_private;178if (pfmb & ASLE_PFMB_PWM_VALID) {179u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL);180u32 pwm = pfmb & ASLE_PFMB_PWM_MASK;181blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK;182pwm = pwm >> 9;183/* FIXME - what do we do with the PWM? */184}185return 0;186}187188static u32 asle_set_pfit(struct drm_device *dev, u32 pfit)189{190/* Panel fitting is currently controlled by the X code, so this is a191noop until modesetting support works fully */192if (!(pfit & ASLE_PFIT_VALID))193return ASLE_PFIT_FAILED;194return 0;195}196197void intel_opregion_asle_intr(struct drm_device *dev)198{199struct drm_i915_private *dev_priv = dev->dev_private;200struct opregion_asle *asle = dev_priv->opregion.asle;201u32 asle_stat = 0;202u32 asle_req;203204if (!asle)205return;206207asle_req = asle->aslc & ASLE_REQ_MSK;208209if (!asle_req) {210DRM_DEBUG_DRIVER("non asle set request??\n");211return;212}213214if (asle_req & ASLE_SET_ALS_ILLUM)215asle_stat |= asle_set_als_illum(dev, asle->alsi);216217if (asle_req & ASLE_SET_BACKLIGHT)218asle_stat |= asle_set_backlight(dev, asle->bclp);219220if (asle_req & ASLE_SET_PFIT)221asle_stat |= asle_set_pfit(dev, asle->pfit);222223if (asle_req & ASLE_SET_PWM_FREQ)224asle_stat |= asle_set_pwm_freq(dev, asle->pfmb);225226asle->aslc = asle_stat;227}228229/* Only present on Ironlake+ */230void intel_opregion_gse_intr(struct drm_device *dev)231{232struct drm_i915_private *dev_priv = dev->dev_private;233struct opregion_asle *asle = dev_priv->opregion.asle;234u32 asle_stat = 0;235u32 asle_req;236237if (!asle)238return;239240asle_req = asle->aslc & ASLE_REQ_MSK;241242if (!asle_req) {243DRM_DEBUG_DRIVER("non asle set request??\n");244return;245}246247if (asle_req & ASLE_SET_ALS_ILLUM) {248DRM_DEBUG_DRIVER("Illum is not supported\n");249asle_stat |= ASLE_ALS_ILLUM_FAILED;250}251252if (asle_req & ASLE_SET_BACKLIGHT)253asle_stat |= asle_set_backlight(dev, asle->bclp);254255if (asle_req & ASLE_SET_PFIT) {256DRM_DEBUG_DRIVER("Pfit is not supported\n");257asle_stat |= ASLE_PFIT_FAILED;258}259260if (asle_req & ASLE_SET_PWM_FREQ) {261DRM_DEBUG_DRIVER("PWM freq is not supported\n");262asle_stat |= ASLE_PWM_FREQ_FAILED;263}264265asle->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)271272void intel_opregion_enable_asle(struct drm_device *dev)273{274struct drm_i915_private *dev_priv = dev->dev_private;275struct opregion_asle *asle = dev_priv->opregion.asle;276277if (asle) {278if (IS_MOBILE(dev))279intel_enable_asle(dev);280281asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |282ASLE_PFMB_EN;283asle->ardy = 1;284}285}286287#define ACPI_EV_DISPLAY_SWITCH (1<<0)288#define ACPI_EV_LID (1<<1)289#define ACPI_EV_DOCK (1<<2)290291static struct intel_opregion *system_opregion;292293static int intel_opregion_video_event(struct notifier_block *nb,294unsigned long val, void *data)295{296/* The only video events relevant to opregion are 0x80. These indicate297either a docking event, lid switch or display switch request. In298Linux, these are handled by the dock, button and video drivers.299We might want to fix the video driver to be opregion-aware in300future, but right now we just indicate to the firmware that the301request has been handled */302303struct opregion_acpi *acpi;304305if (!system_opregion)306return NOTIFY_DONE;307308acpi = system_opregion->acpi;309acpi->csts = 0;310311return NOTIFY_OK;312}313314static struct notifier_block intel_opregion_notifier = {315.notifier_call = intel_opregion_video_event,316};317318/*319* Initialise the DIDL field in opregion. This passes a list of devices to320* the firmware. Values are defined by section B.4.2 of the ACPI specification321* (version 3)322*/323324static void intel_didl_outputs(struct drm_device *dev)325{326struct drm_i915_private *dev_priv = dev->dev_private;327struct intel_opregion *opregion = &dev_priv->opregion;328struct drm_connector *connector;329acpi_handle handle;330struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL;331unsigned long long device_id;332acpi_status status;333int i = 0;334335handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);336if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev)))337return;338339if (acpi_is_video_device(acpi_dev))340acpi_video_bus = acpi_dev;341else {342list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {343if (acpi_is_video_device(acpi_cdev)) {344acpi_video_bus = acpi_cdev;345break;346}347}348}349350if (!acpi_video_bus) {351printk(KERN_WARNING "No ACPI video bus found\n");352return;353}354355list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {356if (i >= 8) {357dev_printk (KERN_ERR, &dev->pdev->dev,358"More than 8 outputs detected\n");359return;360}361status =362acpi_evaluate_integer(acpi_cdev->handle, "_ADR",363NULL, &device_id);364if (ACPI_SUCCESS(status)) {365if (!device_id)366goto blind_set;367opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f);368i++;369}370}371372end:373/* If fewer than 8 outputs, the list must be null terminated */374if (i < 8)375opregion->acpi->didl[i] = 0;376return;377378blind_set:379i = 0;380list_for_each_entry(connector, &dev->mode_config.connector_list, head) {381int output_type = ACPI_OTHER_OUTPUT;382if (i >= 8) {383dev_printk (KERN_ERR, &dev->pdev->dev,384"More than 8 outputs detected\n");385return;386}387switch (connector->connector_type) {388case DRM_MODE_CONNECTOR_VGA:389case DRM_MODE_CONNECTOR_DVIA:390output_type = ACPI_VGA_OUTPUT;391break;392case DRM_MODE_CONNECTOR_Composite:393case DRM_MODE_CONNECTOR_SVIDEO:394case DRM_MODE_CONNECTOR_Component:395case DRM_MODE_CONNECTOR_9PinDIN:396output_type = ACPI_TV_OUTPUT;397break;398case DRM_MODE_CONNECTOR_DVII:399case DRM_MODE_CONNECTOR_DVID:400case DRM_MODE_CONNECTOR_DisplayPort:401case DRM_MODE_CONNECTOR_HDMIA:402case DRM_MODE_CONNECTOR_HDMIB:403output_type = ACPI_DIGITAL_OUTPUT;404break;405case DRM_MODE_CONNECTOR_LVDS:406output_type = ACPI_LVDS_OUTPUT;407break;408}409opregion->acpi->didl[i] |= (1<<31) | output_type | i;410i++;411}412goto end;413}414415void intel_opregion_init(struct drm_device *dev)416{417struct drm_i915_private *dev_priv = dev->dev_private;418struct intel_opregion *opregion = &dev_priv->opregion;419420if (!opregion->header)421return;422423if (opregion->acpi) {424if (drm_core_check_feature(dev, DRIVER_MODESET))425intel_didl_outputs(dev);426427/* Notify BIOS we are ready to handle ACPI video ext notifs.428* Right now, all the events are handled by the ACPI video module.429* We don't actually need to do anything with them. */430opregion->acpi->csts = 0;431opregion->acpi->drdy = 1;432433system_opregion = opregion;434register_acpi_notifier(&intel_opregion_notifier);435}436437if (opregion->asle)438intel_opregion_enable_asle(dev);439}440441void intel_opregion_fini(struct drm_device *dev)442{443struct drm_i915_private *dev_priv = dev->dev_private;444struct intel_opregion *opregion = &dev_priv->opregion;445446if (!opregion->header)447return;448449if (opregion->acpi) {450opregion->acpi->drdy = 0;451452system_opregion = NULL;453unregister_acpi_notifier(&intel_opregion_notifier);454}455456/* just clear all opregion memory pointers now */457iounmap(opregion->header);458opregion->header = NULL;459opregion->acpi = NULL;460opregion->swsci = NULL;461opregion->asle = NULL;462opregion->vbt = NULL;463}464#endif465466int intel_opregion_setup(struct drm_device *dev)467{468struct drm_i915_private *dev_priv = dev->dev_private;469struct intel_opregion *opregion = &dev_priv->opregion;470void *base;471u32 asls, mboxes;472int err = 0;473474pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);475DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);476if (asls == 0) {477DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");478return -ENOTSUPP;479}480481base = acpi_os_ioremap(asls, OPREGION_SIZE);482if (!base)483return -ENOMEM;484485if (memcmp(base, OPREGION_SIGNATURE, 16)) {486DRM_DEBUG_DRIVER("opregion signature mismatch\n");487err = -EINVAL;488goto err_out;489}490opregion->header = base;491opregion->vbt = base + OPREGION_VBT_OFFSET;492493opregion->lid_state = base + ACPI_CLID;494495mboxes = opregion->header->mboxes;496if (mboxes & MBOX_ACPI) {497DRM_DEBUG_DRIVER("Public ACPI methods supported\n");498opregion->acpi = base + OPREGION_ACPI_OFFSET;499}500501if (mboxes & MBOX_SWSCI) {502DRM_DEBUG_DRIVER("SWSCI supported\n");503opregion->swsci = base + OPREGION_SWSCI_OFFSET;504}505if (mboxes & MBOX_ASLE) {506DRM_DEBUG_DRIVER("ASLE supported\n");507opregion->asle = base + OPREGION_ASLE_OFFSET;508}509510return 0;511512err_out:513iounmap(base);514return err;515}516517518