aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/cdv/drv/psb_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/cdv/drv/psb_irq.c')
-rw-r--r--drivers/staging/cdv/drv/psb_irq.c681
1 files changed, 681 insertions, 0 deletions
diff --git a/drivers/staging/cdv/drv/psb_irq.c b/drivers/staging/cdv/drv/psb_irq.c
new file mode 100644
index 000000000000..84af1566f463
--- /dev/null
+++ b/drivers/staging/cdv/drv/psb_irq.c
@@ -0,0 +1,681 @@
+/**************************************************************************
+ * Copyright (c) 2011, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+ * develop this driver.
+ *
+ **************************************************************************/
+/*
+ */
+
+#include <drm/drmP.h>
+#include "psb_drv.h"
+#include "psb_reg.h"
+#include "psb_msvdx.h"
+#include "psb_intel_reg.h"
+#include "psb_powermgmt.h"
+
+
+/*
+ * inline functions
+ */
+static inline u32
+psb_pipestat(int pipe)
+{
+ if (pipe == 0)
+ return PIPEASTAT;
+ if (pipe == 1)
+ return PIPEBSTAT;
+ BUG();
+}
+
+static inline u32
+psb_pipe_event(int pipe)
+{
+ if (pipe == 0)
+ return PSB_IRQ_PIPEA_EVENT;
+ if (pipe == 1)
+ return PSB_IRQ_PIPEB_EVENT;
+ BUG();
+}
+
+static inline u32
+psb_pipeconf(int pipe)
+{
+ if (pipe == 0)
+ return PIPEACONF;
+ if (pipe == 1)
+ return PIPEBCONF;
+ BUG();
+}
+
+void
+psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask)
+{
+ if ((dev_priv->pipestat[pipe] & mask) != mask) {
+ u32 reg = psb_pipestat(pipe);
+ u32 writeVal = PSB_RVDC32(reg);
+
+ dev_priv->pipestat[pipe] |= mask;
+ /* Enable the interrupt, clear any pending status */
+ writeVal |= (mask | (mask >> 16));
+ PSB_WVDC32(writeVal, reg);
+ (void) PSB_RVDC32(reg);
+ }
+}
+
+void
+psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask)
+{
+ if ((dev_priv->pipestat[pipe] & mask) != 0) {
+ u32 reg = psb_pipestat(pipe);
+ u32 writeVal = PSB_RVDC32(reg);
+
+ dev_priv->pipestat[pipe] &= ~mask;
+ writeVal &= ~mask;
+ PSB_WVDC32(writeVal, reg);
+ (void) PSB_RVDC32(reg);
+ }
+}
+
+void
+mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
+{
+ u32 pipe_event = psb_pipe_event(pipe);
+ dev_priv->vdc_irq_mask |= pipe_event;
+ PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
+ PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
+}
+
+void
+mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
+{
+ if (dev_priv->pipestat[pipe] == 0) {
+ u32 pipe_event = psb_pipe_event(pipe);
+ dev_priv->vdc_irq_mask &= ~pipe_event;
+ PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
+ PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
+ }
+}
+
+/**
+ * Display controller interrupt handler for vsync/vblank.
+ *
+ */
+static void mid_vblank_handler(struct drm_device *dev, uint32_t pipe)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ /**
+ * Using TE interrupt for B0 + command mode panels
+ */
+
+ drm_handle_vblank(dev, pipe);
+
+ if( dev_priv->psb_vsync_handler != NULL)
+ (*dev_priv->psb_vsync_handler)(dev,pipe);
+}
+
+/**
+ * Display controller interrupt handler for pipe event.
+ *
+ */
+#define WAIT_STATUS_CLEAR_LOOP_COUNT 0xffff
+static void mid_pipe_event_handler(struct drm_device *dev, uint32_t pipe)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+
+ uint32_t pipe_stat_val = 0;
+ uint32_t pipe_stat_reg = psb_pipestat(pipe);
+ uint32_t pipe_enable = dev_priv->pipestat[pipe];
+ uint32_t pipe_status = dev_priv->pipestat[pipe] >> 16;
+ uint32_t i = 0, temp;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irq_flags);
+
+ pipe_stat_val = PSB_RVDC32(pipe_stat_reg);
+ pipe_stat_val &= pipe_enable | pipe_status;
+ pipe_stat_val &= pipe_stat_val >> 16;
+
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irq_flags);
+ temp = PSB_RVDC32(pipe_stat_reg);
+ temp &=~(0xFFFF);
+ if (temp & PIPE_FIFO_UNDERRUN) {
+ DRM_DEBUG_KMS("Fifo underrun on pipe %d\n", pipe);
+ PSB_WVDC32(temp, pipe_stat_reg);
+ PSB_RVDC32(pipe_stat_reg);
+ }
+ /* clear the 2nd level interrupt status bits */
+ /**
+ * FIXME: shouldn't use while loop here. However, the interrupt
+ * status 'sticky' bits cannot be cleared by setting '1' to that
+ * bit once...
+ */
+ for (i = 0; i < WAIT_STATUS_CLEAR_LOOP_COUNT; i ++) {
+ PSB_WVDC32(PSB_RVDC32(pipe_stat_reg), pipe_stat_reg);
+ (void) PSB_RVDC32(pipe_stat_reg);
+
+ if ((PSB_RVDC32(pipe_stat_reg) & pipe_status) == 0)
+ break;
+ }
+
+ if (i == WAIT_STATUS_CLEAR_LOOP_COUNT)
+ DRM_ERROR("%s, can't clear the status bits in pipe_stat_reg, its value = 0x%x. \n",
+ __FUNCTION__, PSB_RVDC32(pipe_stat_reg));
+
+ if ((pipe_stat_val & PIPE_DPST_EVENT_STATUS) &&
+ (dev_priv->psb_dpst_state != NULL)) {
+ uint32_t pwm_reg = 0;
+ uint32_t hist_reg = 0;
+ u32 irqCtrl = 0;
+ struct dpst_guardband guardband_reg;
+ struct dpst_ie_histogram_control ie_hist_cont_reg;
+
+ hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL);
+
+ /* Determine if this is histogram or pwm interrupt */
+ if(hist_reg & HISTOGRAM_INT_CTRL_CLEAR) {
+ /* Notify UM of histogram interrupt */
+ psb_dpst_notify_change_um(DPST_EVENT_HIST_INTERRUPT,
+ dev_priv->psb_dpst_state);
+
+ /* disable dpst interrupts */
+ guardband_reg.data = PSB_RVDC32(HISTOGRAM_INT_CONTROL);
+ guardband_reg.interrupt_enable = 0;
+ guardband_reg.interrupt_status = 1;
+ PSB_WVDC32(guardband_reg.data, HISTOGRAM_INT_CONTROL);
+
+ ie_hist_cont_reg.data = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL);
+ ie_hist_cont_reg.ie_histogram_enable = 0;
+ PSB_WVDC32(ie_hist_cont_reg.data, HISTOGRAM_LOGIC_CONTROL);
+
+ irqCtrl = PSB_RVDC32(PIPEASTAT);
+ irqCtrl &= ~PIPE_DPST_EVENT_ENABLE;
+ PSB_WVDC32(irqCtrl, PIPEASTAT);
+ }
+ pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
+ if((pwm_reg & PWM_PHASEIN_INT_ENABLE) &&
+ !(pwm_reg & PWM_PHASEIN_ENABLE)) {
+ /* Notify UM of the phase complete */
+ psb_dpst_notify_change_um(DPST_EVENT_PHASE_COMPLETE,
+ dev_priv->psb_dpst_state);
+
+ /* Temporarily get phase mngr ready to generate
+ * another interrupt until this can be moved to
+ * user mode */
+ /* PSB_WVDC32(pwm_reg | 0x80010100 | PWM_PHASEIN_ENABLE,
+ PWM_CONTROL_LOGIC); */
+ }
+ }
+
+ if (pipe_stat_val & PIPE_VBLANK_STATUS) {
+ mid_vblank_handler(dev, pipe);
+ }
+}
+
+/**
+ * Display controller interrupt handler.
+ */
+static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat)
+{
+ if (vdc_stat & PSB_IRQ_ASLE)
+ psb_intel_opregion_asle_intr(dev);
+
+ if (vdc_stat & PSB_IRQ_PIPEA_EVENT)
+ mid_pipe_event_handler(dev, 0);
+
+ if (vdc_stat & PSB_IRQ_PIPEB_EVENT)
+ mid_pipe_event_handler(dev, 1);
+}
+
+static void psb_hotplug_work_func(struct work_struct *work)
+{
+ struct drm_psb_private *dev_priv = container_of(work, struct drm_psb_private,
+ hotplug_work);
+ struct drm_device *dev = dev_priv->dev;
+
+ /* Just fire off a uevent and let userspace tell us what to do */
+ drm_helper_hpd_irq_event(dev);
+}
+
+irqreturn_t psb_irq_handler(DRM_IRQ_ARGS)
+{
+ struct drm_device *dev = (struct drm_device *) arg;
+ struct drm_psb_private *dev_priv = (struct drm_psb_private *) dev->dev_private;
+ uint32_t vdc_stat, dsp_int = 0, sgx_int = 0, msvdx_int = 0, hotplug_int = 0;
+ int handled = 0;
+ unsigned long irq_flags;
+
+/* PSB_DEBUG_ENTRY("\n"); */
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irq_flags);
+
+ vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R);
+
+ if (vdc_stat & _PSB_DISP_ALL_IRQ_FLAG) {
+ PSB_DEBUG_IRQ("Got DISP interrupt\n");
+ dsp_int = 1;
+ }
+
+ if (vdc_stat & _PSB_IRQ_SGX_FLAG) {
+ PSB_DEBUG_IRQ("Got SGX interrupt\n");
+ sgx_int = 1;
+ }
+
+ if (vdc_stat & _PSB_IRQ_MSVDX_FLAG) {
+ PSB_DEBUG_IRQ("Got MSVDX interrupt\n");
+ msvdx_int = 1;
+ }
+
+ if (vdc_stat & PSB_IRQ_DISP_HOTSYNC) {
+ PSB_DEBUG_IRQ("Got hotplug interrupt\n");
+ hotplug_int = 1;
+ }
+
+ vdc_stat &= dev_priv->vdc_irq_mask;
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irq_flags);
+
+ if (dsp_int) {
+ psb_vdc_interrupt(dev, vdc_stat);
+ handled = 1;
+ }
+
+ if (msvdx_int && ospm_power_is_hw_on(OSPM_VIDEO_DEC_ISLAND)) {
+ psb_msvdx_interrupt(dev);
+ handled = 1;
+ }
+
+ if (sgx_int) {
+ if (SYSPVRServiceSGXInterrupt(dev) != 0)
+ handled = 1;
+ }
+
+ if (hotplug_int) {
+ /* use system wq for now*/
+ schedule_work(&dev_priv->hotplug_work);
+ REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT));
+ handled = 1;
+ }
+
+ PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R);
+ (void) PSB_RVDC32(PSB_INT_IDENTITY_R);
+ DRM_READMEMORYBARRIER();
+
+ if (!handled)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+void psb_irq_preinstall(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = (struct drm_psb_private *) dev->dev_private;
+ psb_irq_preinstall_islands(dev, OSPM_ALL_ISLANDS);
+
+ INIT_WORK(&dev_priv->hotplug_work, psb_hotplug_work_func);
+
+ REG_WRITE(PORT_HOTPLUG_EN, 0);
+ REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT));
+}
+
+/**
+ * FIXME: should I remove display irq enable here??
+ */
+void psb_irq_preinstall_islands(struct drm_device *dev, int hw_islands)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+
+ /* PSB_DEBUG_ENTRY("\n"); */
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ if (dev->vblank_enabled[0])
+ dev_priv->vdc_irq_mask |= PSB_IRQ_PIPEA_EVENT;
+ if (dev->vblank_enabled[1])
+ dev_priv->vdc_irq_mask |= PSB_IRQ_PIPEB_EVENT;
+
+ if (hw_islands & OSPM_GRAPHICS_ISLAND) {
+ dev_priv->vdc_irq_mask |= _PSB_IRQ_SGX_FLAG;
+ }
+
+ if (hw_islands & OSPM_VIDEO_DEC_ISLAND)
+ if (ospm_power_is_hw_on(OSPM_VIDEO_DEC_ISLAND))
+ dev_priv->vdc_irq_mask |= _PSB_IRQ_MSVDX_FLAG;
+
+ /* display hotplug */
+ dev_priv->vdc_irq_mask |= PSB_IRQ_DISP_HOTSYNC;
+
+ /* asle interrupt */
+ dev_priv->vdc_irq_mask |= PSB_IRQ_ASLE;
+
+ /*This register is safe even if display island is off*/
+ /* unmask all */
+ PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+}
+
+int psb_irq_postinstall(struct drm_device *dev)
+{
+ return psb_irq_postinstall_islands(dev, OSPM_ALL_ISLANDS);
+}
+
+int psb_irq_postinstall_islands(struct drm_device *dev, int hw_islands)
+{
+
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+ u32 temp;
+
+ /* PSB_DEBUG_ENTRY("\n"); */
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ /*This register is safe even if display island is off*/
+ PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
+
+ if (true/*powermgmt_is_hw_on(dev->pdev, PSB_DISPLAY_ISLAND)*/) {
+ if (dev->vblank_enabled[0])
+ psb_enable_pipestat(dev_priv, 0,
+ PIPE_VBLANK_INTERRUPT_ENABLE);
+ else
+ psb_disable_pipestat(dev_priv, 0,
+ PIPE_VBLANK_INTERRUPT_ENABLE);
+
+ if (dev->vblank_enabled[1])
+ psb_enable_pipestat(dev_priv, 1,
+ PIPE_VBLANK_INTERRUPT_ENABLE);
+ else
+ psb_disable_pipestat(dev_priv, 1,
+ PIPE_VBLANK_INTERRUPT_ENABLE);
+ }
+
+ /* Just enable all display ports for now.
+ * This can be optimized to only enable ports that really exists.
+ */
+ temp = REG_READ(PORT_HOTPLUG_EN);
+ temp |= HDMIB_HOTPLUG_INT_EN;
+ temp |= HDMIC_HOTPLUG_INT_EN;
+ temp |= HDMID_HOTPLUG_INT_EN;
+ temp |= CRT_HOTPLUG_INT_EN;
+ REG_WRITE(PORT_HOTPLUG_EN, temp);
+
+ if (hw_islands & OSPM_VIDEO_DEC_ISLAND)
+ if (true/*powermgmt_is_hw_on(dev->pdev, PSB_VIDEO_DEC_ISLAND)*/)
+ psb_msvdx_enableirq(dev);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+
+ return 0;
+}
+
+void psb_irq_uninstall(struct drm_device *dev)
+{
+ psb_irq_uninstall_islands(dev, OSPM_ALL_ISLANDS);
+}
+
+void psb_irq_uninstall_islands(struct drm_device *dev, int hw_islands)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+
+ /* PSB_DEBUG_ENTRY("\n"); */
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ if (true/*powermgmt_is_hw_on(dev->pdev, PSB_DISPLAY_ISLAND)*/) {
+ if (dev->vblank_enabled[0])
+ psb_disable_pipestat(dev_priv, 0,
+ PIPE_VBLANK_INTERRUPT_ENABLE);
+
+ if (dev->vblank_enabled[1])
+ psb_disable_pipestat(dev_priv, 1,
+ PIPE_VBLANK_INTERRUPT_ENABLE);
+
+ }
+ dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG |
+ _PSB_IRQ_MSVDX_FLAG;
+
+ /*TODO: remove follwoing code*/
+ if (hw_islands & OSPM_GRAPHICS_ISLAND) {
+ dev_priv->vdc_irq_mask &= ~_PSB_IRQ_SGX_FLAG;
+ }
+
+ if ((hw_islands & OSPM_VIDEO_DEC_ISLAND))
+ dev_priv->vdc_irq_mask &= ~_PSB_IRQ_MSVDX_FLAG;
+
+ /*These two registers are safe even if display island is off*/
+ PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
+ PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
+
+ wmb();
+
+ /*This register is safe even if display island is off*/
+ PSB_WVDC32(PSB_RVDC32(PSB_INT_IDENTITY_R), PSB_INT_IDENTITY_R);
+
+ if (hw_islands & OSPM_VIDEO_DEC_ISLAND)
+ if (ospm_power_is_hw_on(OSPM_VIDEO_DEC_ISLAND))
+ psb_msvdx_disableirq(dev);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+}
+
+void psb_irq_turn_on_dpst(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ u32 hist_reg;
+ u32 pwm_reg;
+
+ PSB_WVDC32(BIT31, HISTOGRAM_LOGIC_CONTROL);
+ hist_reg = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL);
+ PSB_WVDC32(BIT31, HISTOGRAM_INT_CONTROL);
+ hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL);
+
+ PSB_WVDC32(0x80010100, PWM_CONTROL_LOGIC);
+ pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
+ PSB_WVDC32(pwm_reg | PWM_PHASEIN_ENABLE | PWM_PHASEIN_INT_ENABLE,
+ PWM_CONTROL_LOGIC);
+ pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
+
+ /* pipe B */
+ psb_enable_pipestat(dev_priv, 1, PIPE_DPST_EVENT_ENABLE);
+
+ hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL);
+ PSB_WVDC32(hist_reg | HISTOGRAM_INT_CTRL_CLEAR,HISTOGRAM_INT_CONTROL);
+ pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
+ PSB_WVDC32(pwm_reg | 0x80010100 | PWM_PHASEIN_ENABLE, PWM_CONTROL_LOGIC);
+
+}
+
+int psb_irq_enable_dpst(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+
+ PSB_DEBUG_ENTRY("\n");
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ /* enable DPST */
+ /* DPST always on LVDS pipe B */
+ mid_enable_pipe_event(dev_priv, 1);
+ psb_irq_turn_on_dpst(dev);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+ return 0;
+}
+
+void psb_irq_turn_off_dpst(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ u32 pwm_reg;
+
+ PSB_WVDC32(0x00000000, HISTOGRAM_INT_CONTROL);
+
+ /* on pipe B */
+ psb_disable_pipestat(dev_priv, 1, PIPE_DPST_EVENT_ENABLE);
+
+ pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
+ PSB_WVDC32(pwm_reg & !(PWM_PHASEIN_INT_ENABLE), PWM_CONTROL_LOGIC);
+ pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
+}
+
+int psb_irq_disable_dpst(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+
+ PSB_DEBUG_ENTRY("\n");
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ /* on pipe B */
+ mid_disable_pipe_event(dev_priv, 1);
+ psb_irq_turn_off_dpst(dev);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+
+ return 0;
+}
+
+/*
+ * It is used to enable VBLANK interrupt
+ */
+int psb_enable_vblank(struct drm_device *dev, int pipe)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+ uint32_t reg_val = 0;
+ uint32_t pipeconf_reg = psb_pipeconf(pipe);
+
+ PSB_DEBUG_ENTRY("\n");
+
+
+ reg_val = REG_READ(pipeconf_reg);
+
+ if (!(reg_val & PIPEACONF_ENABLE))
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ mid_enable_pipe_event(dev_priv, pipe);
+ psb_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+
+ return 0;
+}
+
+/*
+ * It is used to disable VBLANK interrupt
+ */
+void psb_disable_vblank(struct drm_device *dev, int pipe)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+
+ PSB_DEBUG_ENTRY("\n");
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ mid_disable_pipe_event(dev_priv, pipe);
+ psb_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+}
+
+/* Called from drm generic code, passed a 'crtc', which
+ * we use as a pipe index
+ */
+u32 psb_get_vblank_counter(struct drm_device *dev, int pipe)
+{
+ uint32_t high_frame = PIPEAFRAMEHIGH;
+ uint32_t low_frame = PIPEAFRAMEPIXEL;
+ uint32_t pipeconf_reg = PIPEACONF;
+ uint32_t reg_val = 0;
+ uint32_t high1 = 0, high2 = 0, low = 0, count = 0;
+
+ switch (pipe) {
+ case 0:
+ break;
+ case 1:
+ high_frame = PIPEBFRAMEHIGH;
+ low_frame = PIPEBFRAMEPIXEL;
+ pipeconf_reg = PIPEBCONF;
+ break;
+ case 2:
+ high_frame = PIPECFRAMEHIGH;
+ low_frame = PIPECFRAMEPIXEL;
+ pipeconf_reg = PIPECCONF;
+ break;
+ default:
+ DRM_ERROR("%s, invalded pipe. \n", __FUNCTION__);
+ return 0;
+ }
+
+ reg_val = REG_READ(pipeconf_reg);
+
+ if (!(reg_val & PIPEACONF_ENABLE)) {
+ DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+ goto psb_get_vblank_counter_exit;
+ }
+
+ /*
+ * High & low register fields aren't synchronized, so make sure
+ * we get a low value that's stable across two reads of the high
+ * register.
+ */
+ do {
+ high1 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
+ PIPE_FRAME_HIGH_SHIFT);
+ low = ((REG_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
+ PIPE_FRAME_LOW_SHIFT);
+ high2 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
+ PIPE_FRAME_HIGH_SHIFT);
+ } while (high1 != high2);
+
+ count = (high1 << 8) | low;
+
+psb_get_vblank_counter_exit:
+
+ return count;
+}
+
+/*
+ * psb_intel_enable_asle - enable ASLE interrupt for OpRegion
+ */
+void psb_intel_enable_asle(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = (struct drm_psb_private *) dev->dev_private;
+
+ psb_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
+ psb_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
+}
+