Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2011 Intel Corporation
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice (including the next
0012  * paragraph) shall be included in all copies or substantial portions of the
0013  * Software.
0014  *
0015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0018  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0020  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
0021  * DEALINGS IN THE SOFTWARE.
0022  *
0023  */
0024 #include <linux/acpi.h>
0025 #include "psb_drv.h"
0026 #include "psb_irq.h"
0027 #include "psb_intel_reg.h"
0028 
0029 #define PCI_ASLE 0xe4
0030 #define PCI_ASLS 0xfc
0031 
0032 #define OPREGION_HEADER_OFFSET 0
0033 #define OPREGION_ACPI_OFFSET   0x100
0034 #define   ACPI_CLID 0x01ac /* current lid state indicator */
0035 #define   ACPI_CDCK 0x01b0 /* current docking state indicator */
0036 #define OPREGION_SWSCI_OFFSET  0x200
0037 #define OPREGION_ASLE_OFFSET   0x300
0038 #define OPREGION_VBT_OFFSET    0x400
0039 
0040 #define OPREGION_SIGNATURE "IntelGraphicsMem"
0041 #define MBOX_ACPI      (1<<0)
0042 #define MBOX_SWSCI     (1<<1)
0043 #define MBOX_ASLE      (1<<2)
0044 
0045 struct opregion_header {
0046     u8 signature[16];
0047     u32 size;
0048     u32 opregion_ver;
0049     u8 bios_ver[32];
0050     u8 vbios_ver[16];
0051     u8 driver_ver[16];
0052     u32 mboxes;
0053     u8 reserved[164];
0054 } __packed;
0055 
0056 /* OpRegion mailbox #1: public ACPI methods */
0057 struct opregion_acpi {
0058     u32 drdy;   /* driver readiness */
0059     u32 csts;   /* notification status */
0060     u32 cevt;   /* current event */
0061     u8 rsvd1[20];
0062     u32 didl[8];    /* supported display devices ID list */
0063     u32 cpdl[8];    /* currently presented display list */
0064     u32 cadl[8];    /* currently active display list */
0065     u32 nadl[8];    /* next active devices list */
0066     u32 aslp;   /* ASL sleep time-out */
0067     u32 tidx;   /* toggle table index */
0068     u32 chpd;   /* current hotplug enable indicator */
0069     u32 clid;   /* current lid state*/
0070     u32 cdck;   /* current docking state */
0071     u32 sxsw;   /* Sx state resume */
0072     u32 evts;   /* ASL supported events */
0073     u32 cnot;   /* current OS notification */
0074     u32 nrdy;   /* driver status */
0075     u8 rsvd2[60];
0076 } __packed;
0077 
0078 /* OpRegion mailbox #2: SWSCI */
0079 struct opregion_swsci {
0080     /*FIXME: add it later*/
0081 } __packed;
0082 
0083 /* OpRegion mailbox #3: ASLE */
0084 struct opregion_asle {
0085     u32 ardy;   /* driver readiness */
0086     u32 aslc;   /* ASLE interrupt command */
0087     u32 tche;   /* technology enabled indicator */
0088     u32 alsi;   /* current ALS illuminance reading */
0089     u32 bclp;   /* backlight brightness to set */
0090     u32 pfit;   /* panel fitting state */
0091     u32 cblv;   /* current brightness level */
0092     u16 bclm[20];   /* backlight level duty cycle mapping table */
0093     u32 cpfm;   /* current panel fitting mode */
0094     u32 epfm;   /* enabled panel fitting modes */
0095     u8 plut[74];    /* panel LUT and identifier */
0096     u32 pfmb;   /* PWM freq and min brightness */
0097     u8 rsvd[102];
0098 } __packed;
0099 
0100 /* ASLE irq request bits */
0101 #define ASLE_SET_ALS_ILLUM     (1 << 0)
0102 #define ASLE_SET_BACKLIGHT     (1 << 1)
0103 #define ASLE_SET_PFIT          (1 << 2)
0104 #define ASLE_SET_PWM_FREQ      (1 << 3)
0105 #define ASLE_REQ_MSK           0xf
0106 
0107 /* response bits of ASLE irq request */
0108 #define ASLE_ALS_ILLUM_FAILED   (1<<10)
0109 #define ASLE_BACKLIGHT_FAILED   (1<<12)
0110 #define ASLE_PFIT_FAILED        (1<<14)
0111 #define ASLE_PWM_FREQ_FAILED    (1<<16)
0112 
0113 /* ASLE backlight brightness to set */
0114 #define ASLE_BCLP_VALID                (1<<31)
0115 #define ASLE_BCLP_MSK          (~(1<<31))
0116 
0117 /* ASLE panel fitting request */
0118 #define ASLE_PFIT_VALID         (1<<31)
0119 #define ASLE_PFIT_CENTER (1<<0)
0120 #define ASLE_PFIT_STRETCH_TEXT (1<<1)
0121 #define ASLE_PFIT_STRETCH_GFX (1<<2)
0122 
0123 /* response bits of ASLE irq request */
0124 #define ASLE_ALS_ILLUM_FAILED   (1<<10)
0125 #define ASLE_BACKLIGHT_FAILED   (1<<12)
0126 #define ASLE_PFIT_FAILED    (1<<14)
0127 #define ASLE_PWM_FREQ_FAILED    (1<<16)
0128 
0129 /* ASLE backlight brightness to set */
0130 #define ASLE_BCLP_VALID                (1<<31)
0131 #define ASLE_BCLP_MSK          (~(1<<31))
0132 
0133 /* ASLE panel fitting request */
0134 #define ASLE_PFIT_VALID         (1<<31)
0135 #define ASLE_PFIT_CENTER (1<<0)
0136 #define ASLE_PFIT_STRETCH_TEXT (1<<1)
0137 #define ASLE_PFIT_STRETCH_GFX (1<<2)
0138 
0139 /* PWM frequency and minimum brightness */
0140 #define ASLE_PFMB_BRIGHTNESS_MASK (0xff)
0141 #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)
0142 #define ASLE_PFMB_PWM_MASK (0x7ffffe00)
0143 #define ASLE_PFMB_PWM_VALID (1<<31)
0144 
0145 #define ASLE_CBLV_VALID         (1<<31)
0146 
0147 static struct psb_intel_opregion *system_opregion;
0148 
0149 static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
0150 {
0151     struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
0152     struct opregion_asle *asle = dev_priv->opregion.asle;
0153     struct backlight_device *bd = dev_priv->backlight_device;
0154 
0155     DRM_DEBUG_DRIVER("asle set backlight %x\n", bclp);
0156 
0157     if (!(bclp & ASLE_BCLP_VALID))
0158         return ASLE_BACKLIGHT_FAILED;
0159 
0160     if (bd == NULL)
0161         return ASLE_BACKLIGHT_FAILED;
0162 
0163     bclp &= ASLE_BCLP_MSK;
0164     if (bclp > 255)
0165         return ASLE_BACKLIGHT_FAILED;
0166 
0167     gma_backlight_set(dev, bclp * bd->props.max_brightness / 255);
0168 
0169     asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID;
0170 
0171     return 0;
0172 }
0173 
0174 static void psb_intel_opregion_asle_work(struct work_struct *work)
0175 {
0176     struct psb_intel_opregion *opregion =
0177         container_of(work, struct psb_intel_opregion, asle_work);
0178     struct drm_psb_private *dev_priv =
0179         container_of(opregion, struct drm_psb_private, opregion);
0180     struct opregion_asle *asle = opregion->asle;
0181     u32 asle_stat = 0;
0182     u32 asle_req;
0183 
0184     if (!asle)
0185         return;
0186 
0187     asle_req = asle->aslc & ASLE_REQ_MSK;
0188     if (!asle_req) {
0189         DRM_DEBUG_DRIVER("non asle set request??\n");
0190         return;
0191     }
0192 
0193     if (asle_req & ASLE_SET_BACKLIGHT)
0194         asle_stat |= asle_set_backlight(&dev_priv->dev, asle->bclp);
0195 
0196     asle->aslc = asle_stat;
0197 
0198 }
0199 
0200 void psb_intel_opregion_asle_intr(struct drm_device *dev)
0201 {
0202     struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
0203 
0204     if (dev_priv->opregion.asle)
0205         schedule_work(&dev_priv->opregion.asle_work);
0206 }
0207 
0208 #define ASLE_ALS_EN    (1<<0)
0209 #define ASLE_BLC_EN    (1<<1)
0210 #define ASLE_PFIT_EN   (1<<2)
0211 #define ASLE_PFMB_EN   (1<<3)
0212 
0213 void psb_intel_opregion_enable_asle(struct drm_device *dev)
0214 {
0215     struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
0216     struct opregion_asle *asle = dev_priv->opregion.asle;
0217 
0218     if (asle && system_opregion ) {
0219         /* Don't do this on Medfield or other non PC like devices, they
0220            use the bit for something different altogether */
0221         gma_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
0222         gma_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
0223 
0224         asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN
0225                                 | ASLE_PFMB_EN;
0226         asle->ardy = 1;
0227     }
0228 }
0229 
0230 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
0231 #define ACPI_EV_LID            (1<<1)
0232 #define ACPI_EV_DOCK           (1<<2)
0233 
0234 
0235 static int psb_intel_opregion_video_event(struct notifier_block *nb,
0236                       unsigned long val, void *data)
0237 {
0238     /* The only video events relevant to opregion are 0x80. These indicate
0239        either a docking event, lid switch or display switch request. In
0240        Linux, these are handled by the dock, button and video drivers.
0241        We might want to fix the video driver to be opregion-aware in
0242        future, but right now we just indicate to the firmware that the
0243        request has been handled */
0244 
0245     struct opregion_acpi *acpi;
0246 
0247     if (!system_opregion)
0248         return NOTIFY_DONE;
0249 
0250     acpi = system_opregion->acpi;
0251     acpi->csts = 0;
0252 
0253     return NOTIFY_OK;
0254 }
0255 
0256 static struct notifier_block psb_intel_opregion_notifier = {
0257     .notifier_call = psb_intel_opregion_video_event,
0258 };
0259 
0260 void psb_intel_opregion_init(struct drm_device *dev)
0261 {
0262     struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
0263     struct psb_intel_opregion *opregion = &dev_priv->opregion;
0264 
0265     if (!opregion->header)
0266         return;
0267 
0268     if (opregion->acpi) {
0269         /* Notify BIOS we are ready to handle ACPI video ext notifs.
0270          * Right now, all the events are handled by the ACPI video
0271          * module. We don't actually need to do anything with them. */
0272         opregion->acpi->csts = 0;
0273         opregion->acpi->drdy = 1;
0274 
0275         system_opregion = opregion;
0276         register_acpi_notifier(&psb_intel_opregion_notifier);
0277     }
0278 }
0279 
0280 void psb_intel_opregion_fini(struct drm_device *dev)
0281 {
0282     struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
0283     struct psb_intel_opregion *opregion = &dev_priv->opregion;
0284 
0285     if (!opregion->header)
0286         return;
0287 
0288     if (opregion->acpi) {
0289         opregion->acpi->drdy = 0;
0290 
0291         system_opregion = NULL;
0292         unregister_acpi_notifier(&psb_intel_opregion_notifier);
0293     }
0294 
0295     cancel_work_sync(&opregion->asle_work);
0296 
0297     /* just clear all opregion memory pointers now */
0298     iounmap(opregion->header);
0299     opregion->header = NULL;
0300     opregion->acpi = NULL;
0301     opregion->swsci = NULL;
0302     opregion->asle = NULL;
0303     opregion->vbt = NULL;
0304 }
0305 
0306 int psb_intel_opregion_setup(struct drm_device *dev)
0307 {
0308     struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
0309     struct pci_dev *pdev = to_pci_dev(dev->dev);
0310     struct psb_intel_opregion *opregion = &dev_priv->opregion;
0311     u32 opregion_phy, mboxes;
0312     void __iomem *base;
0313     int err = 0;
0314 
0315     pci_read_config_dword(pdev, PCI_ASLS, &opregion_phy);
0316     if (opregion_phy == 0) {
0317         DRM_DEBUG_DRIVER("ACPI Opregion not supported\n");
0318         return -ENOTSUPP;
0319     }
0320 
0321     INIT_WORK(&opregion->asle_work, psb_intel_opregion_asle_work);
0322 
0323     DRM_DEBUG("OpRegion detected at 0x%8x\n", opregion_phy);
0324     base = acpi_os_ioremap(opregion_phy, 8*1024);
0325     if (!base)
0326         return -ENOMEM;
0327 
0328     if (memcmp(base, OPREGION_SIGNATURE, 16)) {
0329         DRM_DEBUG_DRIVER("opregion signature mismatch\n");
0330         err = -EINVAL;
0331         goto err_out;
0332     }
0333 
0334     opregion->header = base;
0335     opregion->vbt = base + OPREGION_VBT_OFFSET;
0336 
0337     opregion->lid_state = base + ACPI_CLID;
0338 
0339     mboxes = opregion->header->mboxes;
0340     if (mboxes & MBOX_ACPI) {
0341         DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
0342         opregion->acpi = base + OPREGION_ACPI_OFFSET;
0343     }
0344 
0345     if (mboxes & MBOX_ASLE) {
0346         DRM_DEBUG_DRIVER("ASLE supported\n");
0347         opregion->asle = base + OPREGION_ASLE_OFFSET;
0348     }
0349 
0350     return 0;
0351 
0352 err_out:
0353     iounmap(base);
0354     return err;
0355 }
0356