Initial checkin of vblank rework. Code attempts to reduce the number
of vblank interrupt in order to save power.main
parent
280083d4a2
commit
db689c7b95
|
@ -627,8 +627,9 @@ struct drm_driver {
|
|||
int (*kernel_context_switch) (struct drm_device * dev, int old,
|
||||
int new);
|
||||
void (*kernel_context_switch_unlock) (struct drm_device * dev);
|
||||
int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence);
|
||||
int (*vblank_wait2) (struct drm_device * dev, unsigned int *sequence);
|
||||
u32 (*get_vblank_counter) (struct drm_device *dev, int crtc);
|
||||
void (*enable_vblank) (struct drm_device *dev, int crtc);
|
||||
void (*disable_vblank) (struct drm_device *dev, int crtc);
|
||||
int (*dri_library_name) (struct drm_device * dev, char * buf);
|
||||
|
||||
/**
|
||||
|
@ -783,12 +784,13 @@ typedef struct drm_device {
|
|||
/*@{ */
|
||||
|
||||
wait_queue_head_t vbl_queue; /**< VBLANK wait queue */
|
||||
atomic_t vbl_received;
|
||||
atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */
|
||||
atomic_t *vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */
|
||||
spinlock_t vbl_lock;
|
||||
struct list_head vbl_sigs; /**< signal list to send on VBLANK */
|
||||
struct list_head vbl_sigs2; /**< signals to send on secondary VBLANK */
|
||||
unsigned int vbl_pending;
|
||||
atomic_t *vbl_pending;
|
||||
u32 *last_vblank; /* protected by dev->vbl_lock */
|
||||
unsigned long max_vblank_count; /**< size of vblank counter register */
|
||||
spinlock_t tasklet_lock; /**< For drm_locked_tasklet */
|
||||
void (*locked_tasklet_func)(struct drm_device *dev);
|
||||
|
||||
|
|
|
@ -221,6 +221,44 @@ int drm_control(struct inode *inode, struct file *filp,
|
|||
}
|
||||
}
|
||||
|
||||
static void drm_vblank_get(drm_device_t *dev, int crtc)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
u32 cur_vblank, diff;
|
||||
|
||||
if (atomic_add_return(1, &dev->vbl_pending[crtc]) != 1)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Interrupts were disabled prior to this call, so deal with counter
|
||||
* wrap if needed.
|
||||
* NOTE! It's possible we lost a full dev->max_vblank_count events
|
||||
* here if the register is small or we had vblank interrupts off for
|
||||
* a long time.
|
||||
*/
|
||||
cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
|
||||
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
||||
if (cur_vblank < dev->last_vblank[crtc]) {
|
||||
diff = dev->max_vblank_count -
|
||||
dev->last_vblank[crtc];
|
||||
diff += cur_vblank;
|
||||
} else {
|
||||
diff = cur_vblank - last_vblank;
|
||||
}
|
||||
dev->last_vblank[crtc] = cur_vblank;
|
||||
spin_lock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
|
||||
atomic_add(diff, &dev->vblank_count[crtc]);
|
||||
dev->driver->enable_vblank(dev, crtc);
|
||||
}
|
||||
|
||||
static void drm_vblank_put(drm_device_t *dev, int crtc)
|
||||
{
|
||||
if (atomic_dec_and_test(&dev->vbl_pending[crtc]))
|
||||
dev->driver->disable_vblank(dev, crtc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wait for VBLANK.
|
||||
*
|
||||
|
@ -248,7 +286,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
|
|||
drm_wait_vblank_t vblwait;
|
||||
struct timeval now;
|
||||
int ret = 0;
|
||||
unsigned int flags, seq;
|
||||
unsigned int flags, seq, crtc;
|
||||
|
||||
if ((!dev->irq) || (!dev->irq_enabled))
|
||||
return -EINVAL;
|
||||
|
@ -265,13 +303,14 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
|
|||
}
|
||||
|
||||
flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK;
|
||||
crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
|
||||
|
||||
if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ?
|
||||
DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL))
|
||||
return -EINVAL;
|
||||
|
||||
seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2
|
||||
: &dev->vbl_received);
|
||||
drm_vblank_get(dev, crtc);
|
||||
seq = atomic_read(&dev->vblank_count[crtc]);
|
||||
|
||||
switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) {
|
||||
case _DRM_VBLANK_RELATIVE:
|
||||
|
@ -307,22 +346,23 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
|
|||
spin_unlock_irqrestore(&dev->vbl_lock,
|
||||
irqflags);
|
||||
vblwait.reply.sequence = seq;
|
||||
drm_vblank_put(dev, crtc);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->vbl_pending >= 100) {
|
||||
if (atomic_read(&dev->vbl_pending[crtc]) >= 100) {
|
||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
drm_vblank_put(dev, crtc);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev->vbl_pending++;
|
||||
|
||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
|
||||
if (!
|
||||
(vbl_sig =
|
||||
drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) {
|
||||
drm_vblank_put(dev, crtc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -340,14 +380,12 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
|
|||
|
||||
vblwait.reply.sequence = seq;
|
||||
} else {
|
||||
if (flags & _DRM_VBLANK_SECONDARY) {
|
||||
if (dev->driver->vblank_wait2)
|
||||
ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence);
|
||||
} else if (dev->driver->vblank_wait)
|
||||
ret =
|
||||
dev->driver->vblank_wait(dev,
|
||||
&vblwait.request.sequence);
|
||||
unsigned long cur_vblank;
|
||||
|
||||
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
|
||||
(((cur_vblank = atomic_read(&dev->vblank_count[crtc]))
|
||||
- seq) <= (1 << 23)));
|
||||
drm_vblank_put(dev, crtc);
|
||||
do_gettimeofday(&now);
|
||||
vblwait.reply.tval_sec = now.tv_sec;
|
||||
vblwait.reply.tval_usec = now.tv_usec;
|
||||
|
@ -379,8 +417,7 @@ void drm_vbl_send_signals(drm_device_t * dev)
|
|||
for (i = 0; i < 2; i++) {
|
||||
drm_vbl_sig_t *vbl_sig, *tmp;
|
||||
struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs;
|
||||
unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 :
|
||||
&dev->vbl_received);
|
||||
unsigned int vbl_seq = atomic_read(&dev->vblank_count[i]);
|
||||
|
||||
list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
|
||||
if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
|
||||
|
@ -393,7 +430,7 @@ void drm_vbl_send_signals(drm_device_t * dev)
|
|||
drm_free(vbl_sig, sizeof(*vbl_sig),
|
||||
DRM_MEM_DRIVER);
|
||||
|
||||
dev->vbl_pending--;
|
||||
drm_vblank_put(dev, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,8 +83,9 @@ static struct drm_driver driver = {
|
|||
.lastclose = i915_driver_lastclose,
|
||||
.preclose = i915_driver_preclose,
|
||||
.device_is_agp = i915_driver_device_is_agp,
|
||||
.vblank_wait = i915_driver_vblank_wait,
|
||||
.vblank_wait2 = i915_driver_vblank_wait2,
|
||||
.get_vblank_counter = i915_get_vblank_counter,
|
||||
.enable_vblank = i915_enable_vblank,
|
||||
.disable_vblank = i915_disable_vblank,
|
||||
.irq_preinstall = i915_driver_irq_preinstall,
|
||||
.irq_postinstall = i915_driver_irq_postinstall,
|
||||
.irq_uninstall = i915_driver_irq_uninstall,
|
||||
|
|
|
@ -833,6 +833,20 @@ static int i915_setparam(DRM_IOCTL_ARGS)
|
|||
case I915_SETPARAM_ALLOW_BATCHBUFFER:
|
||||
dev_priv->allow_batchbuffer = param.value;
|
||||
break;
|
||||
case I915_SETPARAM_PREMODESET:
|
||||
if (param.value > 1) {
|
||||
DRM_ERROR("bad crtc\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
i915_premodeset(dev, param.value);
|
||||
break;
|
||||
case I915_SETPARAM_POSTMODESET:
|
||||
if (param.value > 1) {
|
||||
DRM_ERROR("bad crtc\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
i915_postmodeset(dev, param.value);
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("unknown parameter %d\n", param.param);
|
||||
return DRM_ERR(EINVAL);
|
||||
|
|
|
@ -235,6 +235,8 @@ typedef struct drm_i915_getparam {
|
|||
#define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1
|
||||
#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2
|
||||
#define I915_SETPARAM_ALLOW_BATCHBUFFER 3
|
||||
#define I915_SETPARAM_PREMODESET 4
|
||||
#define I915_SETPARAM_POSTMODESET 5
|
||||
|
||||
typedef struct drm_i915_setparam {
|
||||
int param;
|
||||
|
|
|
@ -132,6 +132,8 @@ typedef struct drm_i915_private {
|
|||
spinlock_t swaps_lock;
|
||||
drm_i915_vbl_swap_t vbl_swaps;
|
||||
unsigned int swaps_pending;
|
||||
unsigned long vblank_offset[2];
|
||||
unsigned long vblank_premodeset[2];
|
||||
} drm_i915_private_t;
|
||||
|
||||
enum intel_chip_family {
|
||||
|
@ -161,8 +163,6 @@ extern int i915_driver_firstopen(struct drm_device *dev);
|
|||
extern int i915_irq_emit(DRM_IOCTL_ARGS);
|
||||
extern int i915_irq_wait(DRM_IOCTL_ARGS);
|
||||
|
||||
extern int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence);
|
||||
extern int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence);
|
||||
extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
|
||||
extern void i915_driver_irq_preinstall(drm_device_t * dev);
|
||||
extern void i915_driver_irq_postinstall(drm_device_t * dev);
|
||||
|
@ -173,6 +173,11 @@ extern int i915_emit_irq(drm_device_t * dev);
|
|||
extern void i915_user_irq_on(drm_i915_private_t *dev_priv);
|
||||
extern void i915_user_irq_off(drm_i915_private_t *dev_priv);
|
||||
extern int i915_vblank_swap(DRM_IOCTL_ARGS);
|
||||
extern void i915_enable_vblank(drm_device_t *dev, int crtc);
|
||||
extern void i915_disable_vblank(drm_device_t *dev, int crtc);
|
||||
extern u32 i915_get_vblank_counter(drm_device_t *dev, int crtc);
|
||||
extern void i915_premodeset(drm_device_t *dev, int crtc);
|
||||
extern void i915_postmodeset(drm_device_t *dev, int crtc);
|
||||
|
||||
/* i915_mem.c */
|
||||
extern int i915_mem_alloc(DRM_IOCTL_ARGS);
|
||||
|
@ -271,6 +276,36 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller);
|
|||
|
||||
#define I915REG_PIPEASTAT 0x70024
|
||||
#define I915REG_PIPEBSTAT 0x71024
|
||||
/*
|
||||
* The two pipe frame counter registers are not synchronized, so
|
||||
* reading a stable value is somewhat tricky. The following code
|
||||
* should work:
|
||||
*
|
||||
* do {
|
||||
* high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >>
|
||||
* PIPE_FRAME_HIGH_SHIFT;
|
||||
* low1 = ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >>
|
||||
* PIPE_FRAME_LOW_SHIFT);
|
||||
* high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >>
|
||||
* PIPE_FRAME_HIGH_SHIFT);
|
||||
* } while (high1 != high2);
|
||||
* frame = (high1 << 8) | low1;
|
||||
*/
|
||||
#define PIPEAFRAMEHIGH 0x70040
|
||||
#define PIPEBFRAMEHIGH 0x71040
|
||||
#define PIPE_FRAME_HIGH_MASK 0x0000ffff
|
||||
#define PIPE_FRAME_HIGH_SHIFT 0
|
||||
#define PIPEAFRAMEPIXEL 0x70044
|
||||
#define PIPEBFRAMEPIXEL 0x71044
|
||||
|
||||
#define PIPE_FRAME_LOW_MASK 0xff000000
|
||||
#define PIPE_FRAME_LOW_SHIFT 24
|
||||
/*
|
||||
* Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register
|
||||
* and is 24 bits wide.
|
||||
*/
|
||||
#define PIPE_PIXEL_MASK 0x00ffffff
|
||||
#define PIPE_PIXEL_SHIFT 0
|
||||
|
||||
#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17)
|
||||
#define I915_VBLANK_CLEAR (1UL<<1)
|
||||
|
|
|
@ -92,8 +92,8 @@ static void i915_vblank_tasklet(drm_device_t *dev)
|
|||
unsigned long irqflags;
|
||||
struct list_head *list, *tmp, hits, *hit;
|
||||
int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;
|
||||
unsigned counter[2] = { atomic_read(&dev->vbl_received),
|
||||
atomic_read(&dev->vbl_received2) };
|
||||
unsigned counter[2] = { atomic_read(&dev->vblank_count[0]),
|
||||
atomic_read(&dev->vblank_count[1]) };
|
||||
drm_drawable_info_t *drw;
|
||||
drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
|
||||
u32 cpp = dev_priv->cpp, offsets[3];
|
||||
|
@ -313,14 +313,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
|
|||
(DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
|
||||
== (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
|
||||
if (temp & VSYNC_PIPEA_FLAG)
|
||||
atomic_inc(&dev->vbl_received);
|
||||
atomic_inc(&dev->vblank_count[0]);
|
||||
if (temp & VSYNC_PIPEB_FLAG)
|
||||
atomic_inc(&dev->vbl_received2);
|
||||
atomic_inc(&dev->vblank_count[1]);
|
||||
} else if (((temp & VSYNC_PIPEA_FLAG) &&
|
||||
(vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
|
||||
((temp & VSYNC_PIPEB_FLAG) &&
|
||||
(vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
|
||||
atomic_inc(&dev->vbl_received);
|
||||
atomic_inc(&dev->vblank_count[0]);
|
||||
|
||||
DRM_WAKEUP(&dev->vbl_queue);
|
||||
drm_vbl_send_signals(dev);
|
||||
|
@ -410,37 +410,6 @@ static int i915_wait_irq(drm_device_t * dev, int irq_nr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int i915_driver_vblank_do_wait(drm_device_t *dev, unsigned int *sequence,
|
||||
atomic_t *counter)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
unsigned int cur_vblank;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev_priv) {
|
||||
DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
|
||||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
|
||||
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
|
||||
(((cur_vblank = atomic_read(counter))
|
||||
- *sequence) <= (1<<23)));
|
||||
|
||||
*sequence = cur_vblank;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence)
|
||||
{
|
||||
return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);
|
||||
}
|
||||
|
||||
int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence)
|
||||
{
|
||||
return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2);
|
||||
}
|
||||
|
||||
/* Needs the lock as it touches the ring.
|
||||
*/
|
||||
int i915_irq_emit(DRM_IOCTL_ARGS)
|
||||
|
@ -489,15 +458,99 @@ int i915_irq_wait(DRM_IOCTL_ARGS)
|
|||
return i915_wait_irq(dev, irqwait.irq_seq);
|
||||
}
|
||||
|
||||
void i915_enable_vblank(drm_device_t *dev, int crtc)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
|
||||
switch (crtc) {
|
||||
case 0:
|
||||
dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG;
|
||||
break;
|
||||
case 1:
|
||||
dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
|
||||
crtc);
|
||||
break;
|
||||
}
|
||||
|
||||
I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
|
||||
}
|
||||
|
||||
void i915_disable_vblank(drm_device_t *dev, int crtc)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
|
||||
switch (crtc) {
|
||||
case 0:
|
||||
dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG;
|
||||
break;
|
||||
case 1:
|
||||
dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("tried to disable vblank on non-existent crtc %d\n",
|
||||
crtc);
|
||||
break;
|
||||
}
|
||||
|
||||
I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
|
||||
}
|
||||
|
||||
static u32 i915_vblank_counter(drm_device_t *dev, int crtc)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long high_frame = crtc ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
|
||||
unsigned long low_frame = crtc ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
|
||||
u32 high1, high2, low, count;
|
||||
|
||||
/*
|
||||
* 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 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
|
||||
PIPE_FRAME_HIGH_SHIFT);
|
||||
low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
|
||||
PIPE_FRAME_LOW_SHIFT);
|
||||
high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
|
||||
PIPE_FRAME_HIGH_SHIFT);
|
||||
} while (high1 != high2);
|
||||
|
||||
count = (high1 << 8) | low;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 i915_get_vblank_counter(drm_device_t *dev, int crtc)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
return i915_vblank_counter(dev, crtc) + dev_priv->vblank_offset[crtc];
|
||||
}
|
||||
|
||||
void i915_premodeset(drm_device_t *dev, int crtc)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
|
||||
dev_priv->vblank_premodeset[crtc] = i915_vblank_counter(dev, crtc);
|
||||
}
|
||||
|
||||
void i915_postmodeset(drm_device_t *dev, int crtc)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
u32 new;
|
||||
|
||||
new = i915_vblank_counter(dev, crtc);
|
||||
dev_priv->vblank_offset[crtc] = dev_priv->vblank_premodeset[crtc] - new;
|
||||
}
|
||||
|
||||
static void i915_enable_interrupt (drm_device_t *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
|
||||
dev_priv->irq_enable_reg = USER_INT_FLAG;
|
||||
if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
|
||||
dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG;
|
||||
if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
|
||||
dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG;
|
||||
dev_priv->irq_enable_reg |= USER_INT_FLAG;
|
||||
|
||||
I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
|
||||
dev_priv->irq_enabled = 1;
|
||||
|
@ -607,7 +660,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS)
|
|||
|
||||
spin_unlock_irqrestore(&dev->drw_lock, irqflags);
|
||||
|
||||
curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received);
|
||||
curseq = atomic_read(&dev->vblank_count[pipe]);
|
||||
|
||||
if (seqtype == _DRM_VBLANK_RELATIVE)
|
||||
swap.sequence += curseq;
|
||||
|
@ -714,6 +767,7 @@ void i915_driver_irq_preinstall(drm_device_t * dev)
|
|||
void i915_driver_irq_postinstall(drm_device_t * dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
int i;
|
||||
|
||||
spin_lock_init(&dev_priv->swaps_lock);
|
||||
INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
|
||||
|
@ -721,6 +775,34 @@ void i915_driver_irq_postinstall(drm_device_t * dev)
|
|||
|
||||
dev_priv->user_irq_lock = SPIN_LOCK_UNLOCKED;
|
||||
dev_priv->user_irq_refcount = 0;
|
||||
dev_priv->irq_enable_reg = 0;
|
||||
dev->vblank_count = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL);
|
||||
if (!dev->vblank_count) {
|
||||
DRM_ERROR("out of memory\n");
|
||||
return;
|
||||
}
|
||||
dev->vbl_pending = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL);
|
||||
if (!dev->vbl_pending) {
|
||||
DRM_ERROR("out of memory\n");
|
||||
kfree(dev->vblank_count);
|
||||
return;
|
||||
}
|
||||
dev->last_vblank = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL);
|
||||
if (!dev->last_vblank) {
|
||||
DRM_ERROR("out of memory\n");
|
||||
kfree(dev->vblank_count);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Zero per-crtc vblank stuff */
|
||||
for (i = 0; i < 2; i++) {
|
||||
atomic_set(&dev->vbl_pending[i], 0);
|
||||
atomic_set(&dev->vblank_count[i], 0);
|
||||
dev->last_vblank[i] = 0;
|
||||
dev_priv->vblank_offset[i] = 0;
|
||||
}
|
||||
|
||||
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
|
||||
|
||||
i915_enable_interrupt(dev);
|
||||
DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
|
||||
|
|
|
@ -299,6 +299,9 @@ typedef struct drm_radeon_private {
|
|||
|
||||
u32 scratch_ages[5];
|
||||
|
||||
unsigned int crtc_last_cnt;
|
||||
unsigned int crtc2_last_cnt;
|
||||
|
||||
/* starting from here on, data is preserved accross an open */
|
||||
uint32_t flags; /* see radeon_chip_flags */
|
||||
|
||||
|
@ -364,9 +367,9 @@ extern int radeon_irq_wait(DRM_IOCTL_ARGS);
|
|||
|
||||
extern void radeon_do_release(drm_device_t * dev);
|
||||
extern int radeon_driver_vblank_wait(drm_device_t * dev,
|
||||
unsigned int *sequence);
|
||||
unsigned int *sequence, int relative);
|
||||
extern int radeon_driver_vblank_wait2(drm_device_t * dev,
|
||||
unsigned int *sequence);
|
||||
unsigned int *sequence, int relative);
|
||||
extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS);
|
||||
extern void radeon_driver_irq_preinstall(drm_device_t * dev);
|
||||
extern void radeon_driver_irq_postinstall(drm_device_t * dev);
|
||||
|
@ -507,6 +510,9 @@ extern int r300_do_cp_cmdbuf(drm_device_t *dev, DRMFILE filp,
|
|||
? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \
|
||||
: RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) )
|
||||
|
||||
#define RADEON_CRTC_CRNT_FRAME 0x0214
|
||||
#define RADEON_CRTC2_CRNT_FRAME 0x0314
|
||||
|
||||
#define RADEON_GEN_INT_CNTL 0x0040
|
||||
# define RADEON_CRTC_VBLANK_MASK (1 << 0)
|
||||
# define RADEON_CRTC2_VBLANK_MASK (1 << 9)
|
||||
|
|
|
@ -35,6 +35,18 @@
|
|||
#include "radeon_drm.h"
|
||||
#include "radeon_drv.h"
|
||||
|
||||
static void radeon_irq_set_state(drm_device_t *dev, u32 mask, int state)
|
||||
{
|
||||
drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private;
|
||||
|
||||
if (state)
|
||||
dev_priv->irq_enable_reg |= mask;
|
||||
else
|
||||
dev_priv->irq_enable_reg &= ~mask;
|
||||
|
||||
RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
|
||||
}
|
||||
|
||||
static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv,
|
||||
u32 mask)
|
||||
{
|
||||
|
@ -141,14 +153,17 @@ static int radeon_wait_irq(drm_device_t * dev, int swi_nr)
|
|||
}
|
||||
|
||||
int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence,
|
||||
int crtc)
|
||||
int crtc, int relative)
|
||||
{
|
||||
drm_radeon_private_t *dev_priv =
|
||||
(drm_radeon_private_t *) dev->dev_private;
|
||||
unsigned int cur_vblank;
|
||||
unsigned int cur_vblank, diff, irqflags, current_cnt;
|
||||
int ret = 0;
|
||||
int ack = 0;
|
||||
atomic_t *counter;
|
||||
unsigned int *last_cnt;
|
||||
int crtc_cnt_reg;
|
||||
|
||||
if (!dev_priv) {
|
||||
DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
|
||||
return DRM_ERR(EINVAL);
|
||||
|
@ -156,10 +171,14 @@ int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence,
|
|||
|
||||
if (crtc == DRM_RADEON_VBLANK_CRTC1) {
|
||||
counter = &dev->vbl_received;
|
||||
ack |= RADEON_CRTC_VBLANK_STAT;
|
||||
ack = RADEON_CRTC_VBLANK_STAT;
|
||||
last_cnt = &dev_priv->crtc_last_cnt;
|
||||
crtc_cnt_reg = RADEON_CRTC_CRNT_FRAME;
|
||||
} else if (crtc == DRM_RADEON_VBLANK_CRTC2) {
|
||||
counter = &dev->vbl_received2;
|
||||
ack |= RADEON_CRTC2_VBLANK_STAT;
|
||||
ack = RADEON_CRTC2_VBLANK_STAT;
|
||||
last_cnt = &dev_priv->crtc2_last_cnt;
|
||||
crtc_cnt_reg = RADEON_CRTC2_CRNT_FRAME;
|
||||
} else
|
||||
return DRM_ERR(EINVAL);
|
||||
|
||||
|
@ -167,27 +186,46 @@ int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence,
|
|||
|
||||
dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
|
||||
|
||||
if (!relative) {
|
||||
/*
|
||||
* Assume we haven't missed more than several hours of vblank
|
||||
* events, or that it won't matter if they're not accounted
|
||||
* for in the master counter.
|
||||
*/
|
||||
spin_lock_irqsave(&dev->vbl_lock, irqflags);
|
||||
current_cnt = RADEON_READ(crtc_cnt_reg);
|
||||
if (current_cnt < *last_cnt) {
|
||||
current_cnt += (1 << 21) - *last_cnt;
|
||||
*last_cnt = 0;
|
||||
}
|
||||
diff = current_cnt - *last_cnt;
|
||||
*last_cnt = RADEON_READ(crtc_cnt_reg);
|
||||
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
|
||||
atomic_add(diff, counter);
|
||||
}
|
||||
|
||||
/* Assume that the user has missed the current sequence number
|
||||
* by about a day rather than she wants to wait for years
|
||||
* using vertical blanks...
|
||||
*/
|
||||
radeon_irq_set_state(dev, ack, 1);
|
||||
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
|
||||
(((cur_vblank = atomic_read(counter))
|
||||
- *sequence) <= (1 << 23)));
|
||||
|
||||
radeon_irq_set_state(dev, ack, 0);
|
||||
*sequence = cur_vblank;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int radeon_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence)
|
||||
int radeon_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence, int relative)
|
||||
{
|
||||
return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1);
|
||||
return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1, relative);
|
||||
}
|
||||
|
||||
int radeon_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence)
|
||||
int radeon_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence, int relative)
|
||||
{
|
||||
return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2);
|
||||
return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2, relative);
|
||||
}
|
||||
|
||||
/* Needs the lock as it touches the ring.
|
||||
|
@ -238,20 +276,7 @@ int radeon_irq_wait(DRM_IOCTL_ARGS)
|
|||
return radeon_wait_irq(dev, irqwait.irq_seq);
|
||||
}
|
||||
|
||||
static void radeon_enable_interrupt(drm_device_t *dev)
|
||||
{
|
||||
drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private;
|
||||
|
||||
dev_priv->irq_enable_reg = RADEON_SW_INT_ENABLE;
|
||||
if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC1)
|
||||
dev_priv->irq_enable_reg |= RADEON_CRTC_VBLANK_MASK;
|
||||
|
||||
if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC2)
|
||||
dev_priv->irq_enable_reg |= RADEON_CRTC2_VBLANK_MASK;
|
||||
|
||||
RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
|
||||
dev_priv->irq_enabled = 1;
|
||||
}
|
||||
|
||||
/* drm_dma.h hooks
|
||||
*/
|
||||
|
@ -265,7 +290,8 @@ void radeon_driver_irq_preinstall(drm_device_t * dev)
|
|||
|
||||
/* Clear bits if they're already high */
|
||||
radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK |
|
||||
RADEON_CRTC_VBLANK_STAT));
|
||||
RADEON_CRTC_VBLANK_STAT |
|
||||
RADEON_CRTC2_VBLANK_STAT));
|
||||
}
|
||||
|
||||
void radeon_driver_irq_postinstall(drm_device_t * dev)
|
||||
|
@ -276,7 +302,7 @@ void radeon_driver_irq_postinstall(drm_device_t * dev)
|
|||
atomic_set(&dev_priv->swi_emitted, 0);
|
||||
DRM_INIT_WAITQUEUE(&dev_priv->swi_queue);
|
||||
|
||||
radeon_enable_interrupt(dev);
|
||||
radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);
|
||||
}
|
||||
|
||||
void radeon_driver_irq_uninstall(drm_device_t * dev)
|
||||
|
@ -318,6 +344,5 @@ int radeon_vblank_crtc_set(drm_device_t *dev, int64_t value)
|
|||
return DRM_ERR(EINVAL);
|
||||
}
|
||||
dev_priv->vblank_crtc = (unsigned int)value;
|
||||
radeon_enable_interrupt(dev);
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue