/************************************************************************** * 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 * Rajesh Poornachandran * Yun(Mark) Tu * Zhouzhou(Scott) Fang */ #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 #endif #include #include #define SUPPORT_EARLY_SUSPEND 1 #include #if SUPPORT_EARLY_SUSPEND #include #endif /* if SUPPORT_EARLY_SUSPEND */ #include #include #include #include #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; }