From: Arjan van de Ven <arjanv@redhat.com>
To: Arjan van de Ven <arjanv@redhat.com>
Cc: Dave Airlie <airlied@linux.ie>,
linux-kernel@vger.kernel.org, alan@lxorguk.ukuu.org.uk
Subject: Re: 2.4.20-ac2 and i810 drm
Date: 12 Dec 2002 12:29:58 +0100 [thread overview]
Message-ID: <1039692598.1450.4.camel@laptop.fenrus.com> (raw)
In-Reply-To: <1039681967.1450.0.camel@laptop.fenrus.com>
[-- Attachment #1: Type: text/plain, Size: 666 bytes --]
On Thu, 2002-12-12 at 09:32, Arjan van de Ven wrote:
> On Thu, 2002-12-12 at 04:54, Dave Airlie wrote:
> >
> > I've been running 2.4.20-rc4 up to now with DRM enabled for my i810
> > chipset and XFree86 4.2 from RH 7.3.
> >
> > When I run my OpenGL application (internal app) under 2.4.20-ac2 with the
> > same .config when I ctrl-c the application the machine hangs hard.
> >
> > It is the only application running on the X server so the X server
> > restarts when I exit the app.. under 2.4.20-rc4 this works fine...
>
> I just got an updated source for the i810 DRM and will port it to -ac2;
> lots of i810 bugfixes
eh woops, i830 updates
patch is attached
[-- Attachment #2: linux-2.4.20-drm43.patch --]
[-- Type: text/x-patch, Size: 53176 bytes --]
diff -urN linux-2.4.20/drivers/char/drm.org/drm_dma.h linux-2.4.20/drivers/char/drm/drm_dma.h
--- linux-2.4.20/drivers/char/drm.org/drm_dma.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/drm_dma.h 2002-12-12 12:27:37.000000000 +0100
@@ -539,6 +539,10 @@
#if __HAVE_VBL_IRQ
init_waitqueue_head(&dev->vbl_queue);
+
+ spin_lock_init( &dev->vbl_lock );
+
+ INIT_LIST_HEAD( &dev->vbl_sigs.head );
#endif
/* Before installing handler */
@@ -609,7 +613,8 @@
drm_device_t *dev = priv->dev;
drm_wait_vblank_t vblwait;
struct timeval now;
- int ret;
+ int ret = 0;
+ unsigned int flags;
if (!dev->irq)
return -EINVAL;
@@ -617,15 +622,45 @@
DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data,
sizeof(vblwait) );
- if ( vblwait.type == _DRM_VBLANK_RELATIVE ) {
- vblwait.sequence += atomic_read( &dev->vbl_received );
+ switch ( vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK ) {
+ case _DRM_VBLANK_RELATIVE:
+ vblwait.request.sequence += atomic_read( &dev->vbl_received );
+ case _DRM_VBLANK_ABSOLUTE:
+ break;
+ default:
+ return -EINVAL;
}
- ret = DRM(vblank_wait)( dev, &vblwait.sequence );
+ flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK;
+
+ if ( flags & _DRM_VBLANK_SIGNAL ) {
+ unsigned long irqflags;
+ drm_vbl_sig_t *vbl_sig = kmalloc( sizeof( drm_vbl_sig_t ), GFP_KERNEL );
+
+ if ( !vbl_sig )
+ return -ENOMEM;
+
+ memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) );
- do_gettimeofday( &now );
- vblwait.tval_sec = now.tv_sec;
- vblwait.tval_usec = now.tv_usec;
+ vbl_sig->sequence = vblwait.request.sequence;
+ vbl_sig->info.si_signo = vblwait.request.signal;
+ vbl_sig->task = current;
+
+ vblwait.reply.sequence = atomic_read( &dev->vbl_received );
+
+ /* Hook signal entry into list */
+ spin_lock_irqsave( &dev->vbl_lock, irqflags );
+
+ list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head );
+
+ spin_unlock_irqrestore( &dev->vbl_lock, irqflags );
+ } else {
+ ret = DRM(vblank_wait)( dev, &vblwait.request.sequence );
+
+ do_gettimeofday( &now );
+ vblwait.reply.tval_sec = now.tv_sec;
+ vblwait.reply.tval_usec = now.tv_usec;
+ }
DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
sizeof(vblwait) );
@@ -633,6 +668,33 @@
return ret;
}
+void DRM(vbl_send_signals)( drm_device_t *dev )
+{
+ struct list_head *entry, *tmp;
+ drm_vbl_sig_t *vbl_sig;
+ unsigned int vbl_seq = atomic_read( &dev->vbl_received );
+ unsigned long flags;
+
+ spin_lock_irqsave( &dev->vbl_lock, flags );
+
+ list_for_each_safe( entry, tmp, &dev->vbl_sigs.head ) {
+
+ vbl_sig = (drm_vbl_sig_t *) entry;
+
+ if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) {
+
+ vbl_sig->info.si_code = atomic_read( &dev->vbl_received );
+ send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task );
+
+ list_del( entry );
+
+ kfree( entry );
+ }
+ }
+
+ spin_unlock_irqrestore( &dev->vbl_lock, flags );
+}
+
#endif /* __HAVE_VBL_IRQ */
#else
diff -urN linux-2.4.20/drivers/char/drm.org/drm_drv.h linux-2.4.20/drivers/char/drm/drm_drv.h
--- linux-2.4.20/drivers/char/drm.org/drm_drv.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/drm_drv.h 2002-12-12 12:27:37.000000000 +0100
@@ -765,8 +765,8 @@
* Begin inline drm_release
*/
- DRM_DEBUG( "pid = %d, device = 0x%x, open_count = %d\n",
- current->pid, dev->device, dev->open_count );
+ DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n",
+ current->pid, (long)dev->device, dev->open_count );
if ( dev->lock.hw_lock &&
_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
@@ -891,8 +891,9 @@
atomic_inc( &dev->counts[_DRM_STAT_IOCTLS] );
++priv->ioctl_count;
- DRM_DEBUG( "pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%x, auth=%d\n",
- current->pid, cmd, nr, dev->device, priv->authenticated );
+ DRM_DEBUG( "pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n",
+ current->pid, cmd, nr, (long)dev->device,
+ priv->authenticated );
if ( nr >= DRIVER_IOCTL_COUNT ) {
retcode = -EINVAL;
diff -urN linux-2.4.20/drivers/char/drm.org/drm_fops.h linux-2.4.20/drivers/char/drm/drm_fops.h
--- linux-2.4.20/drivers/char/drm.org/drm_fops.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/drm_fops.h 2002-12-12 12:27:37.000000000 +0100
@@ -94,8 +94,8 @@
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
- DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
- current->pid, dev->device, dev->open_count);
+ DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
+ current->pid, (long)dev->device, dev->open_count);
return 0;
}
@@ -105,7 +105,7 @@
drm_device_t *dev = priv->dev;
int retcode;
- DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device);
+ DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, (long)dev->device);
retcode = fasync_helper(fd, filp, on, &dev->buf_async);
if (retcode < 0) return retcode;
return 0;
diff -urN linux-2.4.20/drivers/char/drm.org/drm.h linux-2.4.20/drivers/char/drm/drm.h
--- linux-2.4.20/drivers/char/drm.org/drm.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/drm.h 2002-12-12 12:27:37.000000000 +0100
@@ -354,17 +354,30 @@
} drm_irq_busid_t;
typedef enum {
- _DRM_VBLANK_ABSOLUTE = 0x0, /* Wait for specific vblank sequence number */
- _DRM_VBLANK_RELATIVE = 0x1 /* Wait for given number of vblanks */
+ _DRM_VBLANK_ABSOLUTE = 0x0, /* Wait for specific vblank sequence number */
+ _DRM_VBLANK_RELATIVE = 0x1, /* Wait for given number of vblanks */
+ _DRM_VBLANK_SIGNAL = 0x80000000 /* Send signal instead of blocking */
} drm_vblank_seq_type_t;
-typedef struct drm_radeon_vbl_wait {
+#define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL
+
+struct drm_wait_vblank_request {
+ drm_vblank_seq_type_t type;
+ unsigned int sequence;
+ unsigned long signal;
+};
+
+struct drm_wait_vblank_reply {
drm_vblank_seq_type_t type;
unsigned int sequence;
long tval_sec;
long tval_usec;
-} drm_wait_vblank_t;
+};
+typedef union drm_wait_vblank {
+ struct drm_wait_vblank_request request;
+ struct drm_wait_vblank_reply reply;
+} drm_wait_vblank_t;
typedef struct drm_agp_mode {
unsigned long mode;
diff -urN linux-2.4.20/drivers/char/drm.org/drm_ioctl.h linux-2.4.20/drivers/char/drm/drm_ioctl.h
--- linux-2.4.20/drivers/char/drm.org/drm_ioctl.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/drm_ioctl.h 2002-12-12 12:27:37.000000000 +0100
@@ -111,7 +111,7 @@
do {
struct pci_dev *pci_dev;
- int b, d, f;
+ int domain, b, d, f;
char *p;
for(p = dev->unique; p && *p && *p != ':'; p++);
@@ -123,6 +123,27 @@
f = (int)simple_strtoul(p+1, &p, 10);
if (*p) break;
+ domain = b >> 8;
+ b &= 0xff;
+
+#ifdef __alpha__
+ /*
+ * Find the hose the device is on (the domain number is the
+ * hose index) and offset the bus by the root bus of that
+ * hose.
+ */
+ for(pci_dev = pci_find_device(PCI_ANY_ID,PCI_ANY_ID,NULL);
+ pci_dev;
+ pci_dev = pci_find_device(PCI_ANY_ID,PCI_ANY_ID,pci_dev)) {
+ struct pci_controller *hose = pci_dev->sysdata;
+
+ if (hose->index == domain) {
+ b += hose->bus->number;
+ break;
+ }
+ }
+#endif
+
pci_dev = pci_find_slot(b, PCI_DEVFN(d,f));
if (pci_dev) {
dev->pdev = pci_dev;
diff -urN linux-2.4.20/drivers/char/drm.org/drmP.h linux-2.4.20/drivers/char/drm/drmP.h
--- linux-2.4.20/drivers/char/drm.org/drmP.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/drmP.h 2002-12-12 12:27:37.000000000 +0100
@@ -72,10 +72,7 @@
#include <asm/pgalloc.h>
#include "drm.h"
-/* page_to_bus for earlier kernels, not optimal in all cases */
-#ifndef page_to_bus
-#define page_to_bus(page) ((unsigned int)(virt_to_bus(page_address(page))))
-#endif
+#include "drm_os_linux.h"
/* DRM template customization defaults
*/
@@ -210,6 +207,7 @@
(unsigned long)(n),sizeof(*(ptr))))
#endif /* i386 & alpha */
#endif
+#define __REALLY_HAVE_SG (__HAVE_SG)
/* Begin the DRM...
*/
@@ -617,6 +615,17 @@
drm_map_t *map;
} drm_map_list_t;
+#if __HAVE_VBL_IRQ
+
+typedef struct drm_vbl_sig {
+ struct list_head head;
+ unsigned int sequence;
+ struct siginfo info;
+ struct task_struct *task;
+} drm_vbl_sig_t;
+
+#endif
+
typedef struct drm_device {
const char *name; /* Simple driver name */
char *unique; /* Unique identifier: e.g., busid */
@@ -679,6 +688,8 @@
#if __HAVE_VBL_IRQ
wait_queue_head_t vbl_queue;
atomic_t vbl_received;
+ spinlock_t vbl_lock;
+ drm_vbl_sig_t vbl_sigs;
#endif
cycles_t ctx_start;
cycles_t lck_start;
@@ -915,6 +926,7 @@
extern int DRM(wait_vblank)(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
extern int DRM(vblank_wait)(drm_device_t *dev, unsigned int *vbl_seq);
+extern void DRM(vbl_send_signals)( drm_device_t *dev );
#endif
#if __HAVE_DMA_IRQ_BH
extern void DRM(dma_immediate_bh)( void *dev );
diff -urN linux-2.4.20/drivers/char/drm.org/drm_proc.h linux-2.4.20/drivers/char/drm/drm_proc.h
--- linux-2.4.20/drivers/char/drm.org/drm_proc.h 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/drm_proc.h 2002-12-12 12:27:37.000000000 +0100
@@ -147,10 +147,10 @@
*eof = 0;
if (dev->unique) {
- DRM_PROC_PRINT("%s 0x%x %s\n",
- dev->name, dev->device, dev->unique);
+ DRM_PROC_PRINT("%s 0x%lx %s\n",
+ dev->name, (long)dev->device, dev->unique);
} else {
- DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device);
+ DRM_PROC_PRINT("%s 0x%lx\n", dev->name, (long)dev->device);
}
if (len > request + offset) return request;
diff -urN linux-2.4.20/drivers/char/drm.org/gamma_dma.c linux-2.4.20/drivers/char/drm/gamma_dma.c
--- linux-2.4.20/drivers/char/drm.org/gamma_dma.c 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/gamma_dma.c 2002-12-12 12:27:37.000000000 +0100
@@ -521,11 +521,11 @@
}
}
if (retcode) {
- DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n",
+ DRM_ERROR("ctx%d w%d p%d c%ld i%d l%d %d/%d\n",
d->context,
last_buf->waiting,
last_buf->pending,
- DRM_WAITCOUNT(dev, d->context),
+ (long)DRM_WAITCOUNT(dev, d->context),
last_buf->idx,
last_buf->list,
last_buf->pid,
@@ -591,7 +591,7 @@
drm_buf_t *buf;
int i;
struct list_head *list;
- unsigned int *pgt;
+ unsigned long *pgt;
DRM_DEBUG( "%s\n", __FUNCTION__ );
diff -urN linux-2.4.20/drivers/char/drm.org/i830_dma.c linux-2.4.20/drivers/char/drm/i830_dma.c
--- linux-2.4.20/drivers/char/drm.org/i830_dma.c 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/i830_dma.c 2002-12-12 12:27:37.000000000 +0100
@@ -38,6 +38,7 @@
#include "i830_drm.h"
#include "i830_drv.h"
#include <linux/interrupt.h> /* For task queue support */
+#include <linux/pagemap.h> /* For FASTCALL on unlock_page() */
#include <linux/delay.h>
#define DO_MUNMAP(m, a, l) do_munmap(m, a, l, 1)
@@ -49,7 +50,6 @@
#define I830_BUF_UNMAPPED 0
#define I830_BUF_MAPPED 1
-#define RING_LOCALS unsigned int outring, ringmask; volatile char *virt;
@@ -63,33 +63,6 @@
-#define I830_VERBOSE 0
-
-#define BEGIN_LP_RING(n) do { \
- if (I830_VERBOSE) \
- printk("BEGIN_LP_RING(%d) in %s\n", \
- n, __FUNCTION__); \
- if (dev_priv->ring.space < n*4) \
- i830_wait_ring(dev, n*4); \
- dev_priv->ring.space -= n*4; \
- outring = dev_priv->ring.tail; \
- ringmask = dev_priv->ring.tail_mask; \
- virt = dev_priv->ring.virtual_start; \
-} while (0)
-
-#define ADVANCE_LP_RING() do { \
- if (I830_VERBOSE) printk("ADVANCE_LP_RING %x\n", outring); \
- dev_priv->ring.tail = outring; \
- I830_WRITE(LP_RING + RING_TAIL, outring); \
-} while(0)
-
-#define OUT_RING(n) do { \
- if (I830_VERBOSE) printk(" OUT_RING %x\n", (int)(n)); \
- *(volatile unsigned int *)(virt + outring) = n; \
- outring += 4; \
- outring &= ringmask; \
-} while (0)
-
static inline void i830_print_status_page(drm_device_t *dev)
{
drm_device_dma_t *dma = dev->dma;
@@ -248,7 +221,7 @@
buf = i830_freelist_get(dev);
if (!buf) {
retcode = -ENOMEM;
- DRM_ERROR("retcode=%d\n", retcode);
+ DRM_DEBUG("retcode=%d\n", retcode);
return retcode;
}
@@ -282,12 +255,21 @@
dev_priv->ring.Size);
}
if(dev_priv->hw_status_page != 0UL) {
- pci_free_consistent(dev->pdev, PAGE_SIZE,
+ pci_free_consistent(dev->pdev, PAGE_SIZE,
(void *)dev_priv->hw_status_page,
dev_priv->dma_status_page);
/* Need to rewrite hardware status page */
I830_WRITE(0x02080, 0x1ffff000);
}
+
+ /* Disable interrupts here because after dev_private
+ * is freed, it's too late.
+ */
+ if (dev->irq) {
+ I830_WRITE16( I830REG_INT_MASK_R, 0xffff );
+ I830_WRITE16( I830REG_INT_ENABLE_R, 0x0 );
+ }
+
DRM(free)(dev->dev_private, sizeof(drm_i830_private_t),
DRM_MEM_DRIVER);
dev->dev_private = NULL;
@@ -301,7 +283,7 @@
return 0;
}
-static int i830_wait_ring(drm_device_t *dev, int n)
+int i830_wait_ring(drm_device_t *dev, int n, const char *caller)
{
drm_i830_private_t *dev_priv = dev->dev_private;
drm_i830_ring_buffer_t *ring = &(dev_priv->ring);
@@ -327,6 +309,7 @@
goto out_wait_ring;
}
udelay(1);
+ dev_priv->sarea_priv->perf_boxes |= I830_BOX_WAIT;
}
out_wait_ring:
@@ -342,6 +325,9 @@
ring->tail = I830_READ(LP_RING + RING_TAIL) & TAIL_ADDR;
ring->space = ring->head - (ring->tail+8);
if (ring->space < 0) ring->space += ring->Size;
+
+ if (ring->head == ring->tail)
+ dev_priv->sarea_priv->perf_boxes |= I830_BOX_RING_EMPTY;
}
static int i830_freelist_init(drm_device_t *dev, drm_i830_private_t *dev_priv)
@@ -456,6 +442,8 @@
dev_priv->back_pitch = init->back_pitch;
dev_priv->depth_pitch = init->depth_pitch;
+ dev_priv->do_boxes = 0;
+ dev_priv->use_mi_batchbuffer_start = 0;
/* Program Hardware Status Page */
dev_priv->hw_status_page =
@@ -531,11 +519,7 @@
unsigned int tmp;
RING_LOCALS;
- BEGIN_LP_RING( I830_CTX_SETUP_SIZE + 2 );
-
- OUT_RING( GFX_OP_STIPPLE );
- OUT_RING( 0 );
-
+ BEGIN_LP_RING( I830_CTX_SETUP_SIZE + 4 );
for ( i = 0 ; i < I830_CTXREG_BLENDCOLR0 ; i++ ) {
tmp = code[i];
@@ -573,38 +557,44 @@
ADVANCE_LP_RING();
}
-static void i830EmitTexVerified( drm_device_t *dev,
- volatile unsigned int *code )
+static void i830EmitTexVerified( drm_device_t *dev, unsigned int *code )
{
drm_i830_private_t *dev_priv = dev->dev_private;
int i, j = 0;
unsigned int tmp;
RING_LOCALS;
- BEGIN_LP_RING( I830_TEX_SETUP_SIZE );
-
- OUT_RING( GFX_OP_MAP_INFO );
- OUT_RING( code[I830_TEXREG_MI1] );
- OUT_RING( code[I830_TEXREG_MI2] );
- OUT_RING( code[I830_TEXREG_MI3] );
- OUT_RING( code[I830_TEXREG_MI4] );
- OUT_RING( code[I830_TEXREG_MI5] );
-
- for ( i = 6 ; i < I830_TEX_SETUP_SIZE ; i++ ) {
- tmp = code[i];
- OUT_RING( tmp );
- j++;
- }
+ if (code[I830_TEXREG_MI0] == GFX_OP_MAP_INFO ||
+ (code[I830_TEXREG_MI0] & ~(0xf*LOAD_TEXTURE_MAP0)) ==
+ (STATE3D_LOAD_STATE_IMMEDIATE_2|4)) {
+
+ BEGIN_LP_RING( I830_TEX_SETUP_SIZE );
+
+ OUT_RING( code[I830_TEXREG_MI0] ); /* TM0LI */
+ OUT_RING( code[I830_TEXREG_MI1] ); /* TM0S0 */
+ OUT_RING( code[I830_TEXREG_MI2] ); /* TM0S1 */
+ OUT_RING( code[I830_TEXREG_MI3] ); /* TM0S2 */
+ OUT_RING( code[I830_TEXREG_MI4] ); /* TM0S3 */
+ OUT_RING( code[I830_TEXREG_MI5] ); /* TM0S4 */
+
+ for ( i = 6 ; i < I830_TEX_SETUP_SIZE ; i++ ) {
+ tmp = code[i];
+ OUT_RING( tmp );
+ j++;
+ }
- if (j & 1)
- OUT_RING( 0 );
+ if (j & 1)
+ OUT_RING( 0 );
- ADVANCE_LP_RING();
+ ADVANCE_LP_RING();
+ }
+ else
+ printk("rejected packet %x\n", code[0]);
}
static void i830EmitTexBlendVerified( drm_device_t *dev,
- volatile unsigned int *code,
- volatile unsigned int num)
+ unsigned int *code,
+ unsigned int num)
{
drm_i830_private_t *dev_priv = dev->dev_private;
int i, j = 0;
@@ -614,7 +604,7 @@
if (!num)
return;
- BEGIN_LP_RING( num );
+ BEGIN_LP_RING( num + 1 );
for ( i = 0 ; i < num ; i++ ) {
tmp = code[i];
@@ -637,6 +627,8 @@
int i;
RING_LOCALS;
+ return; /* Is this right ? -- Arjan */
+
BEGIN_LP_RING( 258 );
if(is_shared == 1) {
@@ -650,42 +642,41 @@
OUT_RING(palette[i]);
}
OUT_RING(0);
+ /* KW: WHERE IS THE ADVANCE_LP_RING? This is effectively a noop!
+ */
}
/* Need to do some additional checking when setting the dest buffer.
*/
static void i830EmitDestVerified( drm_device_t *dev,
- volatile unsigned int *code )
+ unsigned int *code )
{
drm_i830_private_t *dev_priv = dev->dev_private;
unsigned int tmp;
RING_LOCALS;
- BEGIN_LP_RING( I830_DEST_SETUP_SIZE + 6 );
+ BEGIN_LP_RING( I830_DEST_SETUP_SIZE + 10 );
+
tmp = code[I830_DESTREG_CBUFADDR];
- if (tmp == dev_priv->front_di1) {
- /* Don't use fence when front buffer rendering */
- OUT_RING( CMD_OP_DESTBUFFER_INFO );
- OUT_RING( BUF_3D_ID_COLOR_BACK |
- BUF_3D_PITCH(dev_priv->back_pitch * dev_priv->cpp) );
- OUT_RING( tmp );
+ if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) {
+ if (((int)outring) & 8) {
+ OUT_RING(0);
+ OUT_RING(0);
+ }
OUT_RING( CMD_OP_DESTBUFFER_INFO );
- OUT_RING( BUF_3D_ID_DEPTH |
- BUF_3D_PITCH(dev_priv->depth_pitch * dev_priv->cpp));
- OUT_RING( dev_priv->zi1 );
- } else if(tmp == dev_priv->back_di1) {
- OUT_RING( CMD_OP_DESTBUFFER_INFO );
OUT_RING( BUF_3D_ID_COLOR_BACK |
BUF_3D_PITCH(dev_priv->back_pitch * dev_priv->cpp) |
BUF_3D_USE_FENCE);
OUT_RING( tmp );
+ OUT_RING( 0 );
OUT_RING( CMD_OP_DESTBUFFER_INFO );
OUT_RING( BUF_3D_ID_DEPTH | BUF_3D_USE_FENCE |
BUF_3D_PITCH(dev_priv->depth_pitch * dev_priv->cpp));
OUT_RING( dev_priv->zi1 );
+ OUT_RING( 0 );
} else {
DRM_ERROR("bad di1 %x (allow %x or %x)\n",
tmp, dev_priv->front_di1, dev_priv->back_di1);
@@ -713,21 +704,35 @@
OUT_RING( 0 );
}
- OUT_RING( code[I830_DESTREG_SENABLE] );
-
OUT_RING( GFX_OP_SCISSOR_RECT );
OUT_RING( code[I830_DESTREG_SR1] );
OUT_RING( code[I830_DESTREG_SR2] );
+ OUT_RING( 0 );
ADVANCE_LP_RING();
}
+static void i830EmitStippleVerified( drm_device_t *dev,
+ unsigned int *code )
+{
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ RING_LOCALS;
+
+ BEGIN_LP_RING( 2 );
+ OUT_RING( GFX_OP_STIPPLE );
+ OUT_RING( code[1] );
+ ADVANCE_LP_RING();
+}
+
+
static void i830EmitState( drm_device_t *dev )
{
drm_i830_private_t *dev_priv = dev->dev_private;
drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv;
unsigned int dirty = sarea_priv->dirty;
+ DRM_DEBUG("%s %x\n", __FUNCTION__, dirty);
+
if (dirty & I830_UPLOAD_BUFFERS) {
i830EmitDestVerified( dev, sarea_priv->BufferState );
sarea_priv->dirty &= ~I830_UPLOAD_BUFFERS;
@@ -761,17 +766,154 @@
}
if (dirty & I830_UPLOAD_TEX_PALETTE_SHARED) {
- i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 1);
+ i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 1);
+ } else {
+ if (dirty & I830_UPLOAD_TEX_PALETTE_N(0)) {
+ i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 0);
+ sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(0);
+ }
+ if (dirty & I830_UPLOAD_TEX_PALETTE_N(1)) {
+ i830EmitTexPalette(dev, sarea_priv->Palette[1], 1, 0);
+ sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(1);
+ }
+
+ /* 1.3:
+ */
+#if 0
+ if (dirty & I830_UPLOAD_TEX_PALETTE_N(2)) {
+ i830EmitTexPalette(dev, sarea_priv->Palette2[0], 0, 0);
+ sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(2);
+ }
+ if (dirty & I830_UPLOAD_TEX_PALETTE_N(3)) {
+ i830EmitTexPalette(dev, sarea_priv->Palette2[1], 1, 0);
+ sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(2);
+ }
+#endif
+ }
+
+ /* 1.3:
+ */
+ if (dirty & I830_UPLOAD_STIPPLE) {
+ i830EmitStippleVerified( dev,
+ sarea_priv->StippleState);
+ sarea_priv->dirty &= ~I830_UPLOAD_STIPPLE;
+ }
+
+ if (dirty & I830_UPLOAD_TEX2) {
+ i830EmitTexVerified( dev, sarea_priv->TexState2 );
+ sarea_priv->dirty &= ~I830_UPLOAD_TEX2;
+ }
+
+ if (dirty & I830_UPLOAD_TEX3) {
+ i830EmitTexVerified( dev, sarea_priv->TexState3 );
+ sarea_priv->dirty &= ~I830_UPLOAD_TEX3;
+ }
+
+
+ if (dirty & I830_UPLOAD_TEXBLEND2) {
+ i830EmitTexBlendVerified(
+ dev,
+ sarea_priv->TexBlendState2,
+ sarea_priv->TexBlendStateWordsUsed2);
+
+ sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND2;
+ }
+
+ if (dirty & I830_UPLOAD_TEXBLEND3) {
+ i830EmitTexBlendVerified(
+ dev,
+ sarea_priv->TexBlendState3,
+ sarea_priv->TexBlendStateWordsUsed3);
+ sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND3;
+ }
+}
+
+/* ================================================================
+ * Performance monitoring functions
+ */
+
+static void i830_fill_box( drm_device_t *dev,
+ int x, int y, int w, int h,
+ int r, int g, int b )
+{
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ u32 color;
+ unsigned int BR13, CMD;
+ RING_LOCALS;
+
+ BR13 = (0xF0 << 16) | (dev_priv->pitch * dev_priv->cpp) | (1<<24);
+ CMD = XY_COLOR_BLT_CMD;
+ x += dev_priv->sarea_priv->boxes[0].x1;
+ y += dev_priv->sarea_priv->boxes[0].y1;
+
+ if (dev_priv->cpp == 4) {
+ BR13 |= (1<<25);
+ CMD |= (XY_COLOR_BLT_WRITE_ALPHA | XY_COLOR_BLT_WRITE_RGB);
+ color = (((0xff) << 24) | (r << 16) | (g << 8) | b);
} else {
- if (dirty & I830_UPLOAD_TEX_PALETTE_N(0)) {
- i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 0);
- sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(0);
- }
- if (dirty & I830_UPLOAD_TEX_PALETTE_N(1)) {
- i830EmitTexPalette(dev, sarea_priv->Palette[1], 1, 0);
- sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(1);
- }
+ color = (((r & 0xf8) << 8) |
+ ((g & 0xfc) << 3) |
+ ((b & 0xf8) >> 3));
}
+
+ BEGIN_LP_RING( 6 );
+ OUT_RING( CMD );
+ OUT_RING( BR13 );
+ OUT_RING( (y << 16) | x );
+ OUT_RING( ((y+h) << 16) | (x+w) );
+
+ if ( dev_priv->current_page == 1 ) {
+ OUT_RING( dev_priv->front_offset );
+ } else {
+ OUT_RING( dev_priv->back_offset );
+ }
+
+ OUT_RING( color );
+ ADVANCE_LP_RING();
+}
+
+static void i830_cp_performance_boxes( drm_device_t *dev )
+{
+ drm_i830_private_t *dev_priv = dev->dev_private;
+
+ /* Purple box for page flipping
+ */
+ if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_FLIP )
+ i830_fill_box( dev, 4, 4, 8, 8, 255, 0, 255 );
+
+ /* Red box if we have to wait for idle at any point
+ */
+ if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_WAIT )
+ i830_fill_box( dev, 16, 4, 8, 8, 255, 0, 0 );
+
+ /* Blue box: lost context?
+ */
+ if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_LOST_CONTEXT )
+ i830_fill_box( dev, 28, 4, 8, 8, 0, 0, 255 );
+
+ /* Yellow box for texture swaps
+ */
+ if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_TEXTURE_LOAD )
+ i830_fill_box( dev, 40, 4, 8, 8, 255, 255, 0 );
+
+ /* Green box if hardware never idles (as far as we can tell)
+ */
+ if ( !(dev_priv->sarea_priv->perf_boxes & I830_BOX_RING_EMPTY) )
+ i830_fill_box( dev, 64, 4, 8, 8, 0, 255, 0 );
+
+
+ /* Draw bars indicating number of buffers allocated
+ * (not a great measure, easily confused)
+ */
+ if (dev_priv->dma_used) {
+ int bar = dev_priv->dma_used / 10240;
+ if (bar > 100) bar = 100;
+ if (bar < 1) bar = 1;
+ i830_fill_box( dev, 4, 16, bar, 4, 196, 128, 128 );
+ dev_priv->dma_used = 0;
+ }
+
+ dev_priv->sarea_priv->perf_boxes = 0;
}
static void i830_dma_dispatch_clear( drm_device_t *dev, int flags,
@@ -789,6 +931,15 @@
unsigned int BR13, CMD, D_CMD;
RING_LOCALS;
+
+ if ( dev_priv->current_page == 1 ) {
+ unsigned int tmp = flags;
+
+ flags &= ~(I830_FRONT | I830_BACK);
+ if ( tmp & I830_FRONT ) flags |= I830_BACK;
+ if ( tmp & I830_BACK ) flags |= I830_FRONT;
+ }
+
i830_kernel_lost_context(dev);
switch(cpp) {
@@ -868,13 +1019,17 @@
drm_clip_rect_t *pbox = sarea_priv->boxes;
int pitch = dev_priv->pitch;
int cpp = dev_priv->cpp;
- int ofs = dev_priv->back_offset;
int i;
unsigned int CMD, BR13;
RING_LOCALS;
DRM_DEBUG("swapbuffers\n");
+ i830_kernel_lost_context(dev);
+
+ if (dev_priv->do_boxes)
+ i830_cp_performance_boxes( dev );
+
switch(cpp) {
case 2:
BR13 = (pitch * cpp) | (0xCC << 16) | (1<<24);
@@ -891,7 +1046,6 @@
break;
}
- i830_kernel_lost_context(dev);
if (nbox > I830_NR_SAREA_CLIPRECTS)
nbox = I830_NR_SAREA_CLIPRECTS;
@@ -911,23 +1065,72 @@
BEGIN_LP_RING( 8 );
OUT_RING( CMD );
OUT_RING( BR13 );
+ OUT_RING( (pbox->y1 << 16) | pbox->x1 );
+ OUT_RING( (pbox->y2 << 16) | pbox->x2 );
- OUT_RING( (pbox->y1 << 16) |
- pbox->x1 );
- OUT_RING( (pbox->y2 << 16) |
- pbox->x2 );
-
- OUT_RING( dev_priv->front_offset );
- OUT_RING( (pbox->y1 << 16) |
- pbox->x1 );
+ if (dev_priv->current_page == 0)
+ OUT_RING( dev_priv->front_offset );
+ else
+ OUT_RING( dev_priv->back_offset );
+ OUT_RING( (pbox->y1 << 16) | pbox->x1 );
OUT_RING( BR13 & 0xffff );
- OUT_RING( ofs );
+
+ if (dev_priv->current_page == 0)
+ OUT_RING( dev_priv->back_offset );
+ else
+ OUT_RING( dev_priv->front_offset );
ADVANCE_LP_RING();
}
}
+static void i830_dma_dispatch_flip( drm_device_t *dev )
+{
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ RING_LOCALS;
+
+ DRM_DEBUG( "%s: page=%d pfCurrentPage=%d\n",
+ __FUNCTION__,
+ dev_priv->current_page,
+ dev_priv->sarea_priv->pf_current_page);
+
+ i830_kernel_lost_context(dev);
+
+ if (dev_priv->do_boxes) {
+ dev_priv->sarea_priv->perf_boxes |= I830_BOX_FLIP;
+ i830_cp_performance_boxes( dev );
+ }
+
+
+ BEGIN_LP_RING( 2 );
+ OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+
+ BEGIN_LP_RING( 6 );
+ OUT_RING( CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP );
+ OUT_RING( 0 );
+ if ( dev_priv->current_page == 0 ) {
+ OUT_RING( dev_priv->back_offset );
+ dev_priv->current_page = 1;
+ } else {
+ OUT_RING( dev_priv->front_offset );
+ dev_priv->current_page = 0;
+ }
+ OUT_RING(0);
+ ADVANCE_LP_RING();
+
+
+ BEGIN_LP_RING( 2 );
+ OUT_RING( MI_WAIT_FOR_EVENT |
+ MI_WAIT_FOR_PLANE_A_FLIP );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+
+
+ dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
+}
static void i830_dma_dispatch_vertex(drm_device_t *dev,
drm_buf_t *buf,
@@ -980,8 +1183,10 @@
sarea_priv->vertex_prim |
((used/4)-2));
- vp[used/4] = MI_BATCH_BUFFER_END;
- used += 4;
+ if (dev_priv->use_mi_batchbuffer_start) {
+ vp[used/4] = MI_BATCH_BUFFER_END;
+ used += 4;
+ }
if (used & 4) {
vp[used/4] = 0;
@@ -1004,11 +1209,21 @@
ADVANCE_LP_RING();
}
- BEGIN_LP_RING(2);
- OUT_RING( MI_BATCH_BUFFER_START | (2<<6) );
- OUT_RING( start | MI_BATCH_NON_SECURE );
- ADVANCE_LP_RING();
-
+ if (dev_priv->use_mi_batchbuffer_start) {
+ BEGIN_LP_RING(2);
+ OUT_RING( MI_BATCH_BUFFER_START | (2<<6) );
+ OUT_RING( start | MI_BATCH_NON_SECURE );
+ ADVANCE_LP_RING();
+ }
+ else {
+ BEGIN_LP_RING(4);
+ OUT_RING( MI_BATCH_BUFFER );
+ OUT_RING( start | MI_BATCH_NON_SECURE );
+ OUT_RING( start + used - 4 );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+ }
+
} while (++i < nbox);
}
@@ -1046,7 +1261,7 @@
OUT_RING( 0 );
ADVANCE_LP_RING();
- i830_wait_ring( dev, dev_priv->ring.Size - 8 );
+ i830_wait_ring( dev, dev_priv->ring.Size - 8, __FUNCTION__ );
}
static int i830_flush_queue(drm_device_t *dev)
@@ -1063,7 +1278,7 @@
OUT_RING( 0 );
ADVANCE_LP_RING();
- i830_wait_ring( dev, dev_priv->ring.Size - 8 );
+ i830_wait_ring( dev, dev_priv->ring.Size - 8, __FUNCTION__ );
for (i = 0; i < dma->buf_count; i++) {
drm_buf_t *buf = dma->buflist[ i ];
@@ -1203,6 +1418,53 @@
return 0;
}
+
+
+/* Not sure why this isn't set all the time:
+ */
+static void i830_do_init_pageflip( drm_device_t *dev )
+{
+ drm_i830_private_t *dev_priv = dev->dev_private;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ dev_priv->page_flipping = 1;
+ dev_priv->current_page = 0;
+ dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
+}
+
+int i830_do_cleanup_pageflip( drm_device_t *dev )
+{
+ drm_i830_private_t *dev_priv = dev->dev_private;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ if (dev_priv->current_page != 0)
+ i830_dma_dispatch_flip( dev );
+
+ dev_priv->page_flipping = 0;
+ return 0;
+}
+
+int i830_flip_bufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ drm_i830_private_t *dev_priv = dev->dev_private;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i830_flip_buf called without lock held\n");
+ return -EINVAL;
+ }
+
+ if (!dev_priv->page_flipping)
+ i830_do_init_pageflip( dev );
+
+ i830_dma_dispatch_flip( dev );
+ return 0;
+}
+
int i830_getage(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long arg)
{
@@ -1266,3 +1528,66 @@
{
return 0;
}
+
+
+
+int i830_getparam( struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg )
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ drm_i830_getparam_t param;
+ int value;
+
+ if ( !dev_priv ) {
+ DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
+ return -EINVAL;
+ }
+
+ if (copy_from_user(¶m, (drm_i830_getparam_t *)arg, sizeof(param) ))
+ return -EFAULT;
+
+ switch( param.param ) {
+ case I830_PARAM_IRQ_ACTIVE:
+ value = dev->irq ? 1 : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ( copy_to_user( param.value, &value, sizeof(int) ) ) {
+ DRM_ERROR( "copy_to_user\n" );
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+
+int i830_setparam( struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg )
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ drm_i830_setparam_t param;
+
+ if ( !dev_priv ) {
+ DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
+ return -EINVAL;
+ }
+
+ if (copy_from_user(¶m, (drm_i830_setparam_t *)arg, sizeof(param) ))
+ return -EFAULT;
+
+ switch( param.param ) {
+ case I830_SETPARAM_USE_MI_BATCHBUFFER_START:
+ dev_priv->use_mi_batchbuffer_start = param.value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff -urN linux-2.4.20/drivers/char/drm.org/i830_drm.h linux-2.4.20/drivers/char/drm/i830_drm.h
--- linux-2.4.20/drivers/char/drm.org/i830_drm.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/i830_drm.h 2002-12-12 12:27:37.000000000 +0100
@@ -3,6 +3,9 @@
/* WARNING: These defines must be the same as what the Xserver uses.
* if you change them, you must change the defines in the Xserver.
+ *
+ * KW: Actually, you can't ever change them because doing so would
+ * break backwards compatibility.
*/
#ifndef _I830_DEFINES_
@@ -18,14 +21,12 @@
#define I830_NR_TEX_REGIONS 64
#define I830_LOG_MIN_TEX_REGION_SIZE 16
-/* if defining I830_ENABLE_4_TEXTURES, do it in i830_3d_reg.h, too */
-#if !defined(I830_ENABLE_4_TEXTURES)
+/* KW: These aren't correct but someone set them to two and then
+ * released the module. Now we can't change them as doing so would
+ * break backwards compatibility.
+ */
#define I830_TEXTURE_COUNT 2
-#define I830_TEXBLEND_COUNT 2 /* always same as TEXTURE_COUNT? */
-#else /* defined(I830_ENABLE_4_TEXTURES) */
-#define I830_TEXTURE_COUNT 4
-#define I830_TEXBLEND_COUNT 4 /* always same as TEXTURE_COUNT? */
-#endif /* I830_ENABLE_4_TEXTURES */
+#define I830_TEXBLEND_COUNT I830_TEXTURE_COUNT
#define I830_TEXBLEND_SIZE 12 /* (4 args + op) * 2 + COLOR_FACTOR */
@@ -57,6 +58,7 @@
#define I830_UPLOAD_TEXBLEND_MASK 0xf00000
#define I830_UPLOAD_TEX_PALETTE_N(n) (0x1000000 << (n))
#define I830_UPLOAD_TEX_PALETTE_SHARED 0x4000000
+#define I830_UPLOAD_STIPPLE 0x8000000
/* Indices into buf.Setup where various bits of state are mirrored per
* context and per buffer. These can be fired at the card as a unit,
@@ -73,7 +75,6 @@
*/
#define I830_DESTREG_CBUFADDR 0
-/* Invarient */
#define I830_DESTREG_DBUFADDR 1
#define I830_DESTREG_DV0 2
#define I830_DESTREG_DV1 3
@@ -109,6 +110,13 @@
#define I830_CTXREG_MCSB1 16
#define I830_CTX_SETUP_SIZE 17
+/* 1.3: Stipple state
+ */
+#define I830_STPREG_ST0 0
+#define I830_STPREG_ST1 1
+#define I830_STP_SETUP_SIZE 2
+
+
/* Texture state (per tex unit)
*/
@@ -124,6 +132,18 @@
#define I830_TEXREG_MCS 9 /* GFX_OP_MAP_COORD_SETS */
#define I830_TEX_SETUP_SIZE 10
+#define I830_TEXREG_TM0LI 0 /* load immediate 2 texture map n */
+#define I830_TEXREG_TM0S0 1
+#define I830_TEXREG_TM0S1 2
+#define I830_TEXREG_TM0S2 3
+#define I830_TEXREG_TM0S3 4
+#define I830_TEXREG_TM0S4 5
+#define I830_TEXREG_NOP0 6 /* noop */
+#define I830_TEXREG_NOP1 7 /* noop */
+#define I830_TEXREG_NOP2 8 /* noop */
+#define __I830_TEXREG_MCS 9 /* GFX_OP_MAP_COORD_SETS -- shared */
+#define __I830_TEX_SETUP_SIZE 10
+
#define I830_FRONT 0x1
#define I830_BACK 0x2
#define I830_DEPTH 0x4
@@ -199,8 +219,35 @@
int ctxOwner; /* last context to upload state */
int vertex_prim;
+
+ int pf_enabled; /* is pageflipping allowed? */
+ int pf_active;
+ int pf_current_page; /* which buffer is being displayed? */
+
+ int perf_boxes; /* performance boxes to be displayed */
+
+ /* Here's the state for texunits 2,3:
+ */
+ unsigned int TexState2[I830_TEX_SETUP_SIZE];
+ unsigned int TexBlendState2[I830_TEXBLEND_SIZE];
+ unsigned int TexBlendStateWordsUsed2;
+
+ unsigned int TexState3[I830_TEX_SETUP_SIZE];
+ unsigned int TexBlendState3[I830_TEXBLEND_SIZE];
+ unsigned int TexBlendStateWordsUsed3;
+
+ unsigned int StippleState[I830_STP_SETUP_SIZE];
} drm_i830_sarea_t;
+/* Flags for perf_boxes
+ */
+#define I830_BOX_RING_EMPTY 0x1 /* populated by kernel */
+#define I830_BOX_FLIP 0x2 /* populated by kernel */
+#define I830_BOX_WAIT 0x4 /* populated by kernel & client */
+#define I830_BOX_TEXTURE_LOAD 0x8 /* populated by kernel */
+#define I830_BOX_LOST_CONTEXT 0x10 /* populated by client */
+
+
/* I830 specific ioctls
* The device specific ioctl range is 0x40 to 0x79.
*/
@@ -213,6 +260,11 @@
#define DRM_IOCTL_I830_SWAP DRM_IO ( 0x46)
#define DRM_IOCTL_I830_COPY DRM_IOW( 0x47, drm_i830_copy_t)
#define DRM_IOCTL_I830_DOCOPY DRM_IO ( 0x48)
+#define DRM_IOCTL_I830_FLIP DRM_IO ( 0x49)
+#define DRM_IOCTL_I830_IRQ_EMIT DRM_IOWR(0x4a, drm_i830_irq_emit_t)
+#define DRM_IOCTL_I830_IRQ_WAIT DRM_IOW( 0x4b, drm_i830_irq_wait_t)
+#define DRM_IOCTL_I830_GETPARAM DRM_IOWR(0x4c, drm_i830_getparam_t)
+#define DRM_IOCTL_I830_SETPARAM DRM_IOWR(0x4d, drm_i830_setparam_t)
typedef struct _drm_i830_clear {
int clear_color;
@@ -248,4 +300,36 @@
int granted;
} drm_i830_dma_t;
+
+/* 1.3: Userspace can request & wait on irq's:
+ */
+typedef struct drm_i830_irq_emit {
+ int *irq_seq;
+} drm_i830_irq_emit_t;
+
+typedef struct drm_i830_irq_wait {
+ int irq_seq;
+} drm_i830_irq_wait_t;
+
+
+/* 1.3: New ioctl to query kernel params:
+ */
+#define I830_PARAM_IRQ_ACTIVE 1
+
+typedef struct drm_i830_getparam {
+ int param;
+ int *value;
+} drm_i830_getparam_t;
+
+
+/* 1.3: New ioctl to set kernel params:
+ */
+#define I830_SETPARAM_USE_MI_BATCHBUFFER_START 1
+
+typedef struct drm_i830_setparam {
+ int param;
+ int value;
+} drm_i830_setparam_t;
+
+
#endif /* _I830_DRM_H_ */
diff -urN linux-2.4.20/drivers/char/drm.org/i830_drv.h linux-2.4.20/drivers/char/drm/i830_drv.h
--- linux-2.4.20/drivers/char/drm.org/i830_drv.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/i830_drv.h 2002-12-12 12:27:37.000000000 +0100
@@ -78,6 +78,19 @@
int back_pitch;
int depth_pitch;
unsigned int cpp;
+
+ int do_boxes;
+ int dma_used;
+
+ int current_page;
+ int page_flipping;
+
+ wait_queue_head_t irq_queue;
+ atomic_t irq_received;
+ atomic_t irq_emitted;
+
+ int use_mi_batchbuffer_start;
+
} drm_i830_private_t;
/* i830_dma.c */
@@ -108,6 +121,23 @@
extern int i830_clear_bufs(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
+extern int i830_flip_bufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+extern int i830_getparam( struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg );
+
+extern int i830_setparam( struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg );
+
+/* i830_irq.c */
+extern int i830_irq_emit( struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg );
+extern int i830_irq_wait( struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg );
+extern int i830_wait_irq(drm_device_t *dev, int irq_nr);
+extern int i830_emit_irq(drm_device_t *dev);
+
#define I830_BASE(reg) ((unsigned long) \
dev_priv->mmio_map->handle)
@@ -119,12 +149,53 @@
#define I830_READ16(reg) I830_DEREF16(reg)
#define I830_WRITE16(reg,val) do { I830_DEREF16(reg) = val; } while (0)
+
+
+#define I830_VERBOSE 0
+
+#define RING_LOCALS unsigned int outring, ringmask, outcount; \
+ volatile char *virt;
+
+#define BEGIN_LP_RING(n) do { \
+ if (I830_VERBOSE) \
+ printk("BEGIN_LP_RING(%d) in %s\n", \
+ n, __FUNCTION__); \
+ if (dev_priv->ring.space < n*4) \
+ i830_wait_ring(dev, n*4, __FUNCTION__); \
+ outcount = 0; \
+ outring = dev_priv->ring.tail; \
+ ringmask = dev_priv->ring.tail_mask; \
+ virt = dev_priv->ring.virtual_start; \
+} while (0)
+
+
+#define OUT_RING(n) do { \
+ if (I830_VERBOSE) printk(" OUT_RING %x\n", (int)(n)); \
+ *(volatile unsigned int *)(virt + outring) = n; \
+ outcount++; \
+ outring += 4; \
+ outring &= ringmask; \
+} while (0)
+
+#define ADVANCE_LP_RING() do { \
+ if (I830_VERBOSE) printk("ADVANCE_LP_RING %x\n", outring); \
+ dev_priv->ring.tail = outring; \
+ dev_priv->ring.space -= outcount * 4; \
+ I830_WRITE(LP_RING + RING_TAIL, outring); \
+} while(0)
+
+extern int i830_wait_ring(drm_device_t *dev, int n, const char *caller);
+
+
#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23))
#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23))
#define CMD_REPORT_HEAD (7<<23)
#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1)
#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1)
+#define STATE3D_LOAD_STATE_IMMEDIATE_2 ((0x3<<29)|(0x1d<<24)|(0x03<<16))
+#define LOAD_TEXTURE_MAP0 (1<<11)
+
#define INST_PARSER_CLIENT 0x00000000
#define INST_OP_FLUSH 0x02000000
#define INST_FLUSH_MAP_CACHE 0x00000001
@@ -140,6 +211,9 @@
#define I830REG_INT_MASK_R 0x020a8
#define I830REG_INT_ENABLE_R 0x020a0
+#define I830_IRQ_RESERVED ((1<<13)|(3<<2))
+
+
#define LP_RING 0x2030
#define HP_RING 0x2040
#define RING_TAIL 0x00
@@ -182,6 +256,9 @@
#define CMD_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1)
+#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2)
+#define ASYNC_FLIP (1<<22)
+
#define CMD_3D (0x3<<29)
#define STATE3D_CONST_BLEND_COLOR_CMD (CMD_3D|(0x1d<<24)|(0x88<<16))
#define STATE3D_MAP_COORD_SETBIND_CMD (CMD_3D|(0x1d<<24)|(0x02<<16))
@@ -213,6 +290,11 @@
#define MI_BATCH_BUFFER_END (0xA<<23)
#define MI_BATCH_NON_SECURE (1)
+#define MI_WAIT_FOR_EVENT ((0x3<<23))
+#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2)
+#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1)
+
+#define MI_LOAD_SCAN_LINES_INCL ((0x12<<23))
#endif
diff -urN linux-2.4.20/drivers/char/drm.org/i830.h linux-2.4.20/drivers/char/drm/i830.h
--- linux-2.4.20/drivers/char/drm.org/i830.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/i830.h 2002-12-12 12:27:37.000000000 +0100
@@ -45,22 +45,37 @@
#define DRIVER_NAME "i830"
#define DRIVER_DESC "Intel 830M"
-#define DRIVER_DATE "20020828"
+#define DRIVER_DATE "20021108"
+/* Interface history:
+ *
+ * 1.1: Original.
+ * 1.2: ?
+ * 1.3: New irq emit/wait ioctls.
+ * New pageflip ioctl.
+ * New getparam ioctl.
+ * State for texunits 3&4 in sarea.
+ * New (alternative) layout for texture state.
+ */
#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 2
-#define DRIVER_PATCHLEVEL 1
+#define DRIVER_MINOR 3
+#define DRIVER_PATCHLEVEL 2
#define DRIVER_IOCTLS \
[DRM_IOCTL_NR(DRM_IOCTL_I830_INIT)] = { i830_dma_init, 1, 1 }, \
- [DRM_IOCTL_NR(DRM_IOCTL_I830_VERTEX)] = { i830_dma_vertex, 1, 0 }, \
- [DRM_IOCTL_NR(DRM_IOCTL_I830_CLEAR)] = { i830_clear_bufs, 1, 0 }, \
- [DRM_IOCTL_NR(DRM_IOCTL_I830_FLUSH)] = { i830_flush_ioctl, 1, 0 }, \
- [DRM_IOCTL_NR(DRM_IOCTL_I830_GETAGE)] = { i830_getage, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_VERTEX)] = { i830_dma_vertex, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_CLEAR)] = { i830_clear_bufs, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_FLUSH)] = { i830_flush_ioctl, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_GETAGE)] = { i830_getage, 1, 0 }, \
[DRM_IOCTL_NR(DRM_IOCTL_I830_GETBUF)] = { i830_getbuf, 1, 0 }, \
- [DRM_IOCTL_NR(DRM_IOCTL_I830_SWAP)] = { i830_swap_bufs, 1, 0 }, \
- [DRM_IOCTL_NR(DRM_IOCTL_I830_COPY)] = { i830_copybuf, 1, 0 }, \
- [DRM_IOCTL_NR(DRM_IOCTL_I830_DOCOPY)] = { i830_docopy, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_SWAP)] = { i830_swap_bufs, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_COPY)] = { i830_copybuf, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_DOCOPY)] = { i830_docopy, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_FLIP)] = { i830_flip_bufs, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_IRQ_EMIT)] = { i830_irq_emit, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_IRQ_WAIT)] = { i830_irq_wait, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_GETPARAM)] = { i830_getparam, 1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_I830_SETPARAM)] = { i830_setparam, 1, 0 }
#define __HAVE_COUNTERS 4
#define __HAVE_COUNTER6 _DRM_STAT_IRQ
@@ -87,10 +102,49 @@
i830_dma_quiescent( dev ); \
} while (0)
-/* Don't need an irq any more. The template code will make sure that
- * a noop stub is generated for compatibility.
+
+/* Driver will work either way: IRQ's save cpu time when waiting for
+ * the card, but are subject to subtle interactions between bios,
+ * hardware and the driver.
+ */
+#define USE_IRQS 0
+
+
+#if USE_IRQS
+#define __HAVE_DMA_IRQ 1
+#define __HAVE_SHARED_IRQ 1
+
+#define DRIVER_PREINSTALL() do { \
+ drm_i830_private_t *dev_priv = \
+ (drm_i830_private_t *)dev->dev_private; \
+ \
+ I830_WRITE16( I830REG_HWSTAM, 0xffff ); \
+ I830_WRITE16( I830REG_INT_MASK_R, 0x0 ); \
+ I830_WRITE16( I830REG_INT_ENABLE_R, 0x0 ); \
+} while (0)
+
+
+#define DRIVER_POSTINSTALL() do { \
+ drm_i830_private_t *dev_priv = \
+ (drm_i830_private_t *)dev->dev_private; \
+ I830_WRITE16( I830REG_INT_ENABLE_R, 0x2 ); \
+ atomic_set(&dev_priv->irq_received, 0); \
+ atomic_set(&dev_priv->irq_emitted, 0); \
+ init_waitqueue_head(&dev_priv->irq_queue); \
+} while (0)
+
+
+/* This gets called too late to be useful: dev_priv has already been
+ * freed.
*/
-#define __HAVE_DMA_IRQ 0
+#define DRIVER_UNINSTALL() do { \
+} while (0)
+
+#else
+#define __HAVE_DMA_IRQ 0
+#endif
+
+
/* Buffer customization:
*/
diff -urN linux-2.4.20/drivers/char/drm.org/i830_irq.c linux-2.4.20/drivers/char/drm/i830_irq.c
--- linux-2.4.20/drivers/char/drm.org/i830_irq.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/i830_irq.c 2002-12-12 12:27:37.000000000 +0100
@@ -0,0 +1,178 @@
+/* i830_dma.c -- DMA support for the I830 -*- linux-c -*-
+ *
+ * Copyright 2002 Tungsten Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Keith Whitwell <keith@tungstengraphics.com>
+ *
+ */
+
+
+#include "i830.h"
+#include "drmP.h"
+#include "drm.h"
+#include "i830_drm.h"
+#include "i830_drv.h"
+#include <linux/interrupt.h> /* For task queue support */
+#include <linux/delay.h>
+
+
+void DRM(dma_service)(int irq, void *device, struct pt_regs *regs)
+{
+ drm_device_t *dev = (drm_device_t *)device;
+ drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private;
+ u16 temp;
+
+ temp = I830_READ16(I830REG_INT_IDENTITY_R);
+ printk("%s: %x\n", __FUNCTION__, temp);
+
+ if(temp == 0)
+ return;
+
+ I830_WRITE16(I830REG_INT_IDENTITY_R, temp);
+
+ if (temp & 2) {
+ atomic_inc(&dev_priv->irq_received);
+ wake_up_interruptible(&dev_priv->irq_queue);
+ }
+}
+
+
+int i830_emit_irq(drm_device_t *dev)
+{
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ RING_LOCALS;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ atomic_inc(&dev_priv->irq_emitted);
+
+ BEGIN_LP_RING(2);
+ OUT_RING( 0 );
+ OUT_RING( GFX_OP_USER_INTERRUPT );
+ ADVANCE_LP_RING();
+
+ return atomic_read(&dev_priv->irq_emitted);
+}
+
+
+int i830_wait_irq(drm_device_t *dev, int irq_nr)
+{
+ drm_i830_private_t *dev_priv =
+ (drm_i830_private_t *)dev->dev_private;
+ DECLARE_WAITQUEUE(entry, current);
+ unsigned long end = jiffies + HZ*3;
+ int ret = 0;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (atomic_read(&dev_priv->irq_received) >= irq_nr)
+ return 0;
+
+ dev_priv->sarea_priv->perf_boxes |= I830_BOX_WAIT;
+
+ add_wait_queue(&dev_priv->irq_queue, &entry);
+
+ for (;;) {
+ current->state = TASK_INTERRUPTIBLE;
+ if (atomic_read(&dev_priv->irq_received) >= irq_nr)
+ break;
+ if (time_after(jiffies, end)) {
+ DRM_ERROR("timeout iir %x imr %x ier %x hwstam %x\n",
+ I830_READ16( I830REG_INT_IDENTITY_R ),
+ I830_READ16( I830REG_INT_MASK_R ),
+ I830_READ16( I830REG_INT_ENABLE_R ),
+ I830_READ16( I830REG_HWSTAM ));
+
+ ret = -EBUSY; /* Lockup? Missed irq? */
+ break;
+ }
+ schedule_timeout(HZ*3);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev_priv->irq_queue, &entry);
+ return ret;
+}
+
+
+/* Needs the lock as it touches the ring.
+ */
+int i830_irq_emit( struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg )
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ drm_i830_irq_emit_t emit;
+ int result;
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i830_irq_emit called without lock held\n");
+ return -EINVAL;
+ }
+
+ if ( !dev_priv ) {
+ DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
+ return -EINVAL;
+ }
+
+ if (copy_from_user( &emit, (drm_i830_irq_emit_t *)arg, sizeof(emit) ))
+ return -EFAULT;
+
+ result = i830_emit_irq( dev );
+
+ if ( copy_to_user( emit.irq_seq, &result, sizeof(int) ) ) {
+ DRM_ERROR( "copy_to_user\n" );
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+
+/* Doesn't need the hardware lock.
+ */
+int i830_irq_wait( struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg )
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ drm_i830_private_t *dev_priv = dev->dev_private;
+ drm_i830_irq_wait_t irqwait;
+
+ if ( !dev_priv ) {
+ DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
+ return -EINVAL;
+ }
+
+ if (copy_from_user( &irqwait, (drm_i830_irq_wait_t *)arg,
+ sizeof(irqwait) ))
+ return -EFAULT;
+
+ return i830_wait_irq( dev, irqwait.irq_seq );
+}
+
diff -urN linux-2.4.20/drivers/char/drm.org/Makefile linux-2.4.20/drivers/char/drm/Makefile
--- linux-2.4.20/drivers/char/drm.org/Makefile 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/Makefile 2002-12-12 12:27:37.000000000 +0100
@@ -10,7 +10,7 @@
r128-objs := r128_drv.o r128_cce.o r128_state.o
mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o
i810-objs := i810_drv.o i810_dma.o
-i830-objs := i830_drv.o i830_dma.o
+i830-objs := i830_drv.o i830_dma.o i830_irq.o
radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o
ffb-objs := ffb_drv.o ffb_context.o
diff -urN linux-2.4.20/drivers/char/drm.org/r128_drv.h linux-2.4.20/drivers/char/drm/r128_drv.h
--- linux-2.4.20/drivers/char/drm.org/r128_drv.h 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/r128_drv.h 2002-12-12 12:27:37.000000000 +0100
@@ -438,6 +438,7 @@
return -EBUSY; \
} \
__ring_space_done: ; \
+ break; \
} while (0)
#define VB_AGE_TEST_WITH_RETURN( dev_priv ) \
diff -urN linux-2.4.20/drivers/char/drm.org/radeon_irq.c linux-2.4.20/drivers/char/drm/radeon_irq.c
--- linux-2.4.20/drivers/char/drm.org/radeon_irq.c 2002-12-12 12:26:53.000000000 +0100
+++ linux-2.4.20/drivers/char/drm/radeon_irq.c 2002-12-12 12:27:37.000000000 +0100
@@ -71,13 +71,12 @@
wake_up_interruptible( &dev_priv->swi_queue );
}
-#if __HAVE_VBL_IRQ
/* VBLANK interrupt */
if (stat & RADEON_CRTC_VBLANK_STAT) {
atomic_inc(&dev->vbl_received);
wake_up_interruptible(&dev->vbl_queue);
+ DRM(vbl_send_signals)(dev);
}
-#endif
/* Acknowledge all the bits in GEN_INT_STATUS -- seem to get
* more than we asked for...
@@ -139,7 +138,6 @@
}
-#if __HAVE_VBL_IRQ
int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence)
{
drm_radeon_private_t *dev_priv =
@@ -162,13 +160,12 @@
*/
DRM_WAIT_ON( ret, dev->vbl_queue, 3*HZ,
( ( ( cur_vblank = atomic_read(&dev->vbl_received ) )
- + ~*sequence + 1 ) <= (1<<23) ) );
+ - ~*sequence + 1 ) <= (1<<23) ) );
*sequence = cur_vblank;
return ret;
}
-#endif
/* Needs the lock as it touches the ring.
prev parent reply other threads:[~2002-12-12 11:24 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2002-12-12 3:54 2.4.20-ac2 and i810 drm Dave Airlie
2002-12-12 5:03 ` Joshua Kwan
2002-12-12 8:32 ` Arjan van de Ven
2002-12-12 11:29 ` Arjan van de Ven [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1039692598.1450.4.camel@laptop.fenrus.com \
--to=arjanv@redhat.com \
--cc=airlied@linux.ie \
--cc=alan@lxorguk.ukuu.org.uk \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.