linux/drivers/gpu/drm/gma500/power.c
<<
>>
Prefs
   1/**************************************************************************
   2 * Copyright (c) 2009-2011, Intel Corporation.
   3 * All Rights Reserved.
   4 *
   5 * Permission is hereby granted, free of charge, to any person obtaining a
   6 * copy of this software and associated documentation files (the "Software"),
   7 * to deal in the Software without restriction, including without limitation
   8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   9 * and/or sell copies of the Software, and to permit persons to whom the
  10 * Software is furnished to do so, subject to the following conditions:
  11 *
  12 * The above copyright notice and this permission notice (including the next
  13 * paragraph) shall be included in all copies or substantial portions of the
  14 * Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22 * SOFTWARE.
  23 *
  24 * Authors:
  25 *    Benjamin Defnet <benjamin.r.defnet@intel.com>
  26 *    Rajesh Poornachandran <rajesh.poornachandran@intel.com>
  27 * Massively reworked
  28 *    Alan Cox <alan@linux.intel.com>
  29 */
  30
  31#include "power.h"
  32#include "psb_drv.h"
  33#include "psb_reg.h"
  34#include "psb_intel_reg.h"
  35#include <linux/mutex.h>
  36#include <linux/pm_runtime.h>
  37
  38static struct mutex power_mutex;        /* Serialize power ops */
  39static spinlock_t power_ctrl_lock;      /* Serialize power claim */
  40
  41/**
  42 *      gma_power_init          -       initialise power manager
  43 *      @dev: our device
  44 *
  45 *      Set up for power management tracking of our hardware.
  46 */
  47void gma_power_init(struct drm_device *dev)
  48{
  49        struct drm_psb_private *dev_priv = dev->dev_private;
  50
  51        /* FIXME: Move APM/OSPM base into relevant device code */
  52        dev_priv->apm_base = dev_priv->apm_reg & 0xffff;
  53        dev_priv->ospm_base &= 0xffff;
  54
  55        dev_priv->display_power = true; /* We start active */
  56        dev_priv->display_count = 0;    /* Currently no users */
  57        dev_priv->suspended = false;    /* And not suspended */
  58        spin_lock_init(&power_ctrl_lock);
  59        mutex_init(&power_mutex);
  60
  61        if (dev_priv->ops->init_pm)
  62                dev_priv->ops->init_pm(dev);
  63}
  64
  65/**
  66 *      gma_power_uninit        -       end power manager
  67 *      @dev: device to end for
  68 *
  69 *      Undo the effects of gma_power_init
  70 */
  71void gma_power_uninit(struct drm_device *dev)
  72{
  73        pm_runtime_disable(&dev->pdev->dev);
  74        pm_runtime_set_suspended(&dev->pdev->dev);
  75}
  76
  77/**
  78 *      gma_suspend_display     -       suspend the display logic
  79 *      @dev: our DRM device
  80 *
  81 *      Suspend the display logic of the graphics interface
  82 */
  83static void gma_suspend_display(struct drm_device *dev)
  84{
  85        struct drm_psb_private *dev_priv = dev->dev_private;
  86
  87        if (dev_priv->suspended)
  88                return;
  89        dev_priv->ops->save_regs(dev);
  90        dev_priv->ops->power_down(dev);
  91        dev_priv->display_power = false;
  92}
  93
  94/**
  95 *      gma_resume_display      -       resume display side logic
  96 *
  97 *      Resume the display hardware restoring state and enabling
  98 *      as necessary.
  99 */
 100static void gma_resume_display(struct pci_dev *pdev)
 101{
 102        struct drm_device *dev = pci_get_drvdata(pdev);
 103        struct drm_psb_private *dev_priv = dev->dev_private;
 104
 105        /* turn on the display power island */
 106        dev_priv->ops->power_up(dev);
 107        dev_priv->suspended = false;
 108        dev_priv->display_power = true;
 109
 110        PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL);
 111        pci_write_config_word(pdev, PSB_GMCH_CTRL,
 112                        dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED);
 113        dev_priv->ops->restore_regs(dev);
 114}
 115
 116/**
 117 *      gma_suspend_pci         -       suspend PCI side
 118 *      @pdev: PCI device
 119 *
 120 *      Perform the suspend processing on our PCI device state
 121 */
 122static void gma_suspend_pci(struct pci_dev *pdev)
 123{
 124        struct drm_device *dev = pci_get_drvdata(pdev);
 125        struct drm_psb_private *dev_priv = dev->dev_private;
 126        int bsm, vbt;
 127
 128        if (dev_priv->suspended)
 129                return;
 130
 131        pci_save_state(pdev);
 132        pci_read_config_dword(pdev, 0x5C, &bsm);
 133        dev_priv->regs.saveBSM = bsm;
 134        pci_read_config_dword(pdev, 0xFC, &vbt);
 135        dev_priv->regs.saveVBT = vbt;
 136        pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr);
 137        pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data);
 138
 139        pci_disable_device(pdev);
 140        pci_set_power_state(pdev, PCI_D3hot);
 141
 142        dev_priv->suspended = true;
 143}
 144
 145/**
 146 *      gma_resume_pci          -       resume helper
 147 *      @dev: our PCI device
 148 *
 149 *      Perform the resume processing on our PCI device state - rewrite
 150 *      register state and re-enable the PCI device
 151 */
 152static bool gma_resume_pci(struct pci_dev *pdev)
 153{
 154        struct drm_device *dev = pci_get_drvdata(pdev);
 155        struct drm_psb_private *dev_priv = dev->dev_private;
 156        int ret;
 157
 158        if (!dev_priv->suspended)
 159                return true;
 160
 161        pci_set_power_state(pdev, PCI_D0);
 162        pci_restore_state(pdev);
 163        pci_write_config_dword(pdev, 0x5c, dev_priv->regs.saveBSM);
 164        pci_write_config_dword(pdev, 0xFC, dev_priv->regs.saveVBT);
 165        /* restoring MSI address and data in PCIx space */
 166        pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr);
 167        pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data);
 168        ret = pci_enable_device(pdev);
 169
 170        if (ret != 0)
 171                dev_err(&pdev->dev, "pci_enable failed: %d\n", ret);
 172        else
 173                dev_priv->suspended = false;
 174        return !dev_priv->suspended;
 175}
 176
 177/**
 178 *      gma_power_suspend               -       bus callback for suspend
 179 *      @pdev: our PCI device
 180 *      @state: suspend type
 181 *
 182 *      Called back by the PCI layer during a suspend of the system. We
 183 *      perform the necessary shut down steps and save enough state that
 184 *      we can undo this when resume is called.
 185 */
 186int gma_power_suspend(struct device *_dev)
 187{
 188        struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev);
 189        struct drm_device *dev = pci_get_drvdata(pdev);
 190        struct drm_psb_private *dev_priv = dev->dev_private;
 191
 192        mutex_lock(&power_mutex);
 193        if (!dev_priv->suspended) {
 194                if (dev_priv->display_count) {
 195                        mutex_unlock(&power_mutex);
 196                        dev_err(dev->dev, "GPU hardware busy, cannot suspend\n");
 197                        return -EBUSY;
 198                }
 199                psb_irq_uninstall(dev);
 200                gma_suspend_display(dev);
 201                gma_suspend_pci(pdev);
 202        }
 203        mutex_unlock(&power_mutex);
 204        return 0;
 205}
 206
 207/**
 208 *      gma_power_resume                -       resume power
 209 *      @pdev: PCI device
 210 *
 211 *      Resume the PCI side of the graphics and then the displays
 212 */
 213int gma_power_resume(struct device *_dev)
 214{
 215        struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev);
 216        struct drm_device *dev = pci_get_drvdata(pdev);
 217
 218        mutex_lock(&power_mutex);
 219        gma_resume_pci(pdev);
 220        gma_resume_display(pdev);
 221        psb_irq_preinstall(dev);
 222        psb_irq_postinstall(dev);
 223        mutex_unlock(&power_mutex);
 224        return 0;
 225}
 226
 227/**
 228 *      gma_power_is_on         -       returne true if power is on
 229 *      @dev: our DRM device
 230 *
 231 *      Returns true if the display island power is on at this moment
 232 */
 233bool gma_power_is_on(struct drm_device *dev)
 234{
 235        struct drm_psb_private *dev_priv = dev->dev_private;
 236        return dev_priv->display_power;
 237}
 238
 239/**
 240 *      gma_power_begin         -       begin requiring power
 241 *      @dev: our DRM device
 242 *      @force_on: true to force power on
 243 *
 244 *      Begin an action that requires the display power island is enabled.
 245 *      We refcount the islands.
 246 */
 247bool gma_power_begin(struct drm_device *dev, bool force_on)
 248{
 249        struct drm_psb_private *dev_priv = dev->dev_private;
 250        int ret;
 251        unsigned long flags;
 252
 253        spin_lock_irqsave(&power_ctrl_lock, flags);
 254        /* Power already on ? */
 255        if (dev_priv->display_power) {
 256                dev_priv->display_count++;
 257                pm_runtime_get(&dev->pdev->dev);
 258                spin_unlock_irqrestore(&power_ctrl_lock, flags);
 259                return true;
 260        }
 261        if (force_on == false)
 262                goto out_false;
 263
 264        /* Ok power up needed */
 265        ret = gma_resume_pci(dev->pdev);
 266        if (ret == 0) {
 267                psb_irq_preinstall(dev);
 268                psb_irq_postinstall(dev);
 269                pm_runtime_get(&dev->pdev->dev);
 270                dev_priv->display_count++;
 271                spin_unlock_irqrestore(&power_ctrl_lock, flags);
 272                return true;
 273        }
 274out_false:
 275        spin_unlock_irqrestore(&power_ctrl_lock, flags);
 276        return false;
 277}
 278
 279/**
 280 *      gma_power_end           -       end use of power
 281 *      @dev: Our DRM device
 282 *
 283 *      Indicate that one of our gma_power_begin() requested periods when
 284 *      the diplay island power is needed has completed.
 285 */
 286void gma_power_end(struct drm_device *dev)
 287{
 288        struct drm_psb_private *dev_priv = dev->dev_private;
 289        unsigned long flags;
 290        spin_lock_irqsave(&power_ctrl_lock, flags);
 291        dev_priv->display_count--;
 292        WARN_ON(dev_priv->display_count < 0);
 293        spin_unlock_irqrestore(&power_ctrl_lock, flags);
 294        pm_runtime_put(&dev->pdev->dev);
 295}
 296
 297int psb_runtime_suspend(struct device *dev)
 298{
 299        return gma_power_suspend(dev);
 300}
 301
 302int psb_runtime_resume(struct device *dev)
 303{
 304        return gma_power_resume(dev);
 305}
 306
 307int psb_runtime_idle(struct device *dev)
 308{
 309        struct drm_device *drmdev = pci_get_drvdata(to_pci_dev(dev));
 310        struct drm_psb_private *dev_priv = drmdev->dev_private;
 311        if (dev_priv->display_count)
 312                return 0;
 313        else
 314                return 1;
 315}
 316