android_kernel_modules_leno.../intel_media/common/psb_powermgmt.c

2280 lines
62 KiB
C

/**************************************************************************
* Copyright (c) 2009, Intel Corporation.
* All Rights Reserved.
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Authors:
* Benjamin Defnet <benjamin.r.defnet@intel.com>
* Rajesh Poornachandran <rajesh.poornachandran@intel.com>
* Yun(Mark) Tu <yun.tu@intel.com>
* Zhouzhou(Scott) Fang <zhouzhou.fang@intel.com>
*/
#include "psb_powermgmt.h"
#include "psb_drv.h"
#include "psb_intel_reg.h"
#include "psb_msvdx.h"
#include "pnw_topaz.h"
#include "mdfld_gl3.h"
#include "mdfld_dsi_dbi.h"
#include "mdfld_dsi_dbi_dpu.h"
#include "mdfld_dsi_dpi.h"
#include "android_hdmi.h"
#include "psb_intel_display.h"
#include "psb_irq.h"
#ifdef CONFIG_GFX_RTPM
#include <linux/pm_runtime.h>
#endif
#include <linux/atomic.h>
#include <linux/version.h>
#define SUPPORT_EARLY_SUSPEND 1
#include <asm/intel_scu_pmic.h>
#if SUPPORT_EARLY_SUSPEND
#include <linux/earlysuspend.h>
#endif /* if SUPPORT_EARLY_SUSPEND */
#include <asm/intel-mid.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/early_suspend_sysfs.h>
#include "mdfld_dsi_dbi_dsr.h"
#define SCU_CMD_VPROG2 0xe3
struct drm_device *gpDrmDevice;
EXPORT_SYMBOL(gpDrmDevice);
struct mutex g_ospm_mutex;
/* Lock strategy */
/*
* we use both mutex lock and spin lock, for it
* need synchronization between atomic context and process context
*/
static bool gbSuspendInProgress; /* default set as false */
static bool gbResumeInProgress; /* default set as false */
static bool pcihostSuspendInProgress;
bool gbSuspended; /* Indicate the host PCI suspened or not */
static int g_hw_power_status_mask;
static atomic_t g_display_access_count;
static atomic_t g_graphics_access_count;
atomic_t g_videoenc_access_count;
atomic_t g_videodec_access_count;
extern u32 DISP_PLANEB_STATUS;
void acquire_ospm_lock(void)
{
mutex_lock(&g_ospm_mutex);
}
void release_ospm_lock(void)
{
mutex_unlock(&g_ospm_mutex);
}
static void ospm_early_suspend();
static void ospm_late_resume();
#if SUPPORT_EARLY_SUSPEND
/*
* gfx_early_suspend
*
*/
static void gfx_early_suspend(struct early_suspend *h);
static void gfx_late_resume(struct early_suspend *h);
static struct early_suspend gfx_early_suspend_desc = {
.level = EARLY_SUSPEND_LEVEL_DISABLE_FB,
.suspend = gfx_early_suspend,
.resume = gfx_late_resume,
};
#endif /* if SUPPORT_EARLY_SUSPEND */
static int ospm_runtime_pm_msvdx_suspend(struct drm_device *dev)
{
int ret = 0;
struct drm_psb_private *dev_priv = dev->dev_private;
struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
struct psb_video_ctx *pos, *n;
int decode_ctx = 0, decode_running = 0;
unsigned long irq_flags;
PSB_DEBUG_PM("MSVDX: %s: enter in runtime pm.\n", __func__);
if (!ospm_power_is_hw_on(OSPM_VIDEO_DEC_ISLAND))
goto out;
if (psb_get_power_state(OSPM_VIDEO_DEC_ISLAND) == 0) {
PSB_DEBUG_PM("MSVDX: island already in power off state.\n");
goto out;
}
if (atomic_read(&g_videodec_access_count)) {
ret = -1;
goto out;
}
if (psb_check_msvdx_idle(dev)) {
ret = -2;
goto out;
}
spin_lock_irqsave(&dev_priv->video_ctx_lock, irq_flags);
list_for_each_entry_safe(pos, n, &dev_priv->video_ctx, head) {
int entrypoint = pos->ctx_type & 0xff;
if (entrypoint == VAEntrypointVLD ||
entrypoint == VAEntrypointIZZ ||
entrypoint == VAEntrypointIDCT ||
entrypoint == VAEntrypointMoComp ||
entrypoint == VAEntrypointDeblocking) {
decode_ctx = 1;
break;
}
}
spin_unlock_irqrestore(&dev_priv->video_ctx_lock, irq_flags);
/* have decode context, but not started, or is just closed */
if (decode_ctx && msvdx_priv->msvdx_ctx)
decode_running = 1;
#ifdef CONFIG_MDFD_VIDEO_DECODE
psb_irq_uninstall_islands(gpDrmDevice, OSPM_VIDEO_DEC_ISLAND);
if (decode_running)
psb_msvdx_save_context(gpDrmDevice);
MSVDX_NEW_PMSTATE(dev, msvdx_priv, PSB_PMSTATE_POWERDOWN);
#endif
ospm_power_island_down(OSPM_VIDEO_DEC_ISLAND);
#ifdef CONFIG_MDFD_GL3
/* Power off GL3 */
if (IS_MDFLD(dev))
ospm_power_island_down(OSPM_GL3_CACHE_ISLAND);
#endif
out:
return ret;
}
static int ospm_runtime_pm_topaz_suspend(struct drm_device *dev)
{
int ret = 0;
struct drm_psb_private *dev_priv = dev->dev_private;
struct pnw_topaz_private *pnw_topaz_priv = dev_priv->topaz_private;
struct psb_video_ctx *pos, *n;
int encode_ctx = 0, encode_running = 0;
unsigned long irq_flags;
if (!ospm_power_is_hw_on(OSPM_VIDEO_ENC_ISLAND))
goto out;
if (atomic_read(&g_videoenc_access_count)) {
ret = -1;
goto out;
}
if (IS_MDFLD(dev)) {
if (pnw_check_topaz_idle(dev)) {
ret = -2;
goto out;
}
}
spin_lock_irqsave(&dev_priv->video_ctx_lock, irq_flags);
list_for_each_entry_safe(pos, n, &dev_priv->video_ctx, head) {
int entrypoint = pos->ctx_type & 0xff;
if (entrypoint == VAEntrypointEncSlice ||
entrypoint == VAEntrypointEncPicture) {
encode_ctx = 1;
break;
}
}
spin_unlock_irqrestore(&dev_priv->video_ctx_lock, irq_flags);
/* have encode context, but not started, or is just closed */
if (encode_ctx && dev_priv->topaz_ctx)
encode_running = 1;
#ifdef CONFIG_MDFD_VIDEO_DECODE
psb_irq_uninstall_islands(gpDrmDevice, OSPM_VIDEO_ENC_ISLAND);
if (IS_MDFLD(dev)) {
if (encode_running)
pnw_topaz_save_mtx_state(gpDrmDevice);
PNW_TOPAZ_NEW_PMSTATE(dev, pnw_topaz_priv,
PSB_PMSTATE_POWERDOWN);
}
#endif
ospm_power_island_down(OSPM_VIDEO_ENC_ISLAND);
#ifdef CONFIG_MDFD_GL3
/* Power off GL3 */
if (IS_MDFLD(dev))
ospm_power_island_down(OSPM_GL3_CACHE_ISLAND);
#endif
out:
return ret;
}
#ifdef CONFIG_GFX_RTPM
void psb_ospm_post_power_down()
{
int ret;
if (likely(!gpDrmDevice->pdev->dev.power.runtime_auto))
return;
if (ospm_power_is_hw_on(OSPM_VIDEO_ENC_ISLAND |
OSPM_VIDEO_DEC_ISLAND |
OSPM_GRAPHICS_ISLAND))
return;
PSB_DEBUG_PM("request runtime idle\n");
ret = pm_request_idle(&gpDrmDevice->pdev->dev);
if (ret) {
PSB_DEBUG_PM("pm_request_idle fail, ret %d\n", ret);
ret = pm_runtime_barrier(&gpDrmDevice->pdev->dev);
if (!ret) {
ret = pm_request_idle(&gpDrmDevice->pdev->dev);
PSB_DEBUG_PM("pm_request_idle again, ret %d\n", ret);
}
}
}
#endif
static int ospm_runtime_pm_msvdx_resume(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
/*printk(KERN_ALERT "ospm_runtime_pm_msvdx_resume\n");*/
#ifdef CONFIG_MDFD_VIDEO_DECODE
MSVDX_NEW_PMSTATE(dev, msvdx_priv, PSB_PMSTATE_POWERUP);
psb_msvdx_restore_context(dev);
#endif
return 0;
}
static int ospm_runtime_pm_topaz_resume(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct pnw_topaz_private *pnw_topaz_priv = dev_priv->topaz_private;
struct psb_video_ctx *pos, *n;
int encode_ctx = 0, encode_running = 0;
unsigned long irq_flags;
/*printk(KERN_ALERT "ospm_runtime_pm_topaz_resume\n");*/
spin_lock_irqsave(&dev_priv->video_ctx_lock, irq_flags);
list_for_each_entry_safe(pos, n, &dev_priv->video_ctx, head) {
int entrypoint = pos->ctx_type & 0xff;
if (entrypoint == VAEntrypointEncSlice ||
entrypoint == VAEntrypointEncPicture) {
encode_ctx = 1;
break;
}
}
spin_unlock_irqrestore(&dev_priv->video_ctx_lock, irq_flags);
/* have encode context, but not started, or is just closed */
if (encode_ctx && dev_priv->topaz_ctx)
encode_running = 1;
if (encode_ctx)
PSB_DEBUG_PM("Topaz: has encode context, running=%d\n",
encode_running);
else
PSB_DEBUG_PM("Topaz: no encode running\n");
#ifdef CONFIG_MDFD_VIDEO_DECODE
if (IS_MDFLD(dev)) {
if (encode_running) { /* has encode session running */
psb_irq_uninstall_islands(gpDrmDevice, OSPM_VIDEO_ENC_ISLAND);
pnw_topaz_restore_mtx_state(gpDrmDevice);
}
PNW_TOPAZ_NEW_PMSTATE(dev, pnw_topaz_priv, PSB_PMSTATE_POWERUP);
}
#endif
return 0;
}
void ospm_apm_power_down_msvdx(struct drm_device *dev, int force_off)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
#ifdef CONFIG_SLICE_HEADER_PARSING
unsigned long irq_flags;
int frame_finished = 0;
int seq_flag = 0, shp_ctx_count = 0;
struct psb_video_ctx *pos, *n;
#endif
mutex_lock(&g_ospm_mutex);
if (force_off)
goto power_off;
if (!ospm_power_is_hw_on(OSPM_VIDEO_DEC_ISLAND)) {
PSB_DEBUG_PM("g_hw_power_status_mask: msvdx in power off.\n");
goto out;
}
if (psb_get_power_state(OSPM_VIDEO_DEC_ISLAND) == 0) {
PSB_DEBUG_PM("pmu_nc_get_power_state: msvdx in power off.\n");
goto out;
}
if (atomic_read(&g_videodec_access_count)) {
PSB_DEBUG_PM("g_videodec_access_count has been set.\n");
goto out;
}
#ifdef CONFIG_SLICE_HEADER_PARSING
spin_lock_irqsave(&dev_priv->video_ctx_lock, irq_flags);
list_for_each_entry_safe(pos, n, &dev_priv->video_ctx, head) {
if (pos->slice_extract_flag) {
shp_ctx_count++;
seq_flag = (pos->frame_end_seq == (msvdx_priv->msvdx_current_sequence & (~0xf))) ? 1 : 0;
if(seq_flag && pos->frame_boundary) {
frame_finished = 1;
break;
}
}
}
spin_unlock_irqrestore(&dev_priv->video_ctx_lock, irq_flags);
if (shp_ctx_count != 0 && !frame_finished)
goto out;
#endif
#ifdef CONFIG_MDFD_VIDEO_DECODE
if (psb_check_msvdx_idle(dev))
goto out;
psb_msvdx_save_context(dev);
#endif
power_off:
ospm_power_island_down(OSPM_VIDEO_DEC_ISLAND);
#ifdef CONFIG_MDFD_GL3
/* Power off GL3 */
ospm_power_island_down(OSPM_GL3_CACHE_ISLAND);
#endif
MSVDX_NEW_PMSTATE(dev, msvdx_priv, PSB_PMSTATE_POWERDOWN);
out:
mutex_unlock(&g_ospm_mutex);
return;
}
void ospm_apm_power_down_topaz(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct pnw_topaz_private *pnw_topaz_priv = dev_priv->topaz_private;
mutex_lock(&g_ospm_mutex);
if (!ospm_power_is_hw_on(OSPM_VIDEO_ENC_ISLAND))
goto out;
if (atomic_read(&g_videoenc_access_count))
goto out;
#ifdef CONFIG_MDFD_VIDEO_DECODE
if (IS_MDFLD(dev))
if (pnw_check_topaz_idle(dev))
goto out;
if (IS_MDFLD(dev)) {
psb_irq_uninstall_islands(dev, OSPM_VIDEO_ENC_ISLAND);
pnw_topaz_save_mtx_state(gpDrmDevice);
PNW_TOPAZ_NEW_PMSTATE(dev, pnw_topaz_priv, PSB_PMSTATE_POWERDOWN);
}
ospm_power_island_down(OSPM_VIDEO_ENC_ISLAND);
#endif
#ifdef CONFIG_MDFD_GL3
/* Power off GL3 */
if (IS_MDFLD(dev))
ospm_power_island_down(OSPM_GL3_CACHE_ISLAND);
#endif
out:
mutex_unlock(&g_ospm_mutex);
return;
}
static ssize_t early_suspend_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
if (!strncmp(buf, EARLY_SUSPEND_ON, EARLY_SUSPEND_STATUS_LEN))
ospm_early_suspend();
else if (!strncmp(buf, EARLY_SUSPEND_OFF, EARLY_SUSPEND_STATUS_LEN))
ospm_late_resume();
return count;
}
static DEVICE_EARLY_SUSPEND_ATTR(early_suspend_store);
/*
* ospm_power_init
*
* Description: Initialize this ospm power management module
*/
void ospm_power_init(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = (struct drm_psb_private *)dev->dev_private;
unsigned long flags;
gpDrmDevice = dev;
mutex_init(&g_ospm_mutex);
spin_lock_init(&dev_priv->ospm_lock);
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
g_hw_power_status_mask = OSPM_ALL_ISLANDS;
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
atomic_set(&g_display_access_count, 0);
atomic_set(&g_graphics_access_count, 0);
atomic_set(&g_videoenc_access_count, 0);
atomic_set(&g_videodec_access_count, 0);
device_create_file(&dev->pdev->dev, &dev_attr_early_suspend);
register_early_suspend_device(&gpDrmDevice->pdev->dev);
#if SUPPORT_EARLY_SUSPEND
register_early_suspend(&gfx_early_suspend_desc);
#endif /* if SUPPORT_EARLY_SUSPEND */
#ifdef OSPM_STAT
dev_priv->graphics_state = PSB_PWR_STATE_ON;
dev_priv->gfx_last_mode_change = jiffies;
dev_priv->gfx_on_time = 0;
dev_priv->gfx_off_time = 0;
#endif
}
/*
* ospm_power_uninit
*
* Description: Uninitialize this ospm power management module
*/
void ospm_power_uninit(void)
{
device_remove_file(&gpDrmDevice->pdev->dev, &dev_attr_early_suspend);
unregister_early_suspend_device(&gpDrmDevice->pdev->dev);
#if SUPPORT_EARLY_SUSPEND
unregister_early_suspend(&gfx_early_suspend_desc);
#endif /* if SUPPORT_EARLY_SUSPEND */
mutex_destroy(&g_ospm_mutex);
#ifdef CONFIG_GFX_RTPM
pm_runtime_get_noresume(&gpDrmDevice->pdev->dev);
#endif
}
/*
* mdfld_adjust_display_fifo
*
* Update display fifo setting to avoid hdmi flicker
*/
static void mdfld_adjust_display_fifo(struct drm_device *dev)
{
u32 temp;
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dsi_config *dsi_config = dev_priv->dsi_configs[0];
struct drm_display_mode *mode = dsi_config->fixed_mode;
if (IS_CTP(dev)) {
/* Set proper high priority configuration to avoid overlay
* block memory self-refresh entry */
temp = REG_READ(G_HP_CONTROL);
REG_WRITE(G_HP_CONTROL,
HP_REQUESTORS_STATUS_OVERRIDE_MODE | temp);
if (mode &&
((mode->hdisplay >= 1920 && mode->vdisplay >= 1080) ||
(mode->hdisplay >= 1080 && mode->vdisplay >= 1920))) {
if ((mode->hdisplay == 1920 &&
mode->vdisplay == 1080) ||
(mode->hdisplay == 1080 &&
mode->vdisplay == 1920)) {
/* setting for 1080p panel */
REG_WRITE(DSPARB, 0x0005F8C0);
REG_WRITE(DSPFW1, 0x0F0F1010);
REG_WRITE(DSPFW2, 0x5F2F0F0F);
REG_WRITE(DSPFW4, 0x07071010);
} else {
/* setting for panel bigger than 1080p */
REG_WRITE(DSPARB, 0x0005F8D4);
REG_WRITE(DSPFW1, 0x0F0F1010);
REG_WRITE(DSPFW2, 0x5F2F0F0F);
REG_WRITE(DSPFW4, 0x07071010);
}
} else {
/* setting for panel smaller than 1080p, f.e 720p */
REG_WRITE(DSPARB, 0x0005E480);
REG_WRITE(DSPFW1, 0x0F0F103F);
REG_WRITE(DSPFW4, 0x0707101F);
}
REG_WRITE(MI_ARB, 0x0);
/*
* enable bit 29 for BUNIT.DEBUG0 register
* This gives slightly more priority to urgent ISOCH traffic
* (which applies to Display and Camera) over BE (best effort)
* in memory controller arbiter, to lower the chance that
* display memory request being blocked for long time which
* may cause display controller crash.
*/
temp = intel_mid_msgbus_read32(3, 0x30);
intel_mid_msgbus_write32(3, 0x30, temp | (1 << 29));
}
temp = REG_READ(DSPARB);
PSB_DEBUG_ENTRY("gfx_hdmi_setting: DSPARB = 0x%x", temp);
temp = REG_READ(DSPFW1);
PSB_DEBUG_ENTRY("gfx_hdmi_setting: DSPFW1 = 0x%x", temp);
temp = REG_READ(DSPFW4);
PSB_DEBUG_ENTRY("gfx_hdmi_setting: DSPFW4 = 0x%x", temp);
temp = REG_READ(MI_ARB);
PSB_DEBUG_ENTRY("gfx_hdmi_setting: MI_ARB = 0x%x", temp);
}
/*
* ospm_post_init
*
* Description: Power gate unused GFX & Display islands.
*/
void ospm_post_init(struct drm_device *dev)
{
u32 dc_islands = 0;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *)dev->dev_private;
unsigned long flags;
u32 all_display_islands;
mutex_lock(&g_ospm_mutex);
/* need disable power for msvdx and topaz in init stage */
ospm_power_island_down(OSPM_VIDEO_DEC_ISLAND | OSPM_VIDEO_ENC_ISLAND);
#ifndef CONFIG_MDFD_GL3
ospm_power_island_down(OSPM_GL3_CACHE_ISLAND);
#endif
/*Save & Power gate un-used display islands.*/
mdfld_save_display(dev);
if (!(dev_priv->panel_desc & DISPLAY_A))
dc_islands |= OSPM_DISPLAY_A_ISLAND;
if (!(dev_priv->panel_desc & DISPLAY_B))
dc_islands |= OSPM_DISPLAY_B_ISLAND;
if (!(dev_priv->panel_desc & DISPLAY_C))
dc_islands |= OSPM_DISPLAY_C_ISLAND;
if (!(dev_priv->panel_desc))
dc_islands |= OSPM_MIPI_ISLAND;
DRM_INFO("%s dc_islands: %x to be powered OFF\n", __func__, dc_islands);
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
/*
If pmu_nc_set_power_state fails then accessing HW
reg would result in a crash - IERR/Fabric error.
*/
if (pmu_nc_set_power_state(dc_islands,
OSPM_ISLAND_DOWN, OSPM_REG_TYPE))
BUG();
all_display_islands = (OSPM_DISPLAY_A_ISLAND |
OSPM_DISPLAY_B_ISLAND |
OSPM_DISPLAY_C_ISLAND |
OSPM_MIPI_ISLAND);
if ((dc_islands & all_display_islands) == all_display_islands)
g_hw_power_status_mask &= ~OSPM_DISPLAY_ISLAND;
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
/* if HDMI is disabled in the kernel .config, then we want to
disable these MSIC power rails permanently. */
#ifndef CONFIG_SUPPORT_HDMI
if (IS_MDFLD_OLD(dev)) {
/* turn off HDMI power rails */
intel_scu_ipc_iowrite8(MSIC_VHDMICNT, VHDMI_OFF);
intel_scu_ipc_iowrite8(MSIC_VCC330CNT, VCC330_OFF);
}
if (IS_CTP(dev)) {
//ASUS_BSP Louis +++
#ifdef CONFIG_SUPPORT_OTM8018B_MIPI_480X854_DISPLAY
//do nothing
#else
/* turn off HDMI power rails */
intel_scu_ipc_iowrite8(MSIC_VCC330CNT, VCC330_OFF);
#endif
//ASUS_BSP Louis ---
}
#endif
mdfld_adjust_display_fifo(dev);
mutex_unlock(&g_ospm_mutex);
}
/*
* mdfld_save_display_registers
*
* Description: We are going to suspend so save current display
* register state.
*
*/
static int mdfld_save_display_registers (struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
int i;
/* regester */
u32 dpll_reg = MDFLD_DPLL_B;
u32 fp_reg = MDFLD_DPLL_DIV0;
u32 pipeconf_reg = PIPEBCONF;
u32 htot_reg = HTOTAL_B;
u32 hblank_reg = HBLANK_B;
u32 hsync_reg = HSYNC_B;
u32 vtot_reg = VTOTAL_B;
u32 vblank_reg = VBLANK_B;
u32 vsync_reg = VSYNC_B;
u32 pipesrc_reg = PIPEBSRC;
u32 dspstride_reg = DSPBSTRIDE;
u32 dsplinoff_reg = DSPBLINOFF;
u32 dsptileoff_reg = DSPBTILEOFF;
u32 dspsize_reg = DSPBSIZE;
u32 dsppos_reg = DSPBPOS;
u32 dspsurf_reg = DSPBSURF;
u32 dspcntr_reg = DSPBCNTR;
u32 dspstatus_reg = PIPEBSTAT;
u32 palette_reg = PALETTE_B;
u32 color_coef_reg = PIPEB_COLOR_COEF0;
/* values */
u32 *dpll_val = &dev_priv->saveDPLL_B;
u32 *fp_val = &dev_priv->saveFPB0;
u32 *pipeconf_val = &dev_priv->savePIPEBCONF;
u32 *htot_val = &dev_priv->saveHTOTAL_B;
u32 *hblank_val = &dev_priv->saveHBLANK_B;
u32 *hsync_val = &dev_priv->saveHSYNC_B;
u32 *vtot_val = &dev_priv->saveVTOTAL_B;
u32 *vblank_val = &dev_priv->saveVBLANK_B;
u32 *vsync_val = &dev_priv->saveVSYNC_B;
u32 *pipesrc_val = &dev_priv->savePIPEBSRC;
u32 *dspstride_val = &dev_priv->saveDSPBSTRIDE;
u32 *dsplinoff_val = &dev_priv->saveDSPBLINOFF;
u32 *dsptileoff_val = &dev_priv->saveDSPBTILEOFF;
u32 *dspsize_val = &dev_priv->saveDSPBSIZE;
u32 *dsppos_val = &dev_priv->saveDSPBPOS;
u32 *dspsurf_val = &dev_priv->saveDSPBSURF;
u32 *dspcntr_val = &dev_priv->saveDSPBCNTR;
u32 *dspstatus_val = &dev_priv->saveDSPBSTATUS;
u32 *palette_val = dev_priv->save_palette_b;
u32 *color_coef = dev_priv->save_color_coef_b;
PSB_DEBUG_ENTRY("\n");
if (pipe != 1)
return 0;
/* Pipe & plane A info */
*dpll_val = REG_READ(dpll_reg);
*fp_val = REG_READ(fp_reg);
*pipeconf_val = REG_READ(pipeconf_reg);
*htot_val = REG_READ(htot_reg);
*hblank_val = REG_READ(hblank_reg);
*hsync_val = REG_READ(hsync_reg);
*vtot_val = REG_READ(vtot_reg);
*vblank_val = REG_READ(vblank_reg);
*vsync_val = REG_READ(vsync_reg);
*pipesrc_val = REG_READ(pipesrc_reg);
*dspstride_val = REG_READ(dspstride_reg);
*dsplinoff_val = REG_READ(dsplinoff_reg);
*dsptileoff_val = REG_READ(dsptileoff_reg);
*dspsize_val = REG_READ(dspsize_reg);
*dsppos_val = REG_READ(dsppos_reg);
*dspsurf_val = REG_READ(dspsurf_reg);
*dspcntr_val = REG_READ(dspcntr_reg);
*dspstatus_val = REG_READ(dspstatus_reg);
/*save palette (gamma) */
for (i = 0; i < 256; i++)
palette_val[i] = REG_READ(palette_reg + (i<<2));
/*save color_coef (chrome) */
for (i = 0; i < 6; i++)
color_coef[i] = REG_READ(color_coef_reg + (i<<2));
dev_priv->savePFIT_CONTROL = REG_READ(PFIT_CONTROL);
dev_priv->savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS);
dev_priv->saveHDMIPHYMISCCTL = REG_READ(HDMIPHYMISCCTL);
dev_priv->saveHDMIB_CONTROL = REG_READ(HDMIB_CONTROL);
dev_priv->saveDATALANES_B = REG_READ(HDMIB_LANES02);
return 0;
}
/*
* mdfld_save_cursor_overlay_registers
*
* Description: We are going to suspend so save current cursor and overlay display
* register state.
*/
static int mdfld_save_cursor_overlay_registers(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
/*save cursor regs*/
dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR);
dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE);
dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS);
dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR);
dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE);
dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS);
dev_priv->saveDSPCCURSOR_CTRL = PSB_RVDC32(CURCCNTR);
dev_priv->saveDSPCCURSOR_BASE = PSB_RVDC32(CURCBASE);
dev_priv->saveDSPCCURSOR_POS = PSB_RVDC32(CURCPOS);
/* HW overlay */
dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD);
dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0);
dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1);
dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2);
dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3);
dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4);
dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5);
dev_priv->saveOV_OVADD_C = PSB_RVDC32(OV_OVADD + OV_C_OFFSET);
dev_priv->saveOV_OGAMC0_C = PSB_RVDC32(OV_OGAMC0 + OV_C_OFFSET);
dev_priv->saveOV_OGAMC1_C = PSB_RVDC32(OV_OGAMC1 + OV_C_OFFSET);
dev_priv->saveOV_OGAMC2_C = PSB_RVDC32(OV_OGAMC2 + OV_C_OFFSET);
dev_priv->saveOV_OGAMC3_C = PSB_RVDC32(OV_OGAMC3 + OV_C_OFFSET);
dev_priv->saveOV_OGAMC4_C = PSB_RVDC32(OV_OGAMC4 + OV_C_OFFSET);
dev_priv->saveOV_OGAMC5_C = PSB_RVDC32(OV_OGAMC5 + OV_C_OFFSET);
return 0;
}
/**
* @dev: DRM device
* @pipe: DC pipe
*
* restore registers of display controller. It's just for HDMI, as for MIPI
* pipe, use early suspend to save/restore dc registers.
*/
static int mdfld_restore_display_registers(struct drm_device *dev, int pipe)
{
struct drm_psb_private *dev_priv = dev->dev_private;
int i = 0;
u32 dpll = 0;
/* regester */
u32 dpll_reg = MDFLD_DPLL_B;
u32 fp_reg = MDFLD_DPLL_DIV0;
u32 pipeconf_reg = PIPEBCONF;
u32 htot_reg = HTOTAL_B;
u32 hblank_reg = HBLANK_B;
u32 hsync_reg = HSYNC_B;
u32 vtot_reg = VTOTAL_B;
u32 vblank_reg = VBLANK_B;
u32 vsync_reg = VSYNC_B;
u32 pipesrc_reg = PIPEBSRC;
u32 dspstride_reg = DSPBSTRIDE;
u32 dsplinoff_reg = DSPBLINOFF;
u32 dsptileoff_reg = DSPBTILEOFF;
u32 dspsize_reg = DSPBSIZE;
u32 dsppos_reg = DSPBPOS;
u32 dspsurf_reg = DSPBSURF;
u32 dspcntr_reg = DSPBCNTR;
u32 palette_reg = PALETTE_B;
u32 dspstatus_reg = PIPEBSTAT;
u32 color_coef_reg = PIPEB_COLOR_COEF0;
/* values */
u32 dpll_val = dev_priv->saveDPLL_B & ~DPLL_VCO_ENABLE;
u32 fp_val = dev_priv->saveFPB0;
u32 pipeconf_val = dev_priv->savePIPEBCONF;
u32 htot_val = dev_priv->saveHTOTAL_B;
u32 hblank_val = dev_priv->saveHBLANK_B;
u32 hsync_val = dev_priv->saveHSYNC_B;
u32 vtot_val = dev_priv->saveVTOTAL_B;
u32 vblank_val = dev_priv->saveVBLANK_B;
u32 vsync_val = dev_priv->saveVSYNC_B;
u32 pipesrc_val = dev_priv->savePIPEBSRC;
u32 dspstride_val = dev_priv->saveDSPBSTRIDE;
u32 dsplinoff_val = dev_priv->saveDSPBLINOFF;
u32 dsptileoff_val = dev_priv->saveDSPBTILEOFF;
u32 dspsize_val = dev_priv->saveDSPBSIZE;
u32 dsppos_val = dev_priv->saveDSPBPOS;
u32 dspsurf_val = dev_priv->saveDSPBSURF;
u32 dspcntr_val = dev_priv->saveDSPBCNTR & ~DISPLAY_PLANE_ENABLE;
u32 dspstatus_val = dev_priv->saveDSPBSTATUS;
u32 *palette_val = dev_priv->save_palette_b;
u32 *color_coef = dev_priv->save_color_coef_b;
PSB_DEBUG_ENTRY("\n");
if (pipe != 1)
return 0;
/*make sure VGA plane is off. it initializes to on after reset!*/
REG_WRITE(VGACNTRL, 0x80000000);
dpll = REG_READ(dpll_reg);
if (!(dpll & DPLL_VCO_ENABLE)) {
/**
* When ungating power of DPLL, needs to wait 0.5us
* before enable the VCO
*/
if (dpll & MDFLD_PWR_GATE_EN) {
dpll &= ~MDFLD_PWR_GATE_EN;
REG_WRITE(dpll_reg, dpll);
ndelay(500);
}
REG_WRITE(fp_reg, fp_val);
REG_WRITE(dpll_reg, dpll_val);
ndelay(500);
dpll_val |= DPLL_VCO_ENABLE;
REG_WRITE(dpll_reg, dpll_val);
REG_READ(dpll_reg);
}
/* Restore mode */
REG_WRITE(htot_reg, htot_val);
REG_WRITE(hblank_reg, hblank_val);
REG_WRITE(hsync_reg, hsync_val);
REG_WRITE(vtot_reg, vtot_val);
REG_WRITE(vblank_reg, vblank_val);
REG_WRITE(vsync_reg, vsync_val);
REG_WRITE(pipesrc_reg, pipesrc_val);
REG_WRITE(dspstatus_reg, dspstatus_val);
/*set up the plane*/
REG_WRITE(dspstride_reg, dspstride_val);
REG_WRITE(dsplinoff_reg, dsplinoff_val);
REG_WRITE(dsptileoff_reg, dsptileoff_val);
REG_WRITE(dspsize_reg, dspsize_val);
REG_WRITE(dsppos_reg, dsppos_val);
REG_WRITE(dspsurf_reg, dspsurf_val);
REG_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL);
REG_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS);
REG_WRITE(HDMIPHYMISCCTL, dev_priv->saveHDMIPHYMISCCTL);
REG_WRITE(HDMIB_CONTROL, dev_priv->saveHDMIB_CONTROL);
REG_WRITE(HDMIB_LANES02, dev_priv->saveDATALANES_B);
REG_WRITE(HDMIB_LANES3, dev_priv->saveDATALANES_B);
/*save color_coef (chrome) */
for (i = 0; i < 6; i++)
REG_WRITE(color_coef_reg + (i<<2), color_coef[i]);
/* restore palette (gamma) */
for (i = 0; i < 256; i++)
REG_WRITE(palette_reg + (i<<2), palette_val[i]);
/*enable the plane*/
REG_WRITE(dspcntr_reg, dspcntr_val);
/*enable the pipe*/
REG_WRITE(pipeconf_reg, pipeconf_val);
if (pipeconf_val & PIPEBCONF_ENABLE)
intel_wait_for_pipe_enable_disable(dev, pipe, true);
return 0;
}
/*
* mdfld_restore_cursor_overlay_registers
*
* Description: We are going to resume so restore cursor and overlay register state.
*/
static int mdfld_restore_cursor_overlay_registers(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
/*Enable Cursor A*/
REG_WRITE(CURACNTR, dev_priv->saveDSPACURSOR_CTRL);
REG_WRITE(CURAPOS, dev_priv->saveDSPACURSOR_POS);
REG_WRITE(CURABASE, dev_priv->saveDSPACURSOR_BASE);
REG_WRITE(CURBCNTR, dev_priv->saveDSPBCURSOR_CTRL);
REG_WRITE(CURBPOS, dev_priv->saveDSPBCURSOR_POS);
REG_WRITE(CURBBASE, dev_priv->saveDSPBCURSOR_BASE);
REG_WRITE(CURCCNTR, dev_priv->saveDSPCCURSOR_CTRL);
REG_WRITE(CURCPOS, dev_priv->saveDSPCCURSOR_POS);
REG_WRITE(CURCBASE, dev_priv->saveDSPCCURSOR_BASE);
/* restore HW overlay */
REG_WRITE(OV_OVADD, dev_priv->saveOV_OVADD);
REG_WRITE(OV_OGAMC0, dev_priv->saveOV_OGAMC0);
REG_WRITE(OV_OGAMC1, dev_priv->saveOV_OGAMC1);
REG_WRITE(OV_OGAMC2, dev_priv->saveOV_OGAMC2);
REG_WRITE(OV_OGAMC3, dev_priv->saveOV_OGAMC3);
REG_WRITE(OV_OGAMC4, dev_priv->saveOV_OGAMC4);
REG_WRITE(OV_OGAMC5, dev_priv->saveOV_OGAMC5);
REG_WRITE(OV_OVADD + OV_C_OFFSET, dev_priv->saveOV_OVADD_C);
REG_WRITE(OV_OGAMC0 + OV_C_OFFSET, dev_priv->saveOV_OGAMC0_C);
REG_WRITE(OV_OGAMC1 + OV_C_OFFSET, dev_priv->saveOV_OGAMC1_C);
REG_WRITE(OV_OGAMC2 + OV_C_OFFSET, dev_priv->saveOV_OGAMC2_C);
REG_WRITE(OV_OGAMC3 + OV_C_OFFSET, dev_priv->saveOV_OGAMC3_C);
REG_WRITE(OV_OGAMC4 + OV_C_OFFSET, dev_priv->saveOV_OGAMC4_C);
REG_WRITE(OV_OGAMC5 + OV_C_OFFSET, dev_priv->saveOV_OGAMC5_C);
return 0;
}
/*
* mdfld_save_display
*
* Description: Save display status before DPMS OFF for RuntimePM
*/
void mdfld_save_display(struct drm_device *dev)
{
PSB_DEBUG_ENTRY("\n");
mdfld_save_cursor_overlay_registers(dev);
}
/*
* powermgmt_suspend_display
*
* Description: Suspend the display hardware saving state and disabling
* as necessary.
*/
void ospm_suspend_display(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
PSB_DEBUG_ENTRY("\n");
if (!ospm_power_is_hw_on(OSPM_DISPLAY_ISLAND)) {
DRM_INFO("%s: Exit because island is down\n", __func__);
return;
}
mdfld_save_cursor_overlay_registers(dev);
if (dev_priv->panel_desc & DISPLAY_A) {
mdfld_save_display_registers(dev, 0);
mdfld_disable_crtc(dev, 0);
}
if (dev_priv->panel_desc & DISPLAY_B) {
android_hdmi_suspend_display(dev);
}
if (dev_priv->panel_desc & DISPLAY_C) {
mdfld_save_display_registers(dev, 2);
mdfld_disable_crtc(dev, 2);
}
/*save performance state*/
dev_priv->savePERF_MODE = REG_READ(MRST_PERF_MODE);
dev_priv->saveCLOCKGATING = REG_READ(PSB_GFX_CLOCKGATING);
dev_priv->saveVED_CG_DIS = REG_READ(PSB_MSVDX_CLOCKGATING);
dev_priv->saveVEC_CG_DIS = REG_READ(PSB_TOPAZ_CLOCKGATING);
#ifdef CONFIG_MDFD_GL3
dev_priv->saveGL3_CTL = REG_READ(MDFLD_GL3_CONTROL);
dev_priv->saveGL3_USE_WRT_INVAL = REG_READ(MDFLD_GL3_USE_WRT_INVAL);
#endif
ospm_power_island_down(OSPM_DISPLAY_ISLAND);
}
/*
* ospm_resume_display
*
* Description: Resume the display hardware restoring state and enabling
* as necessary.
*/
void ospm_resume_display(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_psb_private *dev_priv = dev->dev_private;
struct psb_gtt *pg = dev_priv->pg;
PSB_DEBUG_ENTRY("\n");
if (ospm_power_is_hw_on(OSPM_DISPLAY_ISLAND)) {
DRM_INFO("%s: Exit because hw on\n", __func__);
return;
}
/*restore performance mode*/
REG_WRITE(MRST_PERF_MODE, dev_priv->savePERF_MODE);
REG_WRITE(PSB_GFX_CLOCKGATING, dev_priv->saveCLOCKGATING);
REG_WRITE(PSB_MSVDX_CLOCKGATING, dev_priv->saveVED_CG_DIS);
REG_WRITE(PSB_TOPAZ_CLOCKGATING, dev_priv->saveVEC_CG_DIS);
#ifdef CONFIG_MDFD_GL3
REG_WRITE(MDFLD_GL3_CONTROL, dev_priv->saveGL3_CTL);
REG_WRITE(MDFLD_GL3_USE_WRT_INVAL, dev_priv->saveGL3_USE_WRT_INVAL);
#endif
/* turn on the display power island */
ospm_power_island_up(OSPM_DISPLAY_ISLAND);
REG_WRITE(PSB_PGETBL_CTL, pg->pge_ctl | _PSB_PGETBL_ENABLED);
pci_write_config_word(pdev, PSB_GMCH_CTRL,
pg->gmch_ctrl | _PSB_GMCH_ENABLED);
if (dev_priv->panel_desc & DISPLAY_A)
mdfld_restore_display_registers(dev, 0);
if (dev_priv->panel_desc & DISPLAY_C)
mdfld_restore_display_registers(dev, 2);
/*
* Don't restore Display B registers during resuming, if HDMI
* isn't turned on before suspending.
*/
if (dev_priv->panel_desc & DISPLAY_B) {
android_hdmi_resume_display(dev);
/*devices connect status will be changed
when system suspend,re-detect once here*/
if (android_hdmi_is_connected(dev))
mid_hdmi_audio_resume(dev);
}
mdfld_restore_cursor_overlay_registers(dev);
mdfld_adjust_display_fifo(dev);
}
/*
* ospm_suspend_pci
*
* Description: Suspend the pci device saving state and disabling
* as necessary.
*/
void ospm_suspend_pci(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_psb_private *dev_priv = dev->dev_private;
int bsm, vbt;
if (gbSuspended)
return;
PSB_DEBUG_PM("ospm_suspend_pci\n");
pci_save_state(pdev);
pci_read_config_dword(pdev, 0x5C, &bsm);
dev_priv->saveBSM = bsm;
pci_read_config_dword(pdev, 0xFC, &vbt);
dev_priv->saveVBT = vbt;
pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr);
pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
gbSuspended = true;
}
/*
* ospm_resume_pci
*
* Description: Resume the pci device restoring state and enabling
* as necessary.
*/
static bool ospm_resume_pci(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_psb_private *dev_priv = dev->dev_private;
int ret = 0;
if (!gbSuspended)
return true;
PSB_DEBUG_PM("ospm_resume_pci.\n");
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
pci_write_config_dword(pdev, 0x5c, dev_priv->saveBSM);
pci_write_config_dword(pdev, 0xFC, dev_priv->saveVBT);
/* retoring MSI address and data in PCIx space */
pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr);
pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data);
ret = pci_enable_device(pdev);
if (ret != 0)
printk(KERN_ALERT "ospm_resume_pci: pci_enable_device failed: %d\n", ret);
else {
if (IS_MDFLD(dev)) {
/*restore performance mode*/
PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE);
PSB_WVDC32(dev_priv->saveCLOCKGATING,
PSB_GFX_CLOCKGATING);
PSB_WVDC32(dev_priv->saveVED_CG_DIS,
PSB_MSVDX_CLOCKGATING);
PSB_WVDC32(dev_priv->saveVEC_CG_DIS,
PSB_TOPAZ_CLOCKGATING);
#ifdef CONFIG_MDFD_GL3
PSB_WVDC32(dev_priv->saveGL3_CTL, MDFLD_GL3_CONTROL);
PSB_WVDC32(dev_priv->saveGL3_USE_WRT_INVAL,
MDFLD_GL3_USE_WRT_INVAL);
#endif
}
gbSuspended = false;
}
return !gbSuspended;
}
static void ospm_early_suspend()
{
struct drm_psb_private *dev_priv = gpDrmDevice->dev_private;
struct drm_device *dev = dev_priv->dev;
struct drm_encoder *encoder;
struct drm_encoder_helper_funcs *enc_funcs;
if (!(drm_psb_use_cases_control & PSB_SUSPEND_ENABLE))
return ;
PSB_DEBUG_ENTRY("\n");
dev_priv->b_dsr_enable_status = dev_priv->b_dsr_enable;
if (dev_priv->b_dsr_enable) {
dev_priv->exit_idle(dev,
MDFLD_DSR_2D_3D,
NULL,
0);
dev_priv->b_dsr_enable = false;
}
/* protect early_suspend with dpms and mode config */
mutex_lock(&dev->mode_config.mutex);
list_for_each_entry(encoder,
&dev->mode_config.encoder_list,
head) {
enc_funcs = encoder->helper_private;
if (!drm_helper_encoder_in_use(encoder))
continue;
if (enc_funcs && enc_funcs->save)
enc_funcs->save(encoder);
}
gbdispstatus = false;
dev_priv->early_suspended = true;
mutex_unlock(&dev->mode_config.mutex);
#ifdef CONFIG_GFX_RTPM
pm_runtime_allow(&gpDrmDevice->pdev->dev);
#endif
}
static void ospm_late_resume()
{
struct drm_psb_private *dev_priv = gpDrmDevice->dev_private;
struct drm_device *dev = dev_priv->dev;
struct drm_encoder *encoder;
struct drm_encoder_helper_funcs *enc_funcs;
if (!(drm_psb_use_cases_control & PSB_SUSPEND_ENABLE))
return ;
PSB_DEBUG_ENTRY("\n");
/* protect early_suspend with dpms and mode config */
mutex_lock(&dev->mode_config.mutex);
dev_priv->early_suspended = false;
#ifdef CONFIG_GFX_RTPM
pm_runtime_forbid(&gpDrmDevice->pdev->dev);
mutex_lock(&g_ospm_mutex);
ospm_resume_pci(gpDrmDevice->pdev);
ospm_resume_display(gpDrmDevice->pdev);
psb_irq_preinstall_islands(gpDrmDevice, OSPM_DISPLAY_ISLAND);
psb_irq_postinstall_islands(gpDrmDevice, OSPM_DISPLAY_ISLAND);
mutex_unlock(&g_ospm_mutex);
#endif
list_for_each_entry(encoder,
&dev->mode_config.encoder_list,
head) {
enc_funcs = encoder->helper_private;
if (!drm_helper_encoder_in_use(encoder))
continue;
if (enc_funcs && enc_funcs->restore)
enc_funcs->restore(encoder);
}
gbdispstatus = true;
dev_priv->b_dsr_enable = dev_priv->b_dsr_enable_status;
if (lastFailedBrightness > 0)
psb_set_brightness(NULL);
mutex_unlock(&dev->mode_config.mutex);
}
#if SUPPORT_EARLY_SUSPEND
static void gfx_early_suspend(struct early_suspend *h)
{
ospm_early_suspend();
}
#endif /* if SUPPORT_EARLY_SUSPEND */
#if SUPPORT_EARLY_SUSPEND
static void gfx_late_resume(struct early_suspend *h)
{
ospm_late_resume();
}
#endif /* if SUPPORT_EARLY_SUSPEND */
/*
* ospm_power_suspend
*
* Description: OSPM is telling our driver to suspend so save state
* and power down all hardware.
*/
int ospm_power_suspend(struct pci_dev *pdev, pm_message_t state)
{
int ret = 0;
int graphics_access_count;
int videoenc_access_count;
int videodec_access_count;
int display_access_count;
struct drm_psb_private *dev_priv = gpDrmDevice->dev_private;
unsigned long flags;
bool hdmi_audio_suspend = true;
if (gbSuspendInProgress || gbResumeInProgress) {
PSB_DEBUG_PM(KERN_ALERT "%s: system BUSY\n", __func__);
return -EBUSY;
}
PSB_DEBUG_PM("enter ospm_power_suspend\n");
mutex_lock(&g_ospm_mutex);
if (!gbSuspended) {
hdmi_audio_suspend = mid_hdmi_audio_suspend(dev_priv->dev);
/* Turn on suspending first before check the access count */
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
gbSuspendInProgress = true;
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
graphics_access_count = atomic_read(&g_graphics_access_count);
videoenc_access_count = atomic_read(&g_videoenc_access_count);
videodec_access_count = atomic_read(&g_videodec_access_count);
display_access_count = atomic_read(&g_display_access_count);
if (graphics_access_count
|| videoenc_access_count
|| videodec_access_count
|| display_access_count
|| (hdmi_audio_suspend == false))
ret = -EBUSY;
if (!ret) {
if (ospm_runtime_pm_msvdx_suspend(gpDrmDevice) != 0)
ret = -EBUSY;
if (ospm_runtime_pm_topaz_suspend(gpDrmDevice) != 0)
ret = -EBUSY;
if (!ret) {
ospm_suspend_display(gpDrmDevice);
/* When suspend, the gfx island may increase
** its access count, hence the PCI host
** shouldn't be power off
*/
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
graphics_access_count =
atomic_read(&g_graphics_access_count);
if (!graphics_access_count) {
pcihostSuspendInProgress = true;
spin_unlock_irqrestore(
&dev_priv->ospm_lock, flags);
ospm_suspend_pci(pdev);
pcihostSuspendInProgress = false;
} else {
spin_unlock_irqrestore(
&dev_priv->ospm_lock, flags);
ret = -EBUSY;
}
}
} else {
PSB_DEBUG_PM("ospm_power_suspend: device busy:");
PSB_DEBUG_PM("SGX %d Enc %d Dec %d Display %d\n",
graphics_access_count, videoenc_access_count,
videodec_access_count, display_access_count);
}
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
gbSuspendInProgress = false;
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
}
mutex_unlock(&g_ospm_mutex);
return ret;
}
void ospm_power_graphics_island_up(int hw_islands)
{
unsigned long flags;
unsigned long irqflags;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) gpDrmDevice->dev_private;
if (hw_islands) {
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
/*
If pmu_nc_set_power_state fails then accessing HW
reg would result in a crash - IERR/Fabric error.
*/
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
PSB_DEBUG_PM("power on gfx_islands: 0x%x\n", hw_islands);
if (pmu_nc_set_power_state(hw_islands,
OSPM_ISLAND_UP, APM_REG_TYPE))
BUG();
if (hw_islands & OSPM_GRAPHICS_ISLAND)
atomic_inc(&g_graphics_access_count);
g_hw_power_status_mask |= hw_islands;
psb_irq_preinstall_graphics_islands(gpDrmDevice,
OSPM_GRAPHICS_ISLAND);
psb_irq_postinstall_graphics_islands(gpDrmDevice,
OSPM_GRAPHICS_ISLAND);
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
}
}
/*
* ospm_power_island_up
*
* Description: Restore power to the specified island(s) (powergating)
*/
int ospm_power_island_up(int hw_islands)
{
u32 dc_islands = 0;
u32 gfx_islands = hw_islands;
unsigned long flags;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) gpDrmDevice->dev_private;
int ret = 0;
if (hw_islands & OSPM_DISPLAY_ISLAND) {
/*Power-up required islands only*/
if (dev_priv->panel_desc & DISPLAY_A)
dc_islands |= OSPM_DISPLAY_A_ISLAND;
if (dev_priv->panel_desc & DISPLAY_B)
dc_islands |= OSPM_DISPLAY_B_ISLAND;
if (dev_priv->panel_desc & DISPLAY_C)
dc_islands |= OSPM_DISPLAY_C_ISLAND;
if (dev_priv->panel_desc)
dc_islands |= OSPM_MIPI_ISLAND;
/*
If pmu_nc_set_power_state fails then accessing HW
reg would result in a crash - IERR/Fabric error.
*/
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
PSB_DEBUG_PM("power up display islands 0x%x.\n", dc_islands);
if (pmu_nc_set_power_state(dc_islands,
OSPM_ISLAND_UP, OSPM_REG_TYPE))
BUG();
g_hw_power_status_mask |= OSPM_DISPLAY_ISLAND;
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
/* handle other islands */
gfx_islands = hw_islands & ~OSPM_DISPLAY_ISLAND;
}
if (gfx_islands) {
/*
If pmu_nc_set_power_state fails then accessing HW
reg would result in a crash - IERR/Fabric error.
*/
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
PSB_DEBUG_PM("power on gfx_islands: 0x%x\n", gfx_islands);
ret = pmu_nc_set_power_state(gfx_islands,
OSPM_ISLAND_UP, APM_REG_TYPE);
if (ret) {
PSB_DEBUG_PM("pmu_nc_set_power_state fails, ret is %d\n", ret);
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
return ret;
}
if (gfx_islands & OSPM_GRAPHICS_ISLAND)
atomic_inc(&g_graphics_access_count);
g_hw_power_status_mask |= gfx_islands;
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
}
return 0;
}
/*
* ospm_power_resume
*/
int ospm_power_resume(struct pci_dev *pdev)
{
if (gbSuspendInProgress || gbResumeInProgress) {
DRM_INFO("%s: suspend/resume in progress\n", __func__);
return 0;
}
PSB_DEBUG_ENTRY("\n");
mutex_lock(&g_ospm_mutex);
gbResumeInProgress = true;
ospm_resume_pci(pdev);
ospm_resume_display(gpDrmDevice->pdev);
psb_irq_preinstall_islands(gpDrmDevice, OSPM_DISPLAY_ISLAND);
psb_irq_postinstall_islands(gpDrmDevice, OSPM_DISPLAY_ISLAND);
gbResumeInProgress = false;
mutex_unlock(&g_ospm_mutex);
return 0;
}
/*
* ospm_power_island_down_video
*
* Description: Cut power to the specified video island(s) (powergating)
* If pmu_nc_set_power_state fails then accessing HW
* reg would result in a crash - IERR/Fabric error.
*/
static void ospm_power_island_down_video(int video_islands)
{
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) gpDrmDevice->dev_private;
unsigned long flags;
PSB_DEBUG_PM("MSVDX: power off video island %d.\n", video_islands);
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
if (video_islands & OSPM_VIDEO_DEC_ISLAND) {
if (pmu_nc_set_power_state(OSPM_VIDEO_DEC_ISLAND,
OSPM_ISLAND_DOWN, APM_REG_TYPE))
BUG();
g_hw_power_status_mask &= ~OSPM_VIDEO_DEC_ISLAND;
}
if (video_islands & OSPM_VIDEO_ENC_ISLAND) {
if (pmu_nc_set_power_state(OSPM_VIDEO_ENC_ISLAND,
OSPM_ISLAND_DOWN, APM_REG_TYPE))
BUG();
g_hw_power_status_mask &= ~OSPM_VIDEO_ENC_ISLAND;
}
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
}
void ospm_power_graphics_island_down(int hw_islands)
{
u32 dc_islands = 0;
u32 gfx_islands = hw_islands;
unsigned long flags;
unsigned long irqflags;
struct mdfld_dsi_config *dsi_config;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) gpDrmDevice->dev_private;
if (gfx_islands) {
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
/* both graphics and GL3 based on graphics_access count */
if (gfx_islands & OSPM_GL3_CACHE_ISLAND) {
#ifdef CONFIG_MDFD_GL3
/*
Make sure both GFX & Video aren't
using GL3
*/
if (atomic_read(&g_graphics_access_count) ||
(g_hw_power_status_mask &
(OSPM_VIDEO_DEC_ISLAND |
OSPM_VIDEO_ENC_ISLAND |
OSPM_GRAPHICS_ISLAND)) ||
(drm_psb_gl3_enable == 0)) {
gfx_islands &= ~OSPM_GL3_CACHE_ISLAND;
if (!gfx_islands) {
spin_unlock_irqrestore(
&dev_priv->ospm_lock, flags);
spin_unlock_irqrestore(
&dev_priv->irqmask_lock, irqflags);
return ;
}
}
#endif
}
if (gfx_islands & OSPM_GRAPHICS_ISLAND) {
if (atomic_read(&g_graphics_access_count))
gfx_islands &= ~OSPM_GRAPHICS_ISLAND;
else
psb_irq_uninstall_graphics_islands(gpDrmDevice,
OSPM_GRAPHICS_ISLAND);
}
/*
If pmu_nc_set_power_state fails then accessing HW
reg would result in a crash - IERR/Fabric error.
*/
PSB_DEBUG_PM("power off gfx/gl3 island 0x%x.\n", gfx_islands);
g_hw_power_status_mask &= ~gfx_islands;
if (pmu_nc_set_power_state(gfx_islands,
OSPM_ISLAND_DOWN, APM_REG_TYPE))
BUG();
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
/* From the test, after enter DSR level 1, only GFX island
** has chance to power on and leave PCI host power ungated
** Because after SGX complete a buffer, it will trigger
** PROCESS_QUEUES command to SGX even if there are no more
** 3D thing to do, hence power on SGX and PCI. Because there are
** nothing remain to flip, exit_dsr doesn't be called,
** so PCI host remain power ungated.
** here just give another chance to enter DSR
** Note:
*/
#if 0
if (gfx_islands & OSPM_GRAPHICS_ISLAND) {
dsi_config = dev_priv->dsi_configs[0];
mdfld_dsi_dsr_forbid(dsi_config);
mdfld_dsi_dsr_allow(dsi_config);
}
#endif
}
}
EXPORT_SYMBOL(ospm_power_graphics_island_down);
/*
* ospm_power_island_down
*
* Description: Cut power to the specified island(s) (powergating)
*/
void ospm_power_island_down(int hw_islands)
{
u32 dc_islands = 0;
u32 gfx_islands = hw_islands;
int video_islands = hw_islands &
(OSPM_VIDEO_DEC_ISLAND | OSPM_VIDEO_ENC_ISLAND);
unsigned long flags;
struct mdfld_dsi_config *dsi_config;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) gpDrmDevice->dev_private;
PSB_DEBUG_PM("power down hw_islands: %x\n", hw_islands);
if (video_islands) {
ospm_power_island_down_video(video_islands);
hw_islands = hw_islands &
~(OSPM_VIDEO_DEC_ISLAND | OSPM_VIDEO_ENC_ISLAND);
}
if (hw_islands & OSPM_DISPLAY_ISLAND) {
/*Power gate all display islands.*/
dc_islands |= (OSPM_DISPLAY_A_ISLAND |
OSPM_DISPLAY_B_ISLAND |
OSPM_DISPLAY_C_ISLAND |
OSPM_MIPI_ISLAND);
/*
If pmu_nc_set_power_state fails then accessing HW
reg would result in a crash - IERR/Fabric error.
*/
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
/*last chance of canceling the power off*/
/*
* if (atomic_read(&g_display_access_count))
* goto unlock;
*/
PSB_DEBUG_PM("power off display island\n");
g_hw_power_status_mask &= ~OSPM_DISPLAY_ISLAND;
if (pmu_nc_set_power_state(dc_islands,
OSPM_ISLAND_DOWN, OSPM_REG_TYPE))
BUG();
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
/* handle other islands */
gfx_islands = hw_islands & ~OSPM_DISPLAY_ISLAND;
}
if (gfx_islands) {
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
/* both graphics and GL3 based on graphics_access count */
if (gfx_islands & OSPM_GL3_CACHE_ISLAND) {
#ifdef CONFIG_MDFD_GL3
/*
Make sure both GFX & Video aren't
using GL3
*/
if (atomic_read(&g_graphics_access_count) ||
(g_hw_power_status_mask &
(OSPM_VIDEO_DEC_ISLAND |
OSPM_VIDEO_ENC_ISLAND |
OSPM_GRAPHICS_ISLAND)) ||
(drm_psb_gl3_enable == 0)) {
gfx_islands &= ~OSPM_GL3_CACHE_ISLAND;
if (!gfx_islands) {
spin_unlock_irqrestore(
&dev_priv->ospm_lock, flags);
return ;
}
}
#endif
}
if (gfx_islands & OSPM_GRAPHICS_ISLAND) {
if (atomic_read(&g_graphics_access_count))
gfx_islands &= ~OSPM_GRAPHICS_ISLAND;
}
/*
If pmu_nc_set_power_state fails then accessing HW
reg would result in a crash - IERR/Fabric error.
*/
PSB_DEBUG_PM("power off gfx/gl3 island 0x%x.\n", gfx_islands);
g_hw_power_status_mask &= ~gfx_islands;
if (pmu_nc_set_power_state(gfx_islands,
OSPM_ISLAND_DOWN, APM_REG_TYPE))
BUG();
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
/* From the test, after enter DSR level 1, only GFX island
** has chance to power on and leave PCI host power ungated
** Because after SGX complete a buffer, it will trigger
** PROCESS_QUEUES command to SGX even if there are no more
** 3D thing to do, hence power on SGX and PCI. Because there are
** nothing remain to flip, exit_dsr doesn't be called,
** so PCI host remain power ungated.
** here just give another chance to enter DSR
** Note:
*/
#if 0
if (gfx_islands & OSPM_GRAPHICS_ISLAND) {
dsi_config = dev_priv->dsi_configs[0];
mdfld_dsi_dsr_forbid(dsi_config);
mdfld_dsi_dsr_allow(dsi_config);
}
#endif
}
}
EXPORT_SYMBOL(ospm_power_island_down);
/*
* ospm_power_is_hw_on
*
* Description: do an instantaneous check for if the specified islands
* are on. Only use this in cases where you know the g_state_change_mutex
* is already held such as in irq install/uninstall. Otherwise, use
* ospm_power_using_hw_begin().
*/
bool ospm_power_is_hw_on(int hw_islands)
{
unsigned long flags;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) gpDrmDevice->dev_private;
bool ret = false;
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
ret = ((g_hw_power_status_mask & hw_islands)
== hw_islands) ? true : false;
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
return ret;
}
EXPORT_SYMBOL(ospm_power_is_hw_on);
/* For video case, we only force enable hw in process context.
* Protected by g_ospm_mutex */
bool ospm_power_using_video_begin(int video_island)
{
bool ret = true;
bool island_is_on = true;
struct pci_dev *pdev = gpDrmDevice->pdev;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *)gpDrmDevice->dev_private;
struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
bool already_increase = false;
PSB_DEBUG_PM("MSVDX: need power on island 0x%x.\n", video_island);
if (!(video_island & (OSPM_VIDEO_DEC_ISLAND | OSPM_VIDEO_ENC_ISLAND)))
return false;
#ifdef CONFIG_GFX_RTPM
/* if system suspend is in progress, do NOT allow system resume. if
* runtime_status is RPM_SUSPENDING, and here call pm_runtime_get will
* call rpm_resume indirectly, it causes defferred_resume be set to
* ture, so at the end of rpm_suspend(), rpm_resume() will be called.
* it will block system from entering s0ix */
if (gbSuspendInProgress) {
DRM_INFO("%s: suspend in progress,"
"call pm_runtime_get_noresume\n", __func__);
pm_runtime_get_noresume(&pdev->dev);
} else {
pm_runtime_get(&pdev->dev);
}
/* Taking this lock is very important to keep consistent with
*runtime framework */
spin_lock_irq(&pdev->dev.power.lock);
recheck:
if (pdev->dev.power.runtime_status == RPM_SUSPENDING) {
DEFINE_WAIT(wait);
/* Wait for the other suspend running to finish */
for (;;) {
prepare_to_wait(&pdev->dev.power.wait_queue, &wait,
TASK_UNINTERRUPTIBLE);
if (pdev->dev.power.runtime_status != RPM_SUSPENDING)
break;
spin_unlock_irq(&pdev->dev.power.lock);
schedule();
spin_lock_irq(&pdev->dev.power.lock);
}
finish_wait(&pdev->dev.power.wait_queue, &wait);
goto recheck;
}
/* Because !force_on has been done above, so here is force_on case
**it must be process context in current code base, so it will power on
** island defintely, so increase access_count here to prevent another
** suspending thread run async
*/
switch (video_island) {
case OSPM_VIDEO_ENC_ISLAND:
atomic_inc(&g_videoenc_access_count);
break;
case OSPM_VIDEO_DEC_ISLAND:
atomic_inc(&g_videodec_access_count);
break;
}
already_increase = true;
spin_unlock_irq(&pdev->dev.power.lock);
#endif
/* It must be process context, will not be called in irq */
mutex_lock(&g_ospm_mutex);
island_is_on = ospm_power_is_hw_on(video_island);
if (island_is_on)
goto out;
gbResumeInProgress = true;
/* Because gfx island may resume pci silently,
** so need sync with gfx
*/
if (ospm_resume_pci(pdev) == false) {
ret = false;
goto out;
}
switch (video_island) {
case OSPM_VIDEO_DEC_ISLAND:
if (!ospm_power_is_hw_on(OSPM_DISPLAY_ISLAND)) {
/* printk(KERN_ALERT "%s power on display
** for video decode use\n", __func__);
*/
ospm_resume_display(pdev);
psb_irq_preinstall_islands(gpDrmDevice,
OSPM_DISPLAY_ISLAND);
psb_irq_postinstall_islands(gpDrmDevice,
OSPM_DISPLAY_ISLAND);
} else {
/* printk(KERN_ALERT
** "%s display is already on
** for video decode use\n", __func__);
*/
}
if (!ospm_power_is_hw_on(OSPM_VIDEO_DEC_ISLAND)) {
/* printk(KERN_ALERT "%s power on video decode\n",
** __func__);
*/
/*
* GL3 power island needs to be on for MSVDX working.
* We found this during enabling new MSVDX firmware
* uploading mechanism(by PUNIT) for Penwell D0.
*/
#ifdef CONFIG_MDFD_GL3
if (ospm_power_island_up(OSPM_GL3_CACHE_ISLAND | OSPM_VIDEO_DEC_ISLAND)) {
#else
if (ospm_power_island_up(OSPM_VIDEO_DEC_ISLAND)) {
#endif
ret = false;
goto out;
}
if (msvdx_priv->fw_loaded_by_punit) {
int reg_ret;
reg_ret = psb_wait_for_register(dev_priv,
MSVDX_COMMS_SIGNATURE,
MSVDX_COMMS_SIGNATURE_VALUE,
0xffffffff, 2000000, 5);
if (reg_ret)
PSB_DEBUG_WARN("WARN: load fw fail,\n"
"MSVDX_COMMS_SIGNATURE reg is 0x%x,"
"MSVDX_COMMS_FW_STATUS reg is 0x%x,"
"MTX_ENABLE reg is 0x%x.\n",
PSB_RMSVDX32(MSVDX_COMMS_SIGNATURE),
PSB_RMSVDX32(MSVDX_COMMS_FW_STATUS),
PSB_RMSVDX32(MTX_ENABLE_OFFSET));
}
ospm_runtime_pm_msvdx_resume(gpDrmDevice);
psb_irq_preinstall_islands(gpDrmDevice,
OSPM_VIDEO_DEC_ISLAND);
psb_irq_postinstall_islands(gpDrmDevice,
OSPM_VIDEO_DEC_ISLAND);
} else {
#ifdef CONFIG_MDFD_GL3
if (ospm_power_island_up(OSPM_GL3_CACHE_ISLAND)) {
ret = false;
goto out;
}
#endif
}
break;
case OSPM_VIDEO_ENC_ISLAND:
if (IS_MRST(gpDrmDevice) &&
(!ospm_power_is_hw_on(
OSPM_DISPLAY_ISLAND))) {
ospm_resume_display(pdev);
psb_irq_preinstall_islands(gpDrmDevice,
OSPM_DISPLAY_ISLAND);
psb_irq_postinstall_islands(gpDrmDevice,
OSPM_DISPLAY_ISLAND);
}
if (!ospm_power_is_hw_on(OSPM_VIDEO_ENC_ISLAND)) {
/* printk(KERN_ALERT "%s power on video
** encode\n", __func__);
*/
#ifdef CONFIG_MDFD_GL3
if (ospm_power_island_up(OSPM_VIDEO_ENC_ISLAND | OSPM_GL3_CACHE_ISLAND)) {
#else
if (ospm_power_island_up(OSPM_VIDEO_ENC_ISLAND)) {
#endif
ret = false;
goto out;
}
ospm_runtime_pm_topaz_resume(gpDrmDevice);
psb_irq_preinstall_islands(gpDrmDevice,
OSPM_VIDEO_ENC_ISLAND);
psb_irq_postinstall_islands(gpDrmDevice,
OSPM_VIDEO_ENC_ISLAND);
} else {
#ifdef CONFIG_MDFD_GL3
if (ospm_power_island_up(OSPM_GL3_CACHE_ISLAND)) {
ret = false;
goto out;
}
#endif
}
break;
default:
printk(KERN_ALERT "%s unknown island !!!!\n",
__func__);
break;
}
out:
if (!ret)
printk(KERN_ALERT "%s: %d failed\n",
__func__, video_island);
gbResumeInProgress = false;
if (ret) {
if (!already_increase) {
switch (video_island) {
case OSPM_VIDEO_ENC_ISLAND:
atomic_inc(&g_videoenc_access_count);
break;
case OSPM_VIDEO_DEC_ISLAND:
atomic_inc(&g_videodec_access_count);
break;
}
}
}
#ifdef CONFIG_GFX_RTPM
else {
pm_runtime_put(&pdev->dev);
}
#endif
mutex_unlock(&g_ospm_mutex);
return ret;
}
/*
* ospm_power_using_hw_begin
*
* Description: Notify PowerMgmt module that you will be accessing the
* specified island's hw so don't power it off. If force_on is true,
* this will power on the specified island if it is off.
* Otherwise, this will return false and the caller is expected to not
* access the hw.
*
* NOTE:The function doesn't support force_on in atomic context,
* as there may sleep when resuming these islands. If u have to
* resume in atomic context for these islands, u need revise ur
* logic and move the resume to a process context. return true if
* the island is on(no matter it's forced or already on) otherwise
* false is returned.
*/
bool ospm_power_using_hw_begin(int hw_island, UHBUsage usage)
{
bool ret = true;
bool island_is_on = true;
struct pci_dev *pdev = gpDrmDevice->pdev;
IMG_UINT32 deviceID = 0;
bool force_on = usage ? true : false;
struct drm_psb_private *dev_priv =
(struct drm_psb_private *) gpDrmDevice->dev_private;
unsigned long flags;
if (!(hw_island & (OSPM_GRAPHICS_ISLAND | OSPM_DISPLAY_ISLAND |
OSPM_GL3_CACHE_ISLAND)))
return false;
#ifdef CONFIG_GFX_RTPM
if (force_on)
pm_runtime_get_sync(&pdev->dev);
else
pm_runtime_get_noresume(&pdev->dev);
#endif
/* Only process context is allowed when in
** force_on == true case. In force_on == false cases,
** it may be in atomic or process context, so use spin_lock_irq
*/
if (!force_on) {
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
island_is_on = ((g_hw_power_status_mask & hw_island)
== hw_island) ? true : false;
/* if island is off or another thread has been in
** suspend progress, we should return false to
** keep consistent, the caller should have dealt
** with return value properly
** Note: when in interrupt context, we should
** always return true, for it has triggerred interrupt.
*/
if ((!island_is_on) ||
((((hw_island == OSPM_DISPLAY_ISLAND) &&
gbSuspendInProgress) ||
((hw_island == OSPM_GRAPHICS_ISLAND) &&
pcihostSuspendInProgress)) && (!in_interrupt()))) {
spin_unlock_irqrestore(&dev_priv->ospm_lock,
flags);
#ifdef CONFIG_GFX_RTPM
pm_runtime_put(&pdev->dev);
#endif
return false;
}
/* After sanity check can increase the access_count */
if (hw_island == OSPM_DISPLAY_ISLAND)
atomic_inc(&g_display_access_count);
else if (hw_island == OSPM_GRAPHICS_ISLAND)
atomic_inc(&g_graphics_access_count);
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
return true;
}
/* Actually we can remove the codes below for following
** mutex lock can keep race condition safe. These codes
** exist only for facilitating gfx misuse force_on display
** while taking spin lock. These codes can safely be removed
** once gfx doesn't force on engines when taking spinlock
*/
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
island_is_on = ((g_hw_power_status_mask & hw_island)
== hw_island) ? true : false;
if (island_is_on && force_on &&
((hw_island == OSPM_DISPLAY_ISLAND) &&
!gbSuspendInProgress)) {
atomic_inc(&g_display_access_count);
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
return true;
}
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
BUG_ON(in_interrupt());
mutex_lock(&g_ospm_mutex);
spin_lock_irqsave(&dev_priv->ospm_lock, flags);
island_is_on = ((g_hw_power_status_mask & hw_island)
== hw_island) ? true : false;
if (island_is_on && (hw_island == OSPM_GRAPHICS_ISLAND))
atomic_inc(&g_graphics_access_count);
spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
if (island_is_on)
goto increase_count;
gbResumeInProgress = true;
/* Because gfx island may resume pci silently,
** so need sync with gfx
*/
ret = ospm_resume_pci(pdev);
if (ret) {
if (hw_island == OSPM_DISPLAY_ISLAND) {
deviceID = gui32MRSTDisplayDeviceID;
ospm_resume_display(pdev);
psb_irq_preinstall_islands(gpDrmDevice,
OSPM_DISPLAY_ISLAND);
psb_irq_postinstall_islands(gpDrmDevice,
OSPM_DISPLAY_ISLAND);
} else if (hw_island == OSPM_GRAPHICS_ISLAND) {
deviceID = gui32SGXDeviceID;
#ifdef CONFIG_MDFD_GL3
ospm_power_graphics_island_up(
OSPM_GRAPHICS_ISLAND |
OSPM_GL3_CACHE_ISLAND);
#else
ospm_power_graphics_island_up(
OSPM_GRAPHICS_ISLAND);
#endif
}
}
if (!ret)
DRM_INFO("%s: %d failed\n", __func__, hw_island);
gbResumeInProgress = false;
increase_count:
if (ret) {
if (hw_island == OSPM_DISPLAY_ISLAND)
atomic_inc(&g_display_access_count);
}
#ifdef CONFIG_GFX_RTPM
else
pm_runtime_put(&pdev->dev);
#endif
mutex_unlock(&g_ospm_mutex);
return ret;
}
EXPORT_SYMBOL(ospm_power_using_hw_begin);
/*
* ospm_power_using_video_end
*
* Description: Notify PowerMgmt module that you are done accessing the
* specified video island's hw so feel free to power it off. Note that this
* function doesn't actually power off the islands.
*/
void ospm_power_using_video_end(int video_island)
{
PSB_DEBUG_PM("MSVDX: using video 0x%x end.\n", video_island);
if (!(video_island & (OSPM_VIDEO_ENC_ISLAND | OSPM_VIDEO_DEC_ISLAND)))
return;
switch (video_island) {
case OSPM_VIDEO_ENC_ISLAND:
if (atomic_read(&g_videoenc_access_count) <= 0)
DRM_ERROR("g_videoenc_access_count <=0.\n");
else
atomic_dec(&g_videoenc_access_count);
break;
case OSPM_VIDEO_DEC_ISLAND:
if (atomic_read(&g_videodec_access_count) <= 0)
DRM_ERROR("g_videodec_access_count <=0.\n");
else
atomic_dec(&g_videodec_access_count);
break;
}
#ifdef CONFIG_GFX_RTPM
/* decrement runtime pm ref count */
pm_runtime_put(&gpDrmDevice->pdev->dev);
#endif
}
/*
* ospm_power_using_hw_end
*
* Description: Notify PowerMgmt module that you are done accessing the
* specified island's hw so feel free to power it off. Note that this
* function doesn't actually power off the islands.
*/
void ospm_power_using_hw_end(int hw_island)
{
if (!(hw_island & (OSPM_GRAPHICS_ISLAND | OSPM_DISPLAY_ISLAND |
OSPM_GL3_CACHE_ISLAND)))
return;
switch (hw_island) {
case OSPM_GRAPHICS_ISLAND:
atomic_dec(&g_graphics_access_count);
break;
case OSPM_DISPLAY_ISLAND:
atomic_dec(&g_display_access_count);
break;
}
#ifdef CONFIG_GFX_RTPM
/* decrement runtime pm ref count */
pm_runtime_put(&gpDrmDevice->pdev->dev);
#endif
WARN_ON(atomic_read(&g_graphics_access_count) < 0);
WARN_ON(atomic_read(&g_display_access_count) < 0);
}
EXPORT_SYMBOL(ospm_power_using_hw_end);
int ospm_runtime_pm_allow(struct drm_device *dev)
{
struct drm_psb_private *dev_priv;
struct mdfld_dsi_config **dsi_configs;
bool panel_on = false, panel_on2 = false;
PSB_DEBUG_ENTRY("\n");
dev_priv = (struct drm_psb_private *)dev->dev_private;
dsi_configs = dev_priv->dsi_configs;
if (dev_priv->rpm_enabled)
return 0;
if (dsi_configs[0])
panel_on = dsi_configs[0]->dsi_hw_context.panel_on;
if (dsi_configs[1])
panel_on2 = dsi_configs[1]->dsi_hw_context.panel_on;
#ifdef CONFIG_GFX_RTPM
if (!panel_on && !panel_on2) {
pm_runtime_allow(&dev->pdev->dev);
dev_priv->rpm_enabled = 1;
DRM_INFO("Runtime PM enabled\n");
}
#endif
return 0;
}
void ospm_runtime_pm_forbid(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
PSB_DEBUG_ENTRY("\n");
#ifdef CONFIG_GFX_RTPM
pm_runtime_forbid(&dev->pdev->dev);
#endif
dev_priv->rpm_enabled = 0;
}
int psb_runtime_suspend(struct device *dev)
{
pm_message_t state;
struct drm_psb_private *dev_priv = gpDrmDevice->dev_private;
int ret = 0;
state.event = 0;
PSB_DEBUG_PM("psb_runtime_suspend is called.\n");
if (atomic_read(&g_graphics_access_count) ||
atomic_read(&g_videoenc_access_count) ||
(gbdispstatus == true) ||
atomic_read(&g_videodec_access_count) ||
atomic_read(&g_display_access_count))
return -EBUSY;
else
ret = ospm_power_suspend(gpDrmDevice->pdev, state);
if (!ret)
pm_qos_remove_request(&dev_priv->s0ix_qos);
return ret;
}
int psb_runtime_resume(struct device *dev)
{
struct drm_psb_private *dev_priv = gpDrmDevice->dev_private;
PSB_DEBUG_ENTRY("\n");
pm_qos_add_request(&dev_priv->s0ix_qos,
PM_QOS_CPU_DMA_LATENCY, CSTATE_EXIT_LATENCY_S0i1 - 1);
/* Nop for GFX */
return 0;
}
int psb_runtime_idle(struct device *dev)
{
struct drm_psb_private *dev_priv = gpDrmDevice->dev_private;
bool hdmi_audio_busy = false;
PSB_DEBUG_ENTRY("\n");
hdmi_audio_busy = mid_hdmi_audio_is_busy(dev_priv->dev);
if (atomic_read(&g_graphics_access_count) ||
atomic_read(&g_videoenc_access_count) ||
atomic_read(&g_videodec_access_count) ||
atomic_read(&g_display_access_count) ||
(gbdispstatus == true) ||
(hdmi_audio_busy == true))
return -EBUSY;
else
return 0;
}