Because the gma_irq_install() is call after psb_gem_mm_init() function, when psb_gem_mm_init() fails, the interrupt line haven't been allocated. Yet the gma_irq_uninstall() is called in the psb_driver_unload() function without checking if checking the irq is registered or not. The calltrace is appended as following: [ 20.539253] ioremap memtype_reserve failed -16 [ 20.543895] gma500 0000:00:02.0: Failure to map stolen base. [ 20.565049] ------------[ cut here ]------------ [ 20.565066] Trying to free already-free IRQ 16 [ 20.565087] WARNING: CPU: 1 PID: 381 at kernel/irq/manage.c:1893 free_irq+0x209/0x370 [ 20.565316] CPU: 1 PID: 381 Comm: systemd-udevd Tainted: G C 6.5.0-rc1+ #368 [ 20.565329] Hardware name: To Be Filled By O.E.M. To Be Filled By O.E.M./IMB-140D Plus, BIOS P1.10 11/18/2013 [ 20.565338] RIP: 0010:free_irq+0x209/0x370 [ 20.565357] Code: 41 5d 41 5e 41 5f 5d 31 d2 89 d1 89 d6 89 d7 41 89 d1 c3 cc cc cc cc 8b 75 d0 48 c7 c7 e0 77 12 9f 4c 89 4d c8 e8 57 fe f4 ff <0f> 0b 48 8b 75 c8 4c 89 f7 e8 29 f3 f1 00 49 8b 47 40 48 8b 40 78 [ 20.565369] RSP: 0018:ffffae3b40733808 EFLAGS: 00010046 [ 20.565382] RAX: 0000000000000000 RBX: ffff9f8082bfe000 RCX: 0000000000000000 [ 20.565390] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 20.565397] RBP: ffffae3b40733840 R08: 0000000000000000 R09: 0000000000000000 [ 20.565405] R10: 0000000000000000 R11: 0000000000000000 R12: ffff9f80871c3100 [ 20.565413] R13: ffff9f80835d3360 R14: ffff9f80835d32a4 R15: ffff9f80835d3200 [ 20.565424] FS: 00007f13d36458c0(0000) GS:ffff9f8138880000(0000) knlGS:0000000000000000 [ 20.565434] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 20.565441] CR2: 00007f0d046f3f20 CR3: 0000000006c8c000 CR4: 00000000000006e0 [ 20.565450] Call Trace: [ 20.565458] <TASK> [ 20.565470] ? show_regs+0x72/0x90 [ 20.565488] ? free_irq+0x209/0x370 [ 20.565504] ? __warn+0x8d/0x160 [ 20.565520] ? free_irq+0x209/0x370 [ 20.565536] ? report_bug+0x1bb/0x1d0 [ 20.565555] ? handle_bug+0x46/0x90 [ 20.565572] ? exc_invalid_op+0x19/0x80 [ 20.565587] ? asm_exc_invalid_op+0x1b/0x20 [ 20.565607] ? free_irq+0x209/0x370 [ 20.565625] ? free_irq+0x209/0x370 [ 20.565644] gma_irq_uninstall+0x15b/0x1e0 [gma500_gfx] [ 20.565728] psb_driver_unload+0x27/0x190 [gma500_gfx] [ 20.565800] psb_pci_probe+0x5d2/0x790 [gma500_gfx] [ 20.565873] local_pci_probe+0x48/0xb0 [ 20.565892] pci_device_probe+0xc8/0x280 [ 20.565912] really_probe+0x1d2/0x440 [ 20.565929] __driver_probe_device+0x8a/0x190 [ 20.565944] driver_probe_device+0x23/0xd0 [ 20.565957] __driver_attach+0x10f/0x220 [ 20.565971] ? __pfx___driver_attach+0x10/0x10 [ 20.565984] bus_for_each_dev+0x7a/0xe0 [ 20.566002] driver_attach+0x1e/0x30 [ 20.566014] bus_add_driver+0x127/0x240 [ 20.566029] driver_register+0x64/0x140 [ 20.566043] ? __pfx_psb_init+0x10/0x10 [gma500_gfx] [ 20.566111] __pci_register_driver+0x68/0x80 [ 20.566128] psb_init+0x2c/0xff0 [gma500_gfx] [ 20.566194] do_one_initcall+0x46/0x330 [ 20.566214] ? kmalloc_trace+0x2a/0xb0 [ 20.566233] do_init_module+0x6a/0x270 [ 20.566250] load_module+0x207f/0x23a0 [ 20.566278] init_module_from_file+0x9c/0xf0 [ 20.566293] ? init_module_from_file+0x9c/0xf0 [ 20.566315] idempotent_init_module+0x184/0x240 [ 20.566335] __x64_sys_finit_module+0x64/0xd0 [ 20.566352] do_syscall_64+0x59/0x90 [ 20.566366] ? ksys_mmap_pgoff+0x123/0x270 [ 20.566378] ? __secure_computing+0x9b/0x110 [ 20.566392] ? exit_to_user_mode_prepare+0x39/0x190 [ 20.566406] ? syscall_exit_to_user_mode+0x2a/0x50 [ 20.566420] ? do_syscall_64+0x69/0x90 [ 20.566433] ? do_syscall_64+0x69/0x90 [ 20.566445] ? do_syscall_64+0x69/0x90 [ 20.566458] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 [ 20.566472] RIP: 0033:0x7f13d351ea3d [ 20.566485] Code: 5b 41 5c c3 66 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d c3 a3 0f 00 f7 d8 64 89 01 48 [ 20.566496] RSP: 002b:00007ffe566c1fd8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 20.566510] RAX: ffffffffffffffda RBX: 000055e66806eec0 RCX: 00007f13d351ea3d [ 20.566519] RDX: 0000000000000000 RSI: 00007f13d36d9441 RDI: 0000000000000010 [ 20.566527] RBP: 0000000000020000 R08: 0000000000000000 R09: 0000000000000002 [ 20.566535] R10: 0000000000000010 R11: 0000000000000246 R12: 00007f13d36d9441 [ 20.566543] R13: 000055e6681108c0 R14: 000055e66805ba70 R15: 000055e66819a9c0 [ 20.566559] </TASK> [ 20.566566] ---[ end trace 0000000000000000 ]--- Signed-off-by: Sui Jingfeng <suijingfeng@loongson.cn> Signed-off-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/20230727185855.713318-1-suijingfeng@loongson.cn
494 lines
13 KiB
C
494 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/**************************************************************************
|
|
* Copyright (c) 2007, Intel Corporation.
|
|
* All Rights Reserved.
|
|
*
|
|
* Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
|
|
* develop this driver.
|
|
*
|
|
**************************************************************************/
|
|
|
|
#include <drm/drm_drv.h>
|
|
#include <drm/drm_vblank.h>
|
|
|
|
#include "power.h"
|
|
#include "psb_drv.h"
|
|
#include "psb_intel_reg.h"
|
|
#include "psb_irq.h"
|
|
#include "psb_reg.h"
|
|
|
|
/*
|
|
* inline functions
|
|
*/
|
|
|
|
static inline u32 gma_pipestat(int pipe)
|
|
{
|
|
if (pipe == 0)
|
|
return PIPEASTAT;
|
|
if (pipe == 1)
|
|
return PIPEBSTAT;
|
|
if (pipe == 2)
|
|
return PIPECSTAT;
|
|
BUG();
|
|
}
|
|
|
|
static inline u32 gma_pipeconf(int pipe)
|
|
{
|
|
if (pipe == 0)
|
|
return PIPEACONF;
|
|
if (pipe == 1)
|
|
return PIPEBCONF;
|
|
if (pipe == 2)
|
|
return PIPECCONF;
|
|
BUG();
|
|
}
|
|
|
|
void gma_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask)
|
|
{
|
|
if ((dev_priv->pipestat[pipe] & mask) != mask) {
|
|
u32 reg = gma_pipestat(pipe);
|
|
dev_priv->pipestat[pipe] |= mask;
|
|
/* Enable the interrupt, clear any pending status */
|
|
if (gma_power_begin(&dev_priv->dev, false)) {
|
|
u32 writeVal = PSB_RVDC32(reg);
|
|
writeVal |= (mask | (mask >> 16));
|
|
PSB_WVDC32(writeVal, reg);
|
|
(void) PSB_RVDC32(reg);
|
|
gma_power_end(&dev_priv->dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
void gma_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask)
|
|
{
|
|
if ((dev_priv->pipestat[pipe] & mask) != 0) {
|
|
u32 reg = gma_pipestat(pipe);
|
|
dev_priv->pipestat[pipe] &= ~mask;
|
|
if (gma_power_begin(&dev_priv->dev, false)) {
|
|
u32 writeVal = PSB_RVDC32(reg);
|
|
writeVal &= ~mask;
|
|
PSB_WVDC32(writeVal, reg);
|
|
(void) PSB_RVDC32(reg);
|
|
gma_power_end(&dev_priv->dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Display controller interrupt handler for pipe event.
|
|
*/
|
|
static void gma_pipe_event_handler(struct drm_device *dev, int pipe)
|
|
{
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
|
|
uint32_t pipe_stat_val = 0;
|
|
uint32_t pipe_stat_reg = gma_pipestat(pipe);
|
|
uint32_t pipe_enable = dev_priv->pipestat[pipe];
|
|
uint32_t pipe_status = dev_priv->pipestat[pipe] >> 16;
|
|
uint32_t pipe_clear;
|
|
uint32_t i = 0;
|
|
|
|
spin_lock(&dev_priv->irqmask_lock);
|
|
|
|
pipe_stat_val = PSB_RVDC32(pipe_stat_reg);
|
|
pipe_stat_val &= pipe_enable | pipe_status;
|
|
pipe_stat_val &= pipe_stat_val >> 16;
|
|
|
|
spin_unlock(&dev_priv->irqmask_lock);
|
|
|
|
/* Clear the 2nd level interrupt status bits
|
|
* Sometimes the bits are very sticky so we repeat until they unstick */
|
|
for (i = 0; i < 0xffff; i++) {
|
|
PSB_WVDC32(PSB_RVDC32(pipe_stat_reg), pipe_stat_reg);
|
|
pipe_clear = PSB_RVDC32(pipe_stat_reg) & pipe_status;
|
|
|
|
if (pipe_clear == 0)
|
|
break;
|
|
}
|
|
|
|
if (pipe_clear)
|
|
dev_err(dev->dev,
|
|
"%s, can't clear status bits for pipe %d, its value = 0x%x.\n",
|
|
__func__, pipe, PSB_RVDC32(pipe_stat_reg));
|
|
|
|
if (pipe_stat_val & PIPE_VBLANK_STATUS) {
|
|
struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
|
|
struct gma_crtc *gma_crtc = to_gma_crtc(crtc);
|
|
unsigned long flags;
|
|
|
|
drm_handle_vblank(dev, pipe);
|
|
|
|
spin_lock_irqsave(&dev->event_lock, flags);
|
|
if (gma_crtc->page_flip_event) {
|
|
drm_crtc_send_vblank_event(crtc,
|
|
gma_crtc->page_flip_event);
|
|
gma_crtc->page_flip_event = NULL;
|
|
drm_crtc_vblank_put(crtc);
|
|
}
|
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Display controller interrupt handler.
|
|
*/
|
|
static void gma_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_VSYNC_PIPEA_FLAG)
|
|
gma_pipe_event_handler(dev, 0);
|
|
|
|
if (vdc_stat & _PSB_VSYNC_PIPEB_FLAG)
|
|
gma_pipe_event_handler(dev, 1);
|
|
}
|
|
|
|
/*
|
|
* SGX interrupt handler
|
|
*/
|
|
static void gma_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2)
|
|
{
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
u32 val, addr;
|
|
|
|
if (stat_1 & _PSB_CE_TWOD_COMPLETE)
|
|
val = PSB_RSGX32(PSB_CR_2D_BLIT_STATUS);
|
|
|
|
if (stat_2 & _PSB_CE2_BIF_REQUESTER_FAULT) {
|
|
val = PSB_RSGX32(PSB_CR_BIF_INT_STAT);
|
|
addr = PSB_RSGX32(PSB_CR_BIF_FAULT);
|
|
if (val) {
|
|
if (val & _PSB_CBI_STAT_PF_N_RW)
|
|
DRM_ERROR("SGX MMU page fault:");
|
|
else
|
|
DRM_ERROR("SGX MMU read / write protection fault:");
|
|
|
|
if (val & _PSB_CBI_STAT_FAULT_CACHE)
|
|
DRM_ERROR("\tCache requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_TA)
|
|
DRM_ERROR("\tTA requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_VDM)
|
|
DRM_ERROR("\tVDM requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_2D)
|
|
DRM_ERROR("\t2D requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_PBE)
|
|
DRM_ERROR("\tPBE requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_TSP)
|
|
DRM_ERROR("\tTSP requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_ISP)
|
|
DRM_ERROR("\tISP requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_USSEPDS)
|
|
DRM_ERROR("\tUSSEPDS requestor");
|
|
if (val & _PSB_CBI_STAT_FAULT_HOST)
|
|
DRM_ERROR("\tHost requestor");
|
|
|
|
DRM_ERROR("\tMMU failing address is 0x%08x.\n",
|
|
(unsigned int)addr);
|
|
}
|
|
}
|
|
|
|
/* Clear bits */
|
|
PSB_WSGX32(stat_1, PSB_CR_EVENT_HOST_CLEAR);
|
|
PSB_WSGX32(stat_2, PSB_CR_EVENT_HOST_CLEAR2);
|
|
PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR2);
|
|
}
|
|
|
|
static irqreturn_t gma_irq_handler(int irq, void *arg)
|
|
{
|
|
struct drm_device *dev = arg;
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
uint32_t vdc_stat, dsp_int = 0, sgx_int = 0, hotplug_int = 0;
|
|
u32 sgx_stat_1, sgx_stat_2;
|
|
int handled = 0;
|
|
|
|
spin_lock(&dev_priv->irqmask_lock);
|
|
|
|
vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R);
|
|
|
|
if (vdc_stat & (_PSB_PIPE_EVENT_FLAG|_PSB_IRQ_ASLE))
|
|
dsp_int = 1;
|
|
|
|
if (vdc_stat & _PSB_IRQ_SGX_FLAG)
|
|
sgx_int = 1;
|
|
if (vdc_stat & _PSB_IRQ_DISP_HOTSYNC)
|
|
hotplug_int = 1;
|
|
|
|
vdc_stat &= dev_priv->vdc_irq_mask;
|
|
spin_unlock(&dev_priv->irqmask_lock);
|
|
|
|
if (dsp_int) {
|
|
gma_vdc_interrupt(dev, vdc_stat);
|
|
handled = 1;
|
|
}
|
|
|
|
if (sgx_int) {
|
|
sgx_stat_1 = PSB_RSGX32(PSB_CR_EVENT_STATUS);
|
|
sgx_stat_2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2);
|
|
gma_sgx_interrupt(dev, sgx_stat_1, sgx_stat_2);
|
|
handled = 1;
|
|
}
|
|
|
|
/* Note: this bit has other meanings on some devices, so we will
|
|
need to address that later if it ever matters */
|
|
if (hotplug_int && dev_priv->ops->hotplug) {
|
|
handled = dev_priv->ops->hotplug(dev);
|
|
REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT));
|
|
}
|
|
|
|
PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R);
|
|
(void) PSB_RVDC32(PSB_INT_IDENTITY_R);
|
|
rmb();
|
|
|
|
if (!handled)
|
|
return IRQ_NONE;
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
void gma_irq_preinstall(struct drm_device *dev)
|
|
{
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
unsigned long irqflags;
|
|
|
|
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
|
|
|
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
|
PSB_WVDC32(0x00000000, PSB_INT_MASK_R);
|
|
PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R);
|
|
PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE);
|
|
PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE);
|
|
|
|
if (dev->vblank[0].enabled)
|
|
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
|
|
if (dev->vblank[1].enabled)
|
|
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
|
|
|
|
/* Revisit this area - want per device masks ? */
|
|
if (dev_priv->ops->hotplug)
|
|
dev_priv->vdc_irq_mask |= _PSB_IRQ_DISP_HOTSYNC;
|
|
dev_priv->vdc_irq_mask |= _PSB_IRQ_ASLE | _PSB_IRQ_SGX_FLAG;
|
|
|
|
/* This register is safe even if display island is off */
|
|
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
|
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
|
}
|
|
|
|
void gma_irq_postinstall(struct drm_device *dev)
|
|
{
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
unsigned long irqflags;
|
|
unsigned int i;
|
|
|
|
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
|
|
|
/* Enable 2D and MMU fault interrupts */
|
|
PSB_WSGX32(_PSB_CE2_BIF_REQUESTER_FAULT, PSB_CR_EVENT_HOST_ENABLE2);
|
|
PSB_WSGX32(_PSB_CE_TWOD_COMPLETE, PSB_CR_EVENT_HOST_ENABLE);
|
|
PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); /* Post */
|
|
|
|
/* This register is safe even if display island is off */
|
|
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
|
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
|
|
|
for (i = 0; i < dev->num_crtcs; ++i) {
|
|
if (dev->vblank[i].enabled)
|
|
gma_enable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
|
|
else
|
|
gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
|
|
}
|
|
|
|
if (dev_priv->ops->hotplug_enable)
|
|
dev_priv->ops->hotplug_enable(dev, true);
|
|
|
|
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
|
}
|
|
|
|
int gma_irq_install(struct drm_device *dev)
|
|
{
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
|
int ret;
|
|
|
|
if (dev_priv->use_msi && pci_enable_msi(pdev)) {
|
|
dev_warn(dev->dev, "Enabling MSI failed!\n");
|
|
dev_priv->use_msi = false;
|
|
}
|
|
|
|
if (pdev->irq == IRQ_NOTCONNECTED)
|
|
return -ENOTCONN;
|
|
|
|
gma_irq_preinstall(dev);
|
|
|
|
/* PCI devices require shared interrupts. */
|
|
ret = request_irq(pdev->irq, gma_irq_handler, IRQF_SHARED, dev->driver->name, dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
gma_irq_postinstall(dev);
|
|
|
|
dev_priv->irq_enabled = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gma_irq_uninstall(struct drm_device *dev)
|
|
{
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
|
unsigned long irqflags;
|
|
unsigned int i;
|
|
|
|
if (!dev_priv->irq_enabled)
|
|
return;
|
|
|
|
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
|
|
|
if (dev_priv->ops->hotplug_enable)
|
|
dev_priv->ops->hotplug_enable(dev, false);
|
|
|
|
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
|
|
|
for (i = 0; i < dev->num_crtcs; ++i) {
|
|
if (dev->vblank[i].enabled)
|
|
gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
|
|
}
|
|
|
|
dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG |
|
|
_PSB_IRQ_MSVDX_FLAG |
|
|
_LNC_IRQ_TOPAZ_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);
|
|
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
|
|
|
free_irq(pdev->irq, dev);
|
|
if (dev_priv->use_msi)
|
|
pci_disable_msi(pdev);
|
|
}
|
|
|
|
int gma_crtc_enable_vblank(struct drm_crtc *crtc)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
unsigned int pipe = crtc->index;
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
unsigned long irqflags;
|
|
uint32_t reg_val = 0;
|
|
uint32_t pipeconf_reg = gma_pipeconf(pipe);
|
|
|
|
if (gma_power_begin(dev, false)) {
|
|
reg_val = REG_READ(pipeconf_reg);
|
|
gma_power_end(dev);
|
|
}
|
|
|
|
if (!(reg_val & PIPEACONF_ENABLE))
|
|
return -EINVAL;
|
|
|
|
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
|
|
|
if (pipe == 0)
|
|
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
|
|
else if (pipe == 1)
|
|
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
|
|
|
|
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
|
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
|
gma_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE);
|
|
|
|
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gma_crtc_disable_vblank(struct drm_crtc *crtc)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
unsigned int pipe = crtc->index;
|
|
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
|
unsigned long irqflags;
|
|
|
|
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
|
|
|
if (pipe == 0)
|
|
dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEA_FLAG;
|
|
else if (pipe == 1)
|
|
dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEB_FLAG;
|
|
|
|
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
|
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
|
gma_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 gma_crtc_get_vblank_counter(struct drm_crtc *crtc)
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
unsigned int pipe = crtc->index;
|
|
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:
|
|
dev_err(dev->dev, "%s, invalid pipe.\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if (!gma_power_begin(dev, false))
|
|
return 0;
|
|
|
|
reg_val = REG_READ(pipeconf_reg);
|
|
|
|
if (!(reg_val & PIPEACONF_ENABLE)) {
|
|
dev_err(dev->dev, "trying to get vblank count for disabled pipe %u\n",
|
|
pipe);
|
|
goto err_gma_power_end;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
err_gma_power_end:
|
|
gma_power_end(dev);
|
|
|
|
return count;
|
|
}
|
|
|