On Monday, June 11, 2007 11:36:10 Keith Packard wrote:
> ick. just read the registers and return the value here. We should place
> wrap-detection in the core code by reporting the range of the register
> values; with the offset suggested above, that would result in a single
> addition to convert from raw to cooked frame counts.
Ok, here's an updated version:
- move wraparound logic to the core
- add pre/post modeset ioctls (per-driver right now, making them core would
mean lots more DDX changes I think), hope I got this right
- add vblank_get/put calls so interrupts are enabled at the right time
I haven't implemented Ville's suggestion of adding a short timer before
disabling interrupts again, but it should be easy now that the get/put
routines are in place and we think it's worth it (might make vblank
calls a little cheaper, but it would probably be hard to detect).
Any more thoughts? If it looks good, I'll go ahead and test it, clean it up a
bit,
and convert my old radeon patch over to this new scheme (other drivers will need
work too though).
Thanks,
Jesse
diff --git a/linux-core/drmP.h b/linux-core/drmP.h
index dd3a69d..ed0e0b7 100644
--- a/linux-core/drmP.h
+++ b/linux-core/drmP.h
@@ -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,12 @@ typedef struct drm_device {
/[EMAIL PROTECTED] */
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;
+ 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);
diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c
index 8871671..d9c396a 100644
--- a/linux-core/drm_irq.c
+++ b/linux-core/drm_irq.c
@@ -221,6 +221,42 @@ int drm_control(struct inode *inode, struct file *filp,
}
}
+static u32 last_vblank; /* protected by dev->vbl_lock */
+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;
+
+ /*
+ * Interrpts were disabled prior to this call, so deal with counter
+ * wrap if needed.
+ */
+ cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ if (cur_vblank < last_vblank) {
+ diff = dev->max_vblank_count -
+ last_vblank;
+ diff += cur_vblank;
+ } else {
+ diff = cur_vblank - last_vblank;
+ }
+ last_vblank = cur_vblank;
+ spin_unlock_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 +284,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 +301,13 @@ 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);
+ seq = atomic_read(&dev->vblank_count[crtc]);
switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) {
case _DRM_VBLANK_RELATIVE:
@@ -311,15 +347,15 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
}
}
- if (dev->vbl_pending >= 100) {
+ if (atomic_read(&dev->vbl_pending[crtc]) >= 100) {
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
return -EBUSY;
}
- dev->vbl_pending++;
-
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+ drm_vblank_get(dev, crtc);
+
if (!
(vbl_sig =
drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) {
@@ -340,14 +376,13 @@ 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_vblank_get(dev, crtc);
+ 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 +414,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 +427,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);
}
}
}
diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c
index 7fdb083..e7a9bfd 100644
--- a/linux-core/i915_drv.c
+++ b/linux-core/i915_drv.c
@@ -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,
diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h
index 9deee8e..9aafc2f 100644
--- a/shared-core/i915_drv.h
+++ b/shared-core/i915_drv.h
@@ -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,9 @@ 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);
/* i915_mem.c */
extern int i915_mem_alloc(DRM_IOCTL_ARGS);
@@ -271,6 +274,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)
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c
index dc00f98..6aa5d39 100644
--- a/shared-core/i915_irq.c
+++ b/shared-core/i915_irq.c
@@ -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,116 @@ int i915_irq_wait(DRM_IOCTL_ARGS)
return i915_wait_irq(dev, irqwait.irq_seq);
}
-static void i915_enable_interrupt (drm_device_t *dev)
+void i915_enable_vblank(drm_device_t *dev, int crtc)
{
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)
+ switch (crtc) {
+ case 0:
dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG;
- if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
+ 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];
+}
+
+int i915_premodeset(DRM_IOCTL_ARGS)
+{
+ DRM_DEVICE;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ int crtc = data;
+
+ if (crtc > 1) {
+ DRM_ERROR("bad crtc\n");
+ return -EINVAL;
+ }
+
+ dev_priv->vblank_premodeset[crtc] = i915_vblank_counter(dev, crtc);
+ return 0;
+}
+
+int i915_postmodeset(DRM_IOCTL_ARGS)
+{
+ DRM_DEVICE;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 new;
+ int crtc = data;
+
+ if (crtc > 1) {
+ DRM_ERROR("bad crtc\n");
+ return -EINVAL;
+ }
+
+ new = i915_vblank_counter(dev, crtc);
+ dev_priv->vblank_offset[crtc] = dev_priv->vblank_premodeset[crtc] - new;
+ return 0;
+}
+
+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;
I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
dev_priv->irq_enabled = 1;
@@ -607,7 +677,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 +784,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 +792,27 @@ 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;
+ }
+
+ /* 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_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);
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel