18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/************************************************************************** 38c2ecf20Sopenharmony_ci * Copyright (c) 2007, Intel Corporation. 48c2ecf20Sopenharmony_ci * All Rights Reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to 78c2ecf20Sopenharmony_ci * develop this driver. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci **************************************************************************/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "mdfld_output.h" 148c2ecf20Sopenharmony_ci#include "power.h" 158c2ecf20Sopenharmony_ci#include "psb_drv.h" 168c2ecf20Sopenharmony_ci#include "psb_intel_reg.h" 178c2ecf20Sopenharmony_ci#include "psb_irq.h" 188c2ecf20Sopenharmony_ci#include "psb_reg.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * inline functions 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic inline u32 258c2ecf20Sopenharmony_cipsb_pipestat(int pipe) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci if (pipe == 0) 288c2ecf20Sopenharmony_ci return PIPEASTAT; 298c2ecf20Sopenharmony_ci if (pipe == 1) 308c2ecf20Sopenharmony_ci return PIPEBSTAT; 318c2ecf20Sopenharmony_ci if (pipe == 2) 328c2ecf20Sopenharmony_ci return PIPECSTAT; 338c2ecf20Sopenharmony_ci BUG(); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline u32 378c2ecf20Sopenharmony_cimid_pipe_event(int pipe) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci if (pipe == 0) 408c2ecf20Sopenharmony_ci return _PSB_PIPEA_EVENT_FLAG; 418c2ecf20Sopenharmony_ci if (pipe == 1) 428c2ecf20Sopenharmony_ci return _MDFLD_PIPEB_EVENT_FLAG; 438c2ecf20Sopenharmony_ci if (pipe == 2) 448c2ecf20Sopenharmony_ci return _MDFLD_PIPEC_EVENT_FLAG; 458c2ecf20Sopenharmony_ci BUG(); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic inline u32 498c2ecf20Sopenharmony_cimid_pipe_vsync(int pipe) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci if (pipe == 0) 528c2ecf20Sopenharmony_ci return _PSB_VSYNC_PIPEA_FLAG; 538c2ecf20Sopenharmony_ci if (pipe == 1) 548c2ecf20Sopenharmony_ci return _PSB_VSYNC_PIPEB_FLAG; 558c2ecf20Sopenharmony_ci if (pipe == 2) 568c2ecf20Sopenharmony_ci return _MDFLD_PIPEC_VBLANK_FLAG; 578c2ecf20Sopenharmony_ci BUG(); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline u32 618c2ecf20Sopenharmony_cimid_pipeconf(int pipe) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci if (pipe == 0) 648c2ecf20Sopenharmony_ci return PIPEACONF; 658c2ecf20Sopenharmony_ci if (pipe == 1) 668c2ecf20Sopenharmony_ci return PIPEBCONF; 678c2ecf20Sopenharmony_ci if (pipe == 2) 688c2ecf20Sopenharmony_ci return PIPECCONF; 698c2ecf20Sopenharmony_ci BUG(); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_civoid 738c2ecf20Sopenharmony_cipsb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci if ((dev_priv->pipestat[pipe] & mask) != mask) { 768c2ecf20Sopenharmony_ci u32 reg = psb_pipestat(pipe); 778c2ecf20Sopenharmony_ci dev_priv->pipestat[pipe] |= mask; 788c2ecf20Sopenharmony_ci /* Enable the interrupt, clear any pending status */ 798c2ecf20Sopenharmony_ci if (gma_power_begin(dev_priv->dev, false)) { 808c2ecf20Sopenharmony_ci u32 writeVal = PSB_RVDC32(reg); 818c2ecf20Sopenharmony_ci writeVal |= (mask | (mask >> 16)); 828c2ecf20Sopenharmony_ci PSB_WVDC32(writeVal, reg); 838c2ecf20Sopenharmony_ci (void) PSB_RVDC32(reg); 848c2ecf20Sopenharmony_ci gma_power_end(dev_priv->dev); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid 908c2ecf20Sopenharmony_cipsb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci if ((dev_priv->pipestat[pipe] & mask) != 0) { 938c2ecf20Sopenharmony_ci u32 reg = psb_pipestat(pipe); 948c2ecf20Sopenharmony_ci dev_priv->pipestat[pipe] &= ~mask; 958c2ecf20Sopenharmony_ci if (gma_power_begin(dev_priv->dev, false)) { 968c2ecf20Sopenharmony_ci u32 writeVal = PSB_RVDC32(reg); 978c2ecf20Sopenharmony_ci writeVal &= ~mask; 988c2ecf20Sopenharmony_ci PSB_WVDC32(writeVal, reg); 998c2ecf20Sopenharmony_ci (void) PSB_RVDC32(reg); 1008c2ecf20Sopenharmony_ci gma_power_end(dev_priv->dev); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci if (gma_power_begin(dev_priv->dev, false)) { 1088c2ecf20Sopenharmony_ci u32 pipe_event = mid_pipe_event(pipe); 1098c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= pipe_event; 1108c2ecf20Sopenharmony_ci PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); 1118c2ecf20Sopenharmony_ci PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); 1128c2ecf20Sopenharmony_ci gma_power_end(dev_priv->dev); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci if (dev_priv->pipestat[pipe] == 0) { 1198c2ecf20Sopenharmony_ci if (gma_power_begin(dev_priv->dev, false)) { 1208c2ecf20Sopenharmony_ci u32 pipe_event = mid_pipe_event(pipe); 1218c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask &= ~pipe_event; 1228c2ecf20Sopenharmony_ci PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); 1238c2ecf20Sopenharmony_ci PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); 1248c2ecf20Sopenharmony_ci gma_power_end(dev_priv->dev); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/** 1308c2ecf20Sopenharmony_ci * Display controller interrupt handler for pipe event. 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic void mid_pipe_event_handler(struct drm_device *dev, int pipe) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 1368c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci uint32_t pipe_stat_val = 0; 1398c2ecf20Sopenharmony_ci uint32_t pipe_stat_reg = psb_pipestat(pipe); 1408c2ecf20Sopenharmony_ci uint32_t pipe_enable = dev_priv->pipestat[pipe]; 1418c2ecf20Sopenharmony_ci uint32_t pipe_status = dev_priv->pipestat[pipe] >> 16; 1428c2ecf20Sopenharmony_ci uint32_t pipe_clear; 1438c2ecf20Sopenharmony_ci uint32_t i = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_lock(&dev_priv->irqmask_lock); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci pipe_stat_val = PSB_RVDC32(pipe_stat_reg); 1488c2ecf20Sopenharmony_ci pipe_stat_val &= pipe_enable | pipe_status; 1498c2ecf20Sopenharmony_ci pipe_stat_val &= pipe_stat_val >> 16; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci spin_unlock(&dev_priv->irqmask_lock); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Clear the 2nd level interrupt status bits 1548c2ecf20Sopenharmony_ci * Sometimes the bits are very sticky so we repeat until they unstick */ 1558c2ecf20Sopenharmony_ci for (i = 0; i < 0xffff; i++) { 1568c2ecf20Sopenharmony_ci PSB_WVDC32(PSB_RVDC32(pipe_stat_reg), pipe_stat_reg); 1578c2ecf20Sopenharmony_ci pipe_clear = PSB_RVDC32(pipe_stat_reg) & pipe_status; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (pipe_clear == 0) 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (pipe_clear) 1648c2ecf20Sopenharmony_ci dev_err(dev->dev, 1658c2ecf20Sopenharmony_ci "%s, can't clear status bits for pipe %d, its value = 0x%x.\n", 1668c2ecf20Sopenharmony_ci __func__, pipe, PSB_RVDC32(pipe_stat_reg)); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (pipe_stat_val & PIPE_VBLANK_STATUS || 1698c2ecf20Sopenharmony_ci (IS_MFLD(dev) && pipe_stat_val & PIPE_TE_STATUS)) { 1708c2ecf20Sopenharmony_ci struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); 1718c2ecf20Sopenharmony_ci struct gma_crtc *gma_crtc = to_gma_crtc(crtc); 1728c2ecf20Sopenharmony_ci unsigned long flags; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci drm_handle_vblank(dev, pipe); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 1778c2ecf20Sopenharmony_ci if (gma_crtc->page_flip_event) { 1788c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, 1798c2ecf20Sopenharmony_ci gma_crtc->page_flip_event); 1808c2ecf20Sopenharmony_ci gma_crtc->page_flip_event = NULL; 1818c2ecf20Sopenharmony_ci drm_crtc_vblank_put(crtc); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * Display controller interrupt handler. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci if (vdc_stat & _PSB_IRQ_ASLE) 1938c2ecf20Sopenharmony_ci psb_intel_opregion_asle_intr(dev); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (vdc_stat & _PSB_VSYNC_PIPEA_FLAG) 1968c2ecf20Sopenharmony_ci mid_pipe_event_handler(dev, 0); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (vdc_stat & _PSB_VSYNC_PIPEB_FLAG) 1998c2ecf20Sopenharmony_ci mid_pipe_event_handler(dev, 1); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci * SGX interrupt handler 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cistatic void psb_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 2088c2ecf20Sopenharmony_ci u32 val, addr; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (stat_1 & _PSB_CE_TWOD_COMPLETE) 2118c2ecf20Sopenharmony_ci val = PSB_RSGX32(PSB_CR_2D_BLIT_STATUS); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (stat_2 & _PSB_CE2_BIF_REQUESTER_FAULT) { 2148c2ecf20Sopenharmony_ci val = PSB_RSGX32(PSB_CR_BIF_INT_STAT); 2158c2ecf20Sopenharmony_ci addr = PSB_RSGX32(PSB_CR_BIF_FAULT); 2168c2ecf20Sopenharmony_ci if (val) { 2178c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_PF_N_RW) 2188c2ecf20Sopenharmony_ci DRM_ERROR("SGX MMU page fault:"); 2198c2ecf20Sopenharmony_ci else 2208c2ecf20Sopenharmony_ci DRM_ERROR("SGX MMU read / write protection fault:"); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_CACHE) 2238c2ecf20Sopenharmony_ci DRM_ERROR("\tCache requestor"); 2248c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_TA) 2258c2ecf20Sopenharmony_ci DRM_ERROR("\tTA requestor"); 2268c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_VDM) 2278c2ecf20Sopenharmony_ci DRM_ERROR("\tVDM requestor"); 2288c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_2D) 2298c2ecf20Sopenharmony_ci DRM_ERROR("\t2D requestor"); 2308c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_PBE) 2318c2ecf20Sopenharmony_ci DRM_ERROR("\tPBE requestor"); 2328c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_TSP) 2338c2ecf20Sopenharmony_ci DRM_ERROR("\tTSP requestor"); 2348c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_ISP) 2358c2ecf20Sopenharmony_ci DRM_ERROR("\tISP requestor"); 2368c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_USSEPDS) 2378c2ecf20Sopenharmony_ci DRM_ERROR("\tUSSEPDS requestor"); 2388c2ecf20Sopenharmony_ci if (val & _PSB_CBI_STAT_FAULT_HOST) 2398c2ecf20Sopenharmony_ci DRM_ERROR("\tHost requestor"); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci DRM_ERROR("\tMMU failing address is 0x%08x.\n", 2428c2ecf20Sopenharmony_ci (unsigned int)addr); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Clear bits */ 2478c2ecf20Sopenharmony_ci PSB_WSGX32(stat_1, PSB_CR_EVENT_HOST_CLEAR); 2488c2ecf20Sopenharmony_ci PSB_WSGX32(stat_2, PSB_CR_EVENT_HOST_CLEAR2); 2498c2ecf20Sopenharmony_ci PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR2); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciirqreturn_t psb_irq_handler(int irq, void *arg) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct drm_device *dev = arg; 2558c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 2568c2ecf20Sopenharmony_ci uint32_t vdc_stat, dsp_int = 0, sgx_int = 0, hotplug_int = 0; 2578c2ecf20Sopenharmony_ci u32 sgx_stat_1, sgx_stat_2; 2588c2ecf20Sopenharmony_ci int handled = 0; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci spin_lock(&dev_priv->irqmask_lock); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (vdc_stat & (_PSB_PIPE_EVENT_FLAG|_PSB_IRQ_ASLE)) 2658c2ecf20Sopenharmony_ci dsp_int = 1; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* FIXME: Handle Medfield 2688c2ecf20Sopenharmony_ci if (vdc_stat & _MDFLD_DISP_ALL_IRQ_FLAG) 2698c2ecf20Sopenharmony_ci dsp_int = 1; 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (vdc_stat & _PSB_IRQ_SGX_FLAG) 2738c2ecf20Sopenharmony_ci sgx_int = 1; 2748c2ecf20Sopenharmony_ci if (vdc_stat & _PSB_IRQ_DISP_HOTSYNC) 2758c2ecf20Sopenharmony_ci hotplug_int = 1; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci vdc_stat &= dev_priv->vdc_irq_mask; 2788c2ecf20Sopenharmony_ci spin_unlock(&dev_priv->irqmask_lock); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (dsp_int && gma_power_is_on(dev)) { 2818c2ecf20Sopenharmony_ci psb_vdc_interrupt(dev, vdc_stat); 2828c2ecf20Sopenharmony_ci handled = 1; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (sgx_int) { 2868c2ecf20Sopenharmony_ci sgx_stat_1 = PSB_RSGX32(PSB_CR_EVENT_STATUS); 2878c2ecf20Sopenharmony_ci sgx_stat_2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2); 2888c2ecf20Sopenharmony_ci psb_sgx_interrupt(dev, sgx_stat_1, sgx_stat_2); 2898c2ecf20Sopenharmony_ci handled = 1; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Note: this bit has other meanings on some devices, so we will 2938c2ecf20Sopenharmony_ci need to address that later if it ever matters */ 2948c2ecf20Sopenharmony_ci if (hotplug_int && dev_priv->ops->hotplug) { 2958c2ecf20Sopenharmony_ci handled = dev_priv->ops->hotplug(dev); 2968c2ecf20Sopenharmony_ci REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT)); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R); 3008c2ecf20Sopenharmony_ci (void) PSB_RVDC32(PSB_INT_IDENTITY_R); 3018c2ecf20Sopenharmony_ci rmb(); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!handled) 3048c2ecf20Sopenharmony_ci return IRQ_NONE; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_civoid psb_irq_preinstall(struct drm_device *dev) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 3128c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 3138c2ecf20Sopenharmony_ci unsigned long irqflags; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (gma_power_is_on(dev)) { 3188c2ecf20Sopenharmony_ci PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); 3198c2ecf20Sopenharmony_ci PSB_WVDC32(0x00000000, PSB_INT_MASK_R); 3208c2ecf20Sopenharmony_ci PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); 3218c2ecf20Sopenharmony_ci PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE); 3228c2ecf20Sopenharmony_ci PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci if (dev->vblank[0].enabled) 3258c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG; 3268c2ecf20Sopenharmony_ci if (dev->vblank[1].enabled) 3278c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* FIXME: Handle Medfield irq mask 3308c2ecf20Sopenharmony_ci if (dev->vblank[1].enabled) 3318c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _MDFLD_PIPEB_EVENT_FLAG; 3328c2ecf20Sopenharmony_ci if (dev->vblank[2].enabled) 3338c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _MDFLD_PIPEC_EVENT_FLAG; 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Revisit this area - want per device masks ? */ 3378c2ecf20Sopenharmony_ci if (dev_priv->ops->hotplug) 3388c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _PSB_IRQ_DISP_HOTSYNC; 3398c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _PSB_IRQ_ASLE | _PSB_IRQ_SGX_FLAG; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* This register is safe even if display island is off */ 3428c2ecf20Sopenharmony_ci PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); 3438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciint psb_irq_postinstall(struct drm_device *dev) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 3498c2ecf20Sopenharmony_ci unsigned long irqflags; 3508c2ecf20Sopenharmony_ci unsigned int i; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Enable 2D and MMU fault interrupts */ 3558c2ecf20Sopenharmony_ci PSB_WSGX32(_PSB_CE2_BIF_REQUESTER_FAULT, PSB_CR_EVENT_HOST_ENABLE2); 3568c2ecf20Sopenharmony_ci PSB_WSGX32(_PSB_CE_TWOD_COMPLETE, PSB_CR_EVENT_HOST_ENABLE); 3578c2ecf20Sopenharmony_ci PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); /* Post */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* This register is safe even if display island is off */ 3608c2ecf20Sopenharmony_ci PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); 3618c2ecf20Sopenharmony_ci PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_crtcs; ++i) { 3648c2ecf20Sopenharmony_ci if (dev->vblank[i].enabled) 3658c2ecf20Sopenharmony_ci psb_enable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); 3668c2ecf20Sopenharmony_ci else 3678c2ecf20Sopenharmony_ci psb_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (dev_priv->ops->hotplug_enable) 3718c2ecf20Sopenharmony_ci dev_priv->ops->hotplug_enable(dev, true); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_civoid psb_irq_uninstall(struct drm_device *dev) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 3808c2ecf20Sopenharmony_ci unsigned long irqflags; 3818c2ecf20Sopenharmony_ci unsigned int i; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (dev_priv->ops->hotplug_enable) 3868c2ecf20Sopenharmony_ci dev_priv->ops->hotplug_enable(dev, false); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_crtcs; ++i) { 3918c2ecf20Sopenharmony_ci if (dev->vblank[i].enabled) 3928c2ecf20Sopenharmony_ci psb_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG | 3968c2ecf20Sopenharmony_ci _PSB_IRQ_MSVDX_FLAG | 3978c2ecf20Sopenharmony_ci _LNC_IRQ_TOPAZ_FLAG; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* These two registers are safe even if display island is off */ 4008c2ecf20Sopenharmony_ci PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); 4018c2ecf20Sopenharmony_ci PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci wmb(); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* This register is safe even if display island is off */ 4068c2ecf20Sopenharmony_ci PSB_WVDC32(PSB_RVDC32(PSB_INT_IDENTITY_R), PSB_INT_IDENTITY_R); 4078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_civoid psb_irq_turn_on_dpst(struct drm_device *dev) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 4138c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 4148c2ecf20Sopenharmony_ci u32 hist_reg; 4158c2ecf20Sopenharmony_ci u32 pwm_reg; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (gma_power_begin(dev, false)) { 4188c2ecf20Sopenharmony_ci PSB_WVDC32(1 << 31, HISTOGRAM_LOGIC_CONTROL); 4198c2ecf20Sopenharmony_ci hist_reg = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); 4208c2ecf20Sopenharmony_ci PSB_WVDC32(1 << 31, HISTOGRAM_INT_CONTROL); 4218c2ecf20Sopenharmony_ci hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci PSB_WVDC32(0x80010100, PWM_CONTROL_LOGIC); 4248c2ecf20Sopenharmony_ci pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); 4258c2ecf20Sopenharmony_ci PSB_WVDC32(pwm_reg | PWM_PHASEIN_ENABLE 4268c2ecf20Sopenharmony_ci | PWM_PHASEIN_INT_ENABLE, 4278c2ecf20Sopenharmony_ci PWM_CONTROL_LOGIC); 4288c2ecf20Sopenharmony_ci pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci psb_enable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); 4338c2ecf20Sopenharmony_ci PSB_WVDC32(hist_reg | HISTOGRAM_INT_CTRL_CLEAR, 4348c2ecf20Sopenharmony_ci HISTOGRAM_INT_CONTROL); 4358c2ecf20Sopenharmony_ci pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); 4368c2ecf20Sopenharmony_ci PSB_WVDC32(pwm_reg | 0x80010100 | PWM_PHASEIN_ENABLE, 4378c2ecf20Sopenharmony_ci PWM_CONTROL_LOGIC); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci gma_power_end(dev); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ciint psb_irq_enable_dpst(struct drm_device *dev) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 4468c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 4478c2ecf20Sopenharmony_ci unsigned long irqflags; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* enable DPST */ 4528c2ecf20Sopenharmony_ci mid_enable_pipe_event(dev_priv, 0); 4538c2ecf20Sopenharmony_ci psb_irq_turn_on_dpst(dev); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_civoid psb_irq_turn_off_dpst(struct drm_device *dev) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 4628c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 4638c2ecf20Sopenharmony_ci u32 pwm_reg; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (gma_power_begin(dev, false)) { 4668c2ecf20Sopenharmony_ci PSB_WVDC32(0x00000000, HISTOGRAM_INT_CONTROL); 4678c2ecf20Sopenharmony_ci PSB_RVDC32(HISTOGRAM_INT_CONTROL); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci psb_disable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); 4728c2ecf20Sopenharmony_ci PSB_WVDC32(pwm_reg & ~PWM_PHASEIN_INT_ENABLE, 4738c2ecf20Sopenharmony_ci PWM_CONTROL_LOGIC); 4748c2ecf20Sopenharmony_ci pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci gma_power_end(dev); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciint psb_irq_disable_dpst(struct drm_device *dev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 4838c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 4848c2ecf20Sopenharmony_ci unsigned long irqflags; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mid_disable_pipe_event(dev_priv, 0); 4898c2ecf20Sopenharmony_ci psb_irq_turn_off_dpst(dev); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/* 4978c2ecf20Sopenharmony_ci * It is used to enable VBLANK interrupt 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ciint psb_enable_vblank(struct drm_crtc *crtc) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 5028c2ecf20Sopenharmony_ci unsigned int pipe = crtc->index; 5038c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 5048c2ecf20Sopenharmony_ci unsigned long irqflags; 5058c2ecf20Sopenharmony_ci uint32_t reg_val = 0; 5068c2ecf20Sopenharmony_ci uint32_t pipeconf_reg = mid_pipeconf(pipe); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Medfield is different - we should perhaps extract out vblank 5098c2ecf20Sopenharmony_ci and blacklight etc ops */ 5108c2ecf20Sopenharmony_ci if (IS_MFLD(dev)) 5118c2ecf20Sopenharmony_ci return mdfld_enable_te(dev, pipe); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (gma_power_begin(dev, false)) { 5148c2ecf20Sopenharmony_ci reg_val = REG_READ(pipeconf_reg); 5158c2ecf20Sopenharmony_ci gma_power_end(dev); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!(reg_val & PIPEACONF_ENABLE)) 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (pipe == 0) 5248c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG; 5258c2ecf20Sopenharmony_ci else if (pipe == 1) 5268c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); 5298c2ecf20Sopenharmony_ci PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); 5308c2ecf20Sopenharmony_ci psb_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci/* 5388c2ecf20Sopenharmony_ci * It is used to disable VBLANK interrupt 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_civoid psb_disable_vblank(struct drm_crtc *crtc) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 5438c2ecf20Sopenharmony_ci unsigned int pipe = crtc->index; 5448c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 5458c2ecf20Sopenharmony_ci unsigned long irqflags; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (IS_MFLD(dev)) 5488c2ecf20Sopenharmony_ci mdfld_disable_te(dev, pipe); 5498c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (pipe == 0) 5528c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEA_FLAG; 5538c2ecf20Sopenharmony_ci else if (pipe == 1) 5548c2ecf20Sopenharmony_ci dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEB_FLAG; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); 5578c2ecf20Sopenharmony_ci PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); 5588c2ecf20Sopenharmony_ci psb_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* 5648c2ecf20Sopenharmony_ci * It is used to enable TE interrupt 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ciint mdfld_enable_te(struct drm_device *dev, int pipe) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 5698c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 5708c2ecf20Sopenharmony_ci unsigned long irqflags; 5718c2ecf20Sopenharmony_ci uint32_t reg_val = 0; 5728c2ecf20Sopenharmony_ci uint32_t pipeconf_reg = mid_pipeconf(pipe); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (gma_power_begin(dev, false)) { 5758c2ecf20Sopenharmony_ci reg_val = REG_READ(pipeconf_reg); 5768c2ecf20Sopenharmony_ci gma_power_end(dev); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!(reg_val & PIPEACONF_ENABLE)) 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci mid_enable_pipe_event(dev_priv, pipe); 5858c2ecf20Sopenharmony_ci psb_enable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/* 5938c2ecf20Sopenharmony_ci * It is used to disable TE interrupt 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_civoid mdfld_disable_te(struct drm_device *dev, int pipe) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = 5988c2ecf20Sopenharmony_ci (struct drm_psb_private *) dev->dev_private; 5998c2ecf20Sopenharmony_ci unsigned long irqflags; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (!dev_priv->dsr_enable) 6028c2ecf20Sopenharmony_ci return; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci mid_disable_pipe_event(dev_priv, pipe); 6078c2ecf20Sopenharmony_ci psb_disable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/* Called from drm generic code, passed a 'crtc', which 6138c2ecf20Sopenharmony_ci * we use as a pipe index 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ciu32 psb_get_vblank_counter(struct drm_crtc *crtc) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 6188c2ecf20Sopenharmony_ci unsigned int pipe = crtc->index; 6198c2ecf20Sopenharmony_ci uint32_t high_frame = PIPEAFRAMEHIGH; 6208c2ecf20Sopenharmony_ci uint32_t low_frame = PIPEAFRAMEPIXEL; 6218c2ecf20Sopenharmony_ci uint32_t pipeconf_reg = PIPEACONF; 6228c2ecf20Sopenharmony_ci uint32_t reg_val = 0; 6238c2ecf20Sopenharmony_ci uint32_t high1 = 0, high2 = 0, low = 0, count = 0; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci switch (pipe) { 6268c2ecf20Sopenharmony_ci case 0: 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci case 1: 6298c2ecf20Sopenharmony_ci high_frame = PIPEBFRAMEHIGH; 6308c2ecf20Sopenharmony_ci low_frame = PIPEBFRAMEPIXEL; 6318c2ecf20Sopenharmony_ci pipeconf_reg = PIPEBCONF; 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci case 2: 6348c2ecf20Sopenharmony_ci high_frame = PIPECFRAMEHIGH; 6358c2ecf20Sopenharmony_ci low_frame = PIPECFRAMEPIXEL; 6368c2ecf20Sopenharmony_ci pipeconf_reg = PIPECCONF; 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci default: 6398c2ecf20Sopenharmony_ci dev_err(dev->dev, "%s, invalid pipe.\n", __func__); 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (!gma_power_begin(dev, false)) 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci reg_val = REG_READ(pipeconf_reg); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (!(reg_val & PIPEACONF_ENABLE)) { 6498c2ecf20Sopenharmony_ci dev_err(dev->dev, "trying to get vblank count for disabled pipe %u\n", 6508c2ecf20Sopenharmony_ci pipe); 6518c2ecf20Sopenharmony_ci goto psb_get_vblank_counter_exit; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * High & low register fields aren't synchronized, so make sure 6568c2ecf20Sopenharmony_ci * we get a low value that's stable across two reads of the high 6578c2ecf20Sopenharmony_ci * register. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci do { 6608c2ecf20Sopenharmony_ci high1 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> 6618c2ecf20Sopenharmony_ci PIPE_FRAME_HIGH_SHIFT); 6628c2ecf20Sopenharmony_ci low = ((REG_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> 6638c2ecf20Sopenharmony_ci PIPE_FRAME_LOW_SHIFT); 6648c2ecf20Sopenharmony_ci high2 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> 6658c2ecf20Sopenharmony_ci PIPE_FRAME_HIGH_SHIFT); 6668c2ecf20Sopenharmony_ci } while (high1 != high2); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci count = (high1 << 8) | low; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cipsb_get_vblank_counter_exit: 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci gma_power_end(dev); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return count; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 677