summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-07-23 14:05:01 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-07-23 14:05:01 +0000
commitf3627cbe9236a062012c836f3b6ee311b43f63f2 (patch)
treeae854838b9a73b35bd0f3b8f42e5fb7f9cb1d5a9 /drivers
parentfea12a7b3f20bc135ab533491411e9ff753c01c8 (diff)
Merge with Linux 2.4.0-test5-pre4.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/linear.c2
-rw-r--r--drivers/block/md.c71
-rw-r--r--drivers/block/raid0.c11
-rw-r--r--drivers/char/Config.in29
-rw-r--r--drivers/char/Makefile10
-rw-r--r--drivers/char/drm/Makefile103
-rw-r--r--drivers/char/drm/agpsupport.c327
-rw-r--r--drivers/char/drm/auth.c4
-rw-r--r--drivers/char/drm/bufs.c12
-rw-r--r--drivers/char/drm/context.c3
-rw-r--r--drivers/char/drm/ctxbitmap.c85
-rw-r--r--drivers/char/drm/dma.c26
-rw-r--r--drivers/char/drm/drawable.c3
-rw-r--r--drivers/char/drm/drm.h96
-rw-r--r--drivers/char/drm/drmP.h150
-rw-r--r--drivers/char/drm/fops.c28
-rw-r--r--drivers/char/drm/gamma_dma.c33
-rw-r--r--drivers/char/drm/gamma_drv.c136
-rw-r--r--drivers/char/drm/gamma_drv.h5
-rw-r--r--drivers/char/drm/i810_bufs.c334
-rw-r--r--drivers/char/drm/i810_context.c205
-rw-r--r--drivers/char/drm/i810_dma.c1411
-rw-r--r--drivers/char/drm/i810_drm.h194
-rw-r--r--drivers/char/drm/i810_drv.c643
-rw-r--r--drivers/char/drm/i810_drv.h227
-rw-r--r--drivers/char/drm/init.c3
-rw-r--r--drivers/char/drm/ioctl.c3
-rw-r--r--drivers/char/drm/lists.c3
-rw-r--r--drivers/char/drm/lock.c3
-rw-r--r--drivers/char/drm/memory.c154
-rw-r--r--drivers/char/drm/mga_bufs.c639
-rw-r--r--drivers/char/drm/mga_context.c205
-rw-r--r--drivers/char/drm/mga_dma.c1115
-rw-r--r--drivers/char/drm/mga_drm.h269
-rw-r--r--drivers/char/drm/mga_drv.c654
-rw-r--r--drivers/char/drm/mga_drv.h447
-rw-r--r--drivers/char/drm/mga_state.c1067
-rw-r--r--drivers/char/drm/proc.c31
-rw-r--r--drivers/char/drm/r128_bufs.c308
-rw-r--r--drivers/char/drm/r128_context.c213
-rw-r--r--drivers/char/drm/r128_dma.c922
-rw-r--r--drivers/char/drm/r128_drm.h111
-rw-r--r--drivers/char/drm/r128_drv.c717
-rw-r--r--drivers/char/drm/r128_drv.h226
-rw-r--r--drivers/char/drm/tdfx_context.c24
-rw-r--r--drivers/char/drm/tdfx_drv.c143
-rw-r--r--drivers/char/drm/tdfx_drv.h5
-rw-r--r--drivers/char/drm/vm.c38
-rw-r--r--drivers/char/hp600_keyb.c2
-rw-r--r--drivers/char/rtc.c1
-rw-r--r--drivers/char/scan_keyb.c2
-rw-r--r--drivers/char/scan_keyb.h2
-rw-r--r--drivers/char/serial.c98
-rw-r--r--drivers/char/sh-sci.c187
-rw-r--r--drivers/char/sh-sci.h125
-rw-r--r--drivers/char/tty_io.c4
-rw-r--r--drivers/ide/ide-tape.c2
-rw-r--r--drivers/parport/daisy.c9
-rw-r--r--drivers/parport/ieee1284.c7
-rw-r--r--drivers/parport/share.c7
-rw-r--r--drivers/s390/misc/chandev.c2
-rw-r--r--drivers/scsi/3w-xxxx.c193
-rw-r--r--drivers/scsi/3w-xxxx.h10
-rw-r--r--drivers/scsi/scsi.c2
-rw-r--r--drivers/usb/Config.in14
-rw-r--r--drivers/usb/devices.c2
-rw-r--r--drivers/usb/serial/Makefile53
-rw-r--r--drivers/usb/serial/digi_acceleport.c260
-rw-r--r--drivers/usb/serial/ftdi_sio.c21
-rw-r--r--drivers/usb/serial/keyspan.c42
-rw-r--r--drivers/usb/serial/keyspan.h1
-rw-r--r--drivers/usb/serial/keyspan_pda.c23
-rw-r--r--drivers/usb/serial/omninet.c21
-rw-r--r--drivers/usb/serial/usb-serial.h33
-rw-r--r--drivers/usb/serial/usbserial.c152
-rw-r--r--drivers/usb/serial/visor.c19
-rw-r--r--drivers/usb/serial/whiteheat.c30
-rw-r--r--drivers/usb/storage/Makefile7
-rw-r--r--drivers/usb/storage/debug.c33
-rw-r--r--drivers/usb/storage/debug.h3
-rw-r--r--drivers/usb/storage/protocol.c7
-rw-r--r--drivers/usb/storage/scm.c979
-rw-r--r--drivers/usb/storage/scm.h77
-rw-r--r--drivers/usb/storage/scsiglue.c12
-rw-r--r--drivers/usb/storage/scsiglue.h4
-rw-r--r--drivers/usb/storage/transport.c28
-rw-r--r--drivers/usb/storage/transport.h5
-rw-r--r--drivers/usb/storage/usb.c36
-rw-r--r--drivers/usb/storage/usb.h7
-rw-r--r--drivers/usb/usb-ohci.c27
-rw-r--r--drivers/usb/usb.c230
-rw-r--r--drivers/video/hitfb.c2
92 files changed, 13490 insertions, 739 deletions
diff --git a/drivers/block/linear.c b/drivers/block/linear.c
index 26d159473..b429e1bbf 100644
--- a/drivers/block/linear.c
+++ b/drivers/block/linear.c
@@ -148,7 +148,7 @@ static int linear_make_request (request_queue_t *q, mddev_t *mddev,
return -1;
}
bh->b_rdev = tmp_dev->dev;
- bh->b_rsector = ((block - tmp_dev->offset) << 1) + (bh->b_rsector & 1);
+ bh->b_rsector = bh->b_rsector - (tmp_dev->offset << 1);
return 1;
}
diff --git a/drivers/block/md.c b/drivers/block/md.c
index de977bdf0..2eb6cea4b 100644
--- a/drivers/block/md.c
+++ b/drivers/block/md.c
@@ -100,6 +100,7 @@ static ctl_table raid_root_table[] = {
*/
struct hd_struct md_hd_struct[MAX_MD_DEVS];
static int md_blocksizes[MAX_MD_DEVS];
+static int md_hardsect_sizes[MAX_MD_DEVS];
static int md_maxreadahead[MAX_MD_DEVS];
static mdk_thread_t *md_recovery_thread = NULL;
@@ -569,7 +570,7 @@ static int read_disk_sb (mdk_rdev_t * rdev)
printk (NO_SB,partition_name(rdev->dev));
goto abort;
}
- printk(" [events: %08lx]\n", (unsigned long)get_unaligned(&rdev->sb->events));
+ printk(" [events: %08lx]\n", (unsigned long)rdev->sb->events_lo);
ret = 0;
abort:
if (bh)
@@ -834,7 +835,7 @@ static void print_sb(mdp_super_t *sb)
printk(" UT:%08x ST:%d AD:%d WD:%d FD:%d SD:%d CSUM:%08x E:%08lx\n",
sb->utime, sb->state, sb->active_disks, sb->working_disks,
sb->failed_disks, sb->spare_disks,
- sb->sb_csum, (unsigned long)get_unaligned(&sb->events));
+ sb->sb_csum, (unsigned long)sb->events_lo);
for (i = 0; i < MD_SB_DISKS; i++) {
mdp_disk_t *desc;
@@ -1064,22 +1065,20 @@ int md_update_sb(mddev_t * mddev)
int first, err, count = 100;
struct md_list_head *tmp;
mdk_rdev_t *rdev;
- __u64 ev;
repeat:
mddev->sb->utime = CURRENT_TIME;
- ev = get_unaligned(&mddev->sb->events);
- ++ev;
- put_unaligned(ev,&mddev->sb->events);
- if (ev == (__u64)0) {
+ if ((++mddev->sb->events_lo)==0)
+ ++mddev->sb->events_hi;
+
+ if ((mddev->sb->events_lo|mddev->sb->events_hi)==0) {
/*
* oops, this 64-bit counter should never wrap.
* Either we are in around ~1 trillion A.C., assuming
* 1 reboot per second, or we have a bug:
*/
MD_BUG();
- --ev;
- put_unaligned(ev,&mddev->sb->events);
+ mddev->sb->events_lo = mddev->sb->events_hi = 0xffffffff;
}
sync_sbs(mddev);
@@ -1105,7 +1104,7 @@ repeat:
printk("%s ", partition_name(rdev->dev));
if (!rdev->faulty) {
printk("[events: %08lx]",
- (unsigned long)get_unaligned(&rdev->sb->events));
+ (unsigned long)rdev->sb->events_lo);
err += write_disk_sb(rdev);
} else
printk(")\n");
@@ -1288,15 +1287,13 @@ static int analyze_sbs (mddev_t * mddev)
* one event)
*/
if (calc_sb_csum(rdev->sb) != rdev->sb->sb_csum) {
- __u64 ev = get_unaligned(&rdev->sb->events);
- if (ev != (__u64)0) {
- --ev;
- put_unaligned(ev,&rdev->sb->events);
- }
+ if (rdev->sb->events_lo || rdev->sb->events_hi)
+ if ((rdev->sb->events_lo--)==0)
+ rdev->sb->events_hi--;
}
printk("%s's event counter: %08lx\n", partition_name(rdev->dev),
- (unsigned long)get_unaligned(&rdev->sb->events));
+ (unsigned long)rdev->sb->events_lo);
if (!freshest) {
freshest = rdev;
continue;
@@ -1304,8 +1301,8 @@ static int analyze_sbs (mddev_t * mddev)
/*
* Find the newest superblock version
*/
- ev1 = get_unaligned(&rdev->sb->events);
- ev2 = get_unaligned(&freshest->sb->events);
+ ev1 = md_event(rdev->sb);
+ ev2 = md_event(freshest->sb);
if (ev1 != ev2) {
out_of_date = 1;
if (ev1 > ev2)
@@ -1329,8 +1326,8 @@ static int analyze_sbs (mddev_t * mddev)
* Kick all non-fresh devices faulty
*/
__u64 ev1, ev2;
- ev1 = get_unaligned(&rdev->sb->events);
- ev2 = get_unaligned(&sb->events);
+ ev1 = md_event(rdev->sb);
+ ev2 = md_event(sb);
++ev1;
if (ev1 < ev2) {
printk("md: kicking non-fresh %s from array!\n",
@@ -1350,8 +1347,8 @@ static int analyze_sbs (mddev_t * mddev)
MD_BUG();
goto abort;
}
- ev1 = get_unaligned(&rdev->sb->events);
- ev2 = get_unaligned(&sb->events);
+ ev1 = md_event(rdev->sb);
+ ev2 = md_event(sb);
ev3 = ev2;
--ev3;
if ((rdev->dev != rdev->old_dev) &&
@@ -1694,14 +1691,22 @@ static int do_md_run (mddev_t * mddev)
* Drop all container device buffers, from now on
* the only valid external interface is through the md
* device.
+ * Also find largest hardsector size
*/
+ md_hardsect_sizes[mdidx(mddev)] = 512;
ITERATE_RDEV(mddev,rdev,tmp) {
if (rdev->faulty)
continue;
fsync_dev(rdev->dev);
invalidate_buffers(rdev->dev);
- }
-
+ if (get_hardsect_size(rdev->dev)
+ > md_hardsect_sizes[mdidx(mddev)])
+ md_hardsect_sizes[mdidx(mddev)] =
+ get_hardsect_size(rdev->dev);
+ }
+ md_blocksizes[mdidx(mddev)] = 1024;
+ if (md_blocksizes[mdidx(mddev)] < md_hardsect_sizes[mdidx(mddev)])
+ md_blocksizes[mdidx(mddev)] = md_hardsect_sizes[mdidx(mddev)];
mddev->pers = pers[pnum];
err = mddev->pers->run(mddev);
@@ -3578,7 +3583,7 @@ struct notifier_block md_notifier = {
0
};
-void md__init raid_setup(char *str)
+static int md__init raid_setup(char *str)
{
int len, pos;
@@ -3597,7 +3602,7 @@ void md__init raid_setup(char *str)
pos += wlen+1;
}
raid_setup_args.set = 1;
- return;
+ return 1;
}
__setup("raid=", raid_setup);
@@ -3608,12 +3613,14 @@ static void md_geninit (void)
for(i = 0; i < MAX_MD_DEVS; i++) {
md_blocksizes[i] = 1024;
md_size[i] = 0;
+ md_hardsect_sizes[i] = 512;
md_maxreadahead[i] = MD_READAHEAD;
register_disk(&md_gendisk, MKDEV(MAJOR_NR,i), 1, &md_fops, 0);
}
- blksize_size[MD_MAJOR] = md_blocksizes;
+ blksize_size[MAJOR_NR] = md_blocksizes;
blk_size[MAJOR_NR] = md_size;
- max_readahead[MD_MAJOR] = md_maxreadahead;
+ max_readahead[MAJOR_NR] = md_maxreadahead;
+ hardsect_size[MAJOR_NR] = md_hardsect_sizes;
printk("md.c: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t));
@@ -3636,9 +3643,9 @@ int md__init md_init (void)
MD_MAJOR_VERSION, MD_MINOR_VERSION,
MD_PATCHLEVEL_VERSION, MAX_MD_DEVS, MAX_REAL);
- if (devfs_register_blkdev (MD_MAJOR, "md", &md_fops))
+ if (devfs_register_blkdev (MAJOR_NR, "md", &md_fops))
{
- printk (KERN_ALERT "Unable to get major %d for md\n", MD_MAJOR);
+ printk (KERN_ALERT "Unable to get major %d for md\n", MAJOR_NR);
return (-1);
}
devfs_handle = devfs_mk_dir (NULL, "md", NULL);
@@ -3646,9 +3653,9 @@ int md__init md_init (void)
MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR,
&md_fops, NULL);
- blk_dev[MD_MAJOR].queue = md_get_queue;
+ blk_dev[MAJOR_NR].queue = md_get_queue;
- read_ahead[MD_MAJOR] = INT_MAX;
+ read_ahead[MAJOR_NR] = INT_MAX;
md_gendisk.next = gendisk_head;
gendisk_head = &md_gendisk;
diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c
index f06ddc355..f37d8e5fb 100644
--- a/drivers/block/raid0.c
+++ b/drivers/block/raid0.c
@@ -226,12 +226,12 @@ static int raid0_stop (mddev_t *mddev)
static int raid0_make_request (request_queue_t *q, mddev_t *mddev,
int rw, struct buffer_head * bh)
{
- int blk_in_chunk, chunksize_bits, chunk, chunk_size;
+ unsigned int sect_in_chunk, chunksize_bits, chunk, chunk_size;
raid0_conf_t *conf = mddev_to_conf(mddev);
struct raid0_hash *hash;
struct strip_zone *zone;
mdk_rdev_t *tmp_dev;
- long block, rblock;
+ unsigned long block, rsect;
chunk_size = mddev->param.chunk_size >> 10;
chunksize_bits = ffz(~chunk_size);
@@ -255,17 +255,18 @@ static int raid0_make_request (request_queue_t *q, mddev_t *mddev,
} else
zone = hash->zone0;
- blk_in_chunk = block & (chunk_size -1);
+ sect_in_chunk = bh->b_rsector & ((chunk_size<<1) -1);
chunk = (block - zone->zone_offset) / (zone->nb_dev << chunksize_bits);
tmp_dev = zone->dev[(block >> chunksize_bits) % zone->nb_dev];
- rblock = (chunk << chunksize_bits) + blk_in_chunk + zone->dev_offset;
+ rsect = ((chunk << chunksize_bits) + zone->dev_offset)<<1
+ + sect_in_chunk;
/*
* The new BH_Lock semantics in ll_rw_blk.c guarantee that this
* is the only IO operation happening on this bh.
*/
bh->b_rdev = tmp_dev->dev;
- bh->b_rsector = rblock << 1;
+ bh->b_rsector = rsect;
/*
* Let the main block layer submit the IO and resolve recursion:
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index f18158004..ae79962f4 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -258,24 +258,25 @@ fi
endmenu
bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM
-dep_tristate ' 3dfx Banshee/Voodoo3' CONFIG_DRM_TDFX $CONFIG_DRM
if [ "$CONFIG_DRM" = "y" ]; then
- dep_tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA m
+ tristate ' 3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
+ tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA
+ tristate ' ATI Rage 128' CONFIG_DRM_R128
+ tristate ' Intel I810' CONFIG_DRM_I810
+ tristate ' Matrox g200/g400' CONFIG_DRM_MGA
fi
-if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
- source drivers/char/pcmcia/Config.in
+tristate '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP
+if [ "$CONFIG_AGP" != "n" ]; then
+ bool ' Intel 440LX/BX/GX support' CONFIG_AGP_INTEL
+ bool ' Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810
+ bool ' VIA chipset support' CONFIG_AGP_VIA
+ bool ' AMD Irongate support' CONFIG_AGP_AMD
+ bool ' Generic SiS support' CONFIG_AGP_SIS
+ bool ' ALI M1541 support' CONFIG_AGP_ALI
fi
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate '/dev/agpgart (AGP Support) (EXPERIMENTAL)' CONFIG_AGP
- if [ "$CONFIG_AGP" != "n" ]; then
- bool ' Intel 440LX/BX/GX support' CONFIG_AGP_INTEL
- bool ' Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810
- bool ' VIA chipset support' CONFIG_AGP_VIA
- bool ' AMD Irongate support' CONFIG_AGP_AMD
- bool ' Generic SiS support' CONFIG_AGP_SIS
- bool ' ALI M1541 support' CONFIG_AGP_ALI
- fi
+if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
+ source drivers/char/pcmcia/Config.in
fi
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 9bba10cf4..085c48c8f 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -76,19 +76,11 @@ ifeq ($(ARCH),sh)
KEYMAP =
KEYBD =
CONSOLE =
- SERIAL =
ifeq ($(CONFIG_SH_HP600),y)
KEYMAP = defkeymap.o
KEYBD = scan_keyb.o hp600_keyb.o
CONSOLE = console.o
endif
- ifeq ($(CONFIG_SERIAL),y)
- SERIAL = generic_serial.o sh-sci.o
- else
- ifeq ($(CONFIG_SERIAL),m)
- SERIAL = sh-sci.o
- endif
- endif
endif
ifeq ($(CONFIG_DECSTATION),y)
@@ -149,7 +141,7 @@ obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
# So this should simply provide the wanted functionality!
obj-$(CONFIG_SX) += sx.o generic_serial.o
obj-$(CONFIG_RIO) += rio/rio.o generic_serial.o
-
+obj-$(CONFIG_SH_SCI) += sh-sci.o generic_serial.o
ifeq ($(CONFIG_RIO),y)
SUB_DIRS += rio
diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile
index 1c68f0cd2..3bb727c27 100644
--- a/drivers/char/drm/Makefile
+++ b/drivers/char/drm/Makefile
@@ -10,55 +10,80 @@
# parent makes..
#
-O_TARGET := drm.o
+O_OBJS :=
+OX_OBJS :=
+M_OBJS :=
+MX_OBJS :=
-L_OBJS := init.o memory.o proc.o auth.o context.o drawable.o bufs.o \
- lists.o lock.o ioctl.o fops.o vm.o dma.o
+# Object file lists.
-M_OBJS :=
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
-ifeq ($(CONFIG_DRM_GAMMA),y)
- OX_OBJS += gamma_drv.o
- O_OBJS += gamma_dma.o
-else
- ifeq ($(CONFIG_DRM_GAMMA),m)
- MIX_OBJS += gamma_drv.o
- MI_OBJS += gamma_dma.o
- M_OBJS += gamma.o
- endif
-endif
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+O_TARGET := drm.o
+module-list := gamma.o tdfx.o r128.o ffb.o mga.o i810.o
+export-objs := $(patsubst %.o,%_drv.o,$(module-list))
-ifeq ($(CONFIG_DRM_TDFX),y)
- OX_OBJS += tdfx_drv.o
- O_OBJS += tdfx_context.o
-else
- ifeq ($(CONFIG_DRM_TDFX),m)
- MIX_OBJS += tdfx_drv.o
- MI_OBJS += tdfx_context.o
- M_OBJS += tdfx.o
- endif
+lib-objs := init.o memory.o proc.o auth.o context.o drawable.o bufs.o
+lib-objs += lists.o lock.o ioctl.o fops.o vm.o dma.o ctxbitmap.o
+
+ifneq ($(CONFIG_AGP),)
+ lib-objs += agpsupport.o
endif
-ifeq ($(CONFIG_DRM_FFB),y)
- OX_OBJS += ffb_drv.o
- O_OBJS += ffb_context.o
-else
- ifeq ($(CONFIG_DRM_FFB),m)
- MIX_OBJC += ffb_drv.o
- MI_OBJS += ffb_context.o
- M_OBJS += ffb.o
- endif
+gamma-objs := $(lib-objs) gamma_drv.o gamma_dma.o
+tdfx-objs := $(lib-objs) tdfx_drv.o tdfx_context.o
+r128-objs := $(lib-objs) r128_drv.o r128_dma.o r128_context.o r128_bufs.o
+ffb-objs := $(lib-objs) ffb_drv.o ffb_context.o
+mga-objs := $(lib-objs) mga_drv.o mga_dma.o mga_context.o mga_bufs.o \
+ mga_state.o
+i810-objs := $(lib-objs) i810_drv.o i810_dma.o i810_context.o i810_bufs.o
+
+obj-$(CONFIG_DRM_GAMMA) += gamma.o $(gamma-objs)
+obj-$(CONFIG_DRM_TDFX) += tdfx.o $(tdfx-objs)
+obj-$(CONFIG_DRM_R128) += r128.o $(r128-objs)
+obj-$(CONFIG_DRM_FFB) += ffb.o $(ffb-objs)
+
+ifneq ($CONFIG_AGP),)
+obj-$(CONFIG_DRM_MGA) += mga.o $(mga-objs)
+obj-$(CONFIG_DRM_I810) += i810.o $(i810-objs)
endif
-O_OBJS += $(L_OBJS)
+# Take module names out of obj-y and int-m
+
+obj-y := $(filter-out $(module-list), $(obj-y))
+int-m := $(filter-out $(module-list), $(obj-m))
+
+# Translate to Rules.make lists.
+
+O_OBJS := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS := $(filter $(export-objs), $(obj-y))
+M_OBJS := $(sort $(filter $(module-list), $(obj-m)))
+MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
include $(TOPDIR)/Rules.make
-gamma.o : gamma_drv.o gamma_dma.o $(L_OBJS)
- $(LD) $(LD_RFLAG) -r -o $@ gamma_drv.o gamma_dma.o $(L_OBJS)
+gamma.o: $(gamma-objs)
+ $(LD) $(LD_RFLAG) -r -o $@ $(gamma-objs)
+
+tdfx.o: $(tdfx-objs)
+ $(LD) $(LD_RFLAG) -r -o $@ $(tdfx-objs)
+
+mga.o: $(mga-objs)
+ $(LD) $(LD_RFLAG) -r -o $@ $(mga-objs)
+
+i810.o: $(i810-objs)
+ $(LD) $(LD_RFLAG) -r -o $@ $(i810-objs)
-tdfx.o: tdfx_drv.o tdfx_context.o $(L_OBJS)
- $(LD) $(LD_RFLAG) -r -o $@ tdfx_drv.o tdfx_context.o $(L_OBJS)
+r128.o: $(r128-objs)
+ $(LD) $(LD_RFLAG) -r -o $@ $(r128-objs)
-ffb.o: ffb_drv.o ffb_context.o $(L_OBJS)
- $(LD) $(LD_RFLAG) -r -o $@ ffb_drv.o ffb_context.o $(L_OBJS)
+ffb.o: $(ffb-objs)
+ $(LD) $(LD_RFLAG) -r -o $@ $(ffb-objs)
diff --git a/drivers/char/drm/agpsupport.c b/drivers/char/drm/agpsupport.c
new file mode 100644
index 000000000..628e8cad5
--- /dev/null
+++ b/drivers/char/drm/agpsupport.c
@@ -0,0 +1,327 @@
+/* agpsupport.c -- DRM support for AGP/GART backend -*- linux-c -*-
+ * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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.
+ *
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+drm_agp_func_t drm_agp = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+/* The C standard says that 'void *' is not guaranteed to hold a function
+ pointer, so we use this union to define a generic pointer that is
+ guaranteed to hold any of the function pointers we care about. */
+typedef union {
+ void (*free_memory)(agp_memory *);
+ agp_memory *(*allocate_memory)(size_t, u32);
+ int (*bind_memory)(agp_memory *, off_t);
+ int (*unbind_memory)(agp_memory *);
+ void (*enable)(u32);
+ int (*acquire)(void);
+ void (*release)(void);
+ void (*copy_info)(agp_kern_info *);
+ unsigned long address;
+} drm_agp_func_u;
+
+typedef struct drm_agp_fill {
+ const char *name;
+ drm_agp_func_u *f;
+} drm_agp_fill_t;
+
+static drm_agp_fill_t drm_agp_fill[] = {
+ { __MODULE_STRING(agp_free_memory),
+ (drm_agp_func_u *)&drm_agp.free_memory },
+ { __MODULE_STRING(agp_allocate_memory),
+ (drm_agp_func_u *)&drm_agp.allocate_memory },
+ { __MODULE_STRING(agp_bind_memory),
+ (drm_agp_func_u *)&drm_agp.bind_memory },
+ { __MODULE_STRING(agp_unbind_memory),
+ (drm_agp_func_u *)&drm_agp.unbind_memory },
+ { __MODULE_STRING(agp_enable),
+ (drm_agp_func_u *)&drm_agp.enable },
+ { __MODULE_STRING(agp_backend_acquire),
+ (drm_agp_func_u *)&drm_agp.acquire },
+ { __MODULE_STRING(agp_backend_release),
+ (drm_agp_func_u *)&drm_agp.release },
+ { __MODULE_STRING(agp_copy_info),
+ (drm_agp_func_u *)&drm_agp.copy_info },
+ { NULL, NULL }
+};
+
+int drm_agp_info(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;
+ agp_kern_info *kern;
+ drm_agp_info_t info;
+
+ if (!dev->agp->acquired || !drm_agp.copy_info) return -EINVAL;
+
+ kern = &dev->agp->agp_info;
+ info.agp_version_major = kern->version.major;
+ info.agp_version_minor = kern->version.minor;
+ info.mode = kern->mode;
+ info.aperture_base = kern->aper_base;
+ info.aperture_size = kern->aper_size * 1024 * 1024;
+ info.memory_allowed = kern->max_memory << PAGE_SHIFT;
+ info.memory_used = kern->current_memory << PAGE_SHIFT;
+ info.id_vendor = kern->device->vendor;
+ info.id_device = kern->device->device;
+
+ copy_to_user_ret((drm_agp_info_t *)arg, &info, sizeof(info), -EFAULT);
+ return 0;
+}
+
+int drm_agp_acquire(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;
+ int retcode;
+
+ if (dev->agp->acquired || !drm_agp.acquire) return -EINVAL;
+ if ((retcode = (*drm_agp.acquire)())) return retcode;
+ dev->agp->acquired = 1;
+ return 0;
+}
+
+int drm_agp_release(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;
+
+ if (!dev->agp->acquired || !drm_agp.release) return -EINVAL;
+ (*drm_agp.release)();
+ dev->agp->acquired = 0;
+ return 0;
+
+}
+
+int drm_agp_enable(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_agp_mode_t mode;
+
+ if (!dev->agp->acquired || !drm_agp.enable) return -EINVAL;
+
+ copy_from_user_ret(&mode, (drm_agp_mode_t *)arg, sizeof(mode),
+ -EFAULT);
+
+ dev->agp->mode = mode.mode;
+ (*drm_agp.enable)(mode.mode);
+ dev->agp->base = dev->agp->agp_info.aper_base;
+ dev->agp->enabled = 1;
+ return 0;
+}
+
+int drm_agp_alloc(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_agp_buffer_t request;
+ drm_agp_mem_t *entry;
+ agp_memory *memory;
+ unsigned long pages;
+ u32 type;
+ if (!dev->agp->acquired) return -EINVAL;
+ copy_from_user_ret(&request, (drm_agp_buffer_t *)arg, sizeof(request),
+ -EFAULT);
+ if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS)))
+ return -ENOMEM;
+
+ memset(entry, 0, sizeof(*entry));
+
+ pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE;
+ type = (u32) request.type;
+
+ if (!(memory = drm_alloc_agp(pages, type))) {
+ drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+ return -ENOMEM;
+ }
+
+ entry->handle = (unsigned long)memory->memory;
+ entry->memory = memory;
+ entry->bound = 0;
+ entry->pages = pages;
+ entry->prev = NULL;
+ entry->next = dev->agp->memory;
+ if (dev->agp->memory) dev->agp->memory->prev = entry;
+ dev->agp->memory = entry;
+
+ request.handle = entry->handle;
+ request.physical = memory->physical;
+
+ if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) {
+ dev->agp->memory = entry->next;
+ dev->agp->memory->prev = NULL;
+ drm_free_agp(memory, pages);
+ drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev,
+ unsigned long handle)
+{
+ drm_agp_mem_t *entry;
+
+ for (entry = dev->agp->memory; entry; entry = entry->next) {
+ if (entry->handle == handle) return entry;
+ }
+ return NULL;
+}
+
+int drm_agp_unbind(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_agp_binding_t request;
+ drm_agp_mem_t *entry;
+
+ if (!dev->agp->acquired) return -EINVAL;
+ copy_from_user_ret(&request, (drm_agp_binding_t *)arg, sizeof(request),
+ -EFAULT);
+ if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+ return -EINVAL;
+ if (!entry->bound) return -EINVAL;
+ return drm_unbind_agp(entry->memory);
+}
+
+int drm_agp_bind(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_agp_binding_t request;
+ drm_agp_mem_t *entry;
+ int retcode;
+ int page;
+
+ if (!dev->agp->acquired || !drm_agp.bind_memory) return -EINVAL;
+ copy_from_user_ret(&request, (drm_agp_binding_t *)arg, sizeof(request),
+ -EFAULT);
+ if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+ return -EINVAL;
+ if (entry->bound) return -EINVAL;
+ page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE;
+ if ((retcode = drm_bind_agp(entry->memory, page))) return retcode;
+ entry->bound = dev->agp->base + (page << PAGE_SHIFT);
+ DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n",
+ dev->agp->base, entry->bound);
+ return 0;
+}
+
+int drm_agp_free(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_agp_buffer_t request;
+ drm_agp_mem_t *entry;
+
+ if (!dev->agp->acquired) return -EINVAL;
+ copy_from_user_ret(&request, (drm_agp_buffer_t *)arg, sizeof(request),
+ -EFAULT);
+ if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+ return -EINVAL;
+ if (entry->bound) drm_unbind_agp(entry->memory);
+
+ if (entry->prev) entry->prev->next = entry->next;
+ else dev->agp->memory = entry->next;
+ if (entry->next) entry->next->prev = entry->prev;
+ drm_free_agp(entry->memory, entry->pages);
+ drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+ return 0;
+}
+
+drm_agp_head_t *drm_agp_init(void)
+{
+ drm_agp_fill_t *fill;
+ drm_agp_head_t *head = NULL;
+ int agp_available = 1;
+
+ for (fill = &drm_agp_fill[0]; fill->name; fill++) {
+ char *n = (char *)fill->name;
+ *fill->f = (drm_agp_func_u)get_module_symbol(NULL, n);
+ DRM_DEBUG("%s resolves to 0x%08lx\n", n, (*fill->f).address);
+ if (!(*fill->f).address) agp_available = 0;
+ }
+
+ DRM_DEBUG("agp_available = %d\n", agp_available);
+
+ if (agp_available) {
+ if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS)))
+ return NULL;
+ memset((void *)head, 0, sizeof(*head));
+ (*drm_agp.copy_info)(&head->agp_info);
+ head->memory = NULL;
+ switch (head->agp_info.chipset) {
+ case INTEL_GENERIC: head->chipset = "Intel"; break;
+ case INTEL_LX: head->chipset = "Intel 440LX"; break;
+ case INTEL_BX: head->chipset = "Intel 440BX"; break;
+ case INTEL_GX: head->chipset = "Intel 440GX"; break;
+ case INTEL_I810: head->chipset = "Intel i810"; break;
+ case VIA_GENERIC: head->chipset = "VIA"; break;
+ case VIA_VP3: head->chipset = "VIA VP3"; break;
+ case VIA_MVP3: head->chipset = "VIA MVP3"; break;
+ case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; break;
+ case SIS_GENERIC: head->chipset = "SiS"; break;
+ case AMD_GENERIC: head->chipset = "AMD"; break;
+ case AMD_IRONGATE: head->chipset = "AMD Irongate"; break;
+ case ALI_GENERIC: head->chipset = "ALi"; break;
+ case ALI_M1541: head->chipset = "ALi M1541"; break;
+ default:
+ }
+ DRM_INFO("AGP %d.%d on %s @ 0x%08lx %dMB\n",
+ head->agp_info.version.major,
+ head->agp_info.version.minor,
+ head->chipset,
+ head->agp_info.aper_base,
+ head->agp_info.aper_size);
+ }
+ return head;
+}
+
+void drm_agp_uninit(void)
+{
+ drm_agp_fill_t *fill;
+
+ for (fill = &drm_agp_fill[0]; fill->name; fill++) {
+#if LINUX_VERSION_CODE >= 0x020400
+ if ((*fill->f).address) put_module_symbol((*fill->f).address);
+#endif
+ (*fill->f).address = 0;
+ }
+}
diff --git a/drivers/char/drm/auth.c b/drivers/char/drm/auth.c
index ebf0671f4..9f81c5391 100644
--- a/drivers/char/drm/auth.c
+++ b/drivers/char/drm/auth.c
@@ -2,6 +2,7 @@
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
@@ -44,7 +45,6 @@ static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic)
down(&dev->struct_sem);
for (pt = dev->magiclist[hash].head; pt; pt = pt->next) {
- if (pt->priv->authenticated) continue;
if (pt->magic == magic) {
retval = pt->priv;
break;
diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c
index 3d4c40222..fd3b12488 100644
--- a/drivers/char/drm/bufs.c
+++ b/drivers/char/drm/bufs.c
@@ -2,6 +2,7 @@
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
*
* Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
@@ -71,7 +72,7 @@ int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd,
switch (map->type) {
case _DRM_REGISTERS:
- case _DRM_FRAME_BUFFER:
+ case _DRM_FRAME_BUFFER:
#ifndef __sparc__
if (map->offset + map->size < map->offset
|| map->offset < virt_to_phys(high_memory)) {
@@ -105,6 +106,11 @@ int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd,
dev->lock.hw_lock = map->handle; /* Pointer to lock */
}
break;
+#ifdef DRM_AGP
+ case _DRM_AGP:
+ map->offset = map->offset + dev->agp->base;
+ break;
+#endif
default:
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EINVAL;
@@ -175,7 +181,7 @@ int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
if (dev->queue_count) return -EBUSY; /* Not while in use */
- alignment = (request.flags & DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+ alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
total = PAGE_SIZE << page_order;
diff --git a/drivers/char/drm/context.c b/drivers/char/drm/context.c
index a8919d83d..ca491094e 100644
--- a/drivers/char/drm/context.c
+++ b/drivers/char/drm/context.c
@@ -2,6 +2,7 @@
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
diff --git a/drivers/char/drm/ctxbitmap.c b/drivers/char/drm/ctxbitmap.c
new file mode 100644
index 000000000..615505979
--- /dev/null
+++ b/drivers/char/drm/ctxbitmap.c
@@ -0,0 +1,85 @@
+/* ctxbitmap.c -- Context bitmap management -*- linux-c -*-
+ * Created: Thu Jan 6 03:56:42 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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.
+ *
+ * Author: Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle)
+{
+ if (ctx_handle < 0) goto failed;
+
+ if (ctx_handle < DRM_MAX_CTXBITMAP) {
+ clear_bit(ctx_handle, dev->ctx_bitmap);
+ return;
+ }
+failed:
+ DRM_ERROR("Attempt to free invalid context handle: %d\n",
+ ctx_handle);
+ return;
+}
+
+int drm_ctxbitmap_next(drm_device_t *dev)
+{
+ int bit;
+
+ bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP);
+ if (bit < DRM_MAX_CTXBITMAP) {
+ set_bit(bit, dev->ctx_bitmap);
+ DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit);
+ return bit;
+ }
+ return -1;
+}
+
+int drm_ctxbitmap_init(drm_device_t *dev)
+{
+ int i;
+ int temp;
+
+ dev->ctx_bitmap = (unsigned long *) drm_alloc(PAGE_SIZE,
+ DRM_MEM_CTXBITMAP);
+ if(dev->ctx_bitmap == NULL) {
+ return -ENOMEM;
+ }
+ memset((void *) dev->ctx_bitmap, 0, PAGE_SIZE);
+ for(i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+ temp = drm_ctxbitmap_next(dev);
+ DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp);
+ }
+
+ return 0;
+}
+
+void drm_ctxbitmap_cleanup(drm_device_t *dev)
+{
+ drm_free((void *)dev->ctx_bitmap, PAGE_SIZE,
+ DRM_MEM_CTXBITMAP);
+}
+
diff --git a/drivers/char/drm/dma.c b/drivers/char/drm/dma.c
index 0ec14ede5..ac2d1bc5a 100644
--- a/drivers/char/drm/dma.c
+++ b/drivers/char/drm/dma.c
@@ -1,7 +1,8 @@
/* dma.c -- DMA IOCTL and function support -*- linux-c -*-
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
*
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinuxa.com>
*
*/
@@ -63,15 +64,24 @@ void drm_dma_takedown(drm_device_t *dev)
dma->bufs[i].page_order,
DRM_MEM_DMA);
}
- drm_free(dma->bufs[i].buflist,
- dma->buf_count
- * sizeof(*dma->bufs[0].buflist),
- DRM_MEM_BUFS);
drm_free(dma->bufs[i].seglist,
- dma->buf_count
+ dma->bufs[i].seg_count
* sizeof(*dma->bufs[0].seglist),
DRM_MEM_SEGS);
- drm_freelist_destroy(&dma->bufs[i].freelist);
+ }
+ if(dma->bufs[i].buf_count) {
+ for(j = 0; j < dma->bufs[i].buf_count; j++) {
+ if(dma->bufs[i].buflist[j].dev_private) {
+ drm_free(dma->bufs[i].buflist[j].dev_private,
+ dma->bufs[i].buflist[j].dev_priv_size,
+ DRM_MEM_BUFS);
+ }
+ }
+ drm_free(dma->bufs[i].buflist,
+ dma->bufs[i].buf_count *
+ sizeof(*dma->bufs[0].buflist),
+ DRM_MEM_BUFS);
+ drm_freelist_destroy(&dma->bufs[i].freelist);
}
}
diff --git a/drivers/char/drm/drawable.c b/drivers/char/drm/drawable.c
index 19e5da3b7..03839f5be 100644
--- a/drivers/char/drm/drawable.c
+++ b/drivers/char/drm/drawable.c
@@ -2,6 +2,7 @@
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h
index fe0f8defe..5a979e1cb 100644
--- a/drivers/char/drm/drm.h
+++ b/drivers/char/drm/drm.h
@@ -2,6 +2,7 @@
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
* Acknowledgements:
* Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
@@ -61,6 +62,20 @@ typedef unsigned int drm_context_t;
typedef unsigned int drm_drawable_t;
typedef unsigned int drm_magic_t;
+/* Warning: If you change this structure, make sure you change
+ * XF86DRIClipRectRec in the server as well */
+
+typedef struct drm_clip_rect {
+ unsigned short x1;
+ unsigned short y1;
+ unsigned short x2;
+ unsigned short y2;
+} drm_clip_rect_t;
+
+/* Seperate include files for the i810/mga/r128 specific structures */
+#include "mga_drm.h"
+#include "i810_drm.h"
+#include "r128_drm.h"
typedef struct drm_version {
int version_major; /* Major version */
@@ -101,7 +116,8 @@ typedef struct drm_control {
typedef enum drm_map_type {
_DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */
_DRM_REGISTERS = 1, /* no caching, no core dump */
- _DRM_SHM = 2 /* shared, cached */
+ _DRM_SHM = 2, /* shared, cached */
+ _DRM_AGP = 3 /* AGP/GART */
} drm_map_type_t;
typedef enum drm_map_flags {
@@ -165,8 +181,11 @@ typedef struct drm_buf_desc {
int low_mark; /* Low water mark */
int high_mark; /* High water mark */
enum {
- DRM_PAGE_ALIGN = 0x01 /* Align on page boundaries for DMA */
+ _DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA */
+ _DRM_AGP_BUFFER = 0x02 /* Buffer is in agp space */
} flags;
+ unsigned long agp_start; /* Start address of where the agp buffers
+ * are in the agp aperture */
} drm_buf_desc_t;
typedef struct drm_buf_info {
@@ -237,6 +256,38 @@ typedef struct drm_irq_busid {
int funcnum;
} drm_irq_busid_t;
+typedef struct drm_agp_mode {
+ unsigned long mode;
+} drm_agp_mode_t;
+
+ /* For drm_agp_alloc -- allocated a buffer */
+typedef struct drm_agp_buffer {
+ unsigned long size; /* In bytes -- will round to page boundary */
+ unsigned long handle; /* Used for BIND/UNBIND ioctls */
+ unsigned long type; /* Type of memory to allocate */
+ unsigned long physical; /* Physical used by i810 */
+} drm_agp_buffer_t;
+
+ /* For drm_agp_bind */
+typedef struct drm_agp_binding {
+ unsigned long handle; /* From drm_agp_buffer */
+ unsigned long offset; /* In bytes -- will round to page boundary */
+} drm_agp_binding_t;
+
+typedef struct drm_agp_info {
+ int agp_version_major;
+ int agp_version_minor;
+ unsigned long mode;
+ unsigned long aperture_base; /* physical address */
+ unsigned long aperture_size; /* bytes */
+ unsigned long memory_allowed; /* bytes */
+ unsigned long memory_used;
+
+ /* PCI information */
+ unsigned short id_vendor;
+ unsigned short id_device;
+} drm_agp_info_t;
+
#define DRM_IOCTL_BASE 'd'
#define DRM_IOCTL_NR(n) _IOC_NR(n)
#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr)
@@ -247,7 +298,7 @@ typedef struct drm_irq_busid {
#define DRM_IOCTL_VERSION DRM_IOWR(0x00, drm_version_t)
#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm_unique_t)
-#define DRM_IOCTL_GET_MAGIC DRM_IOW( 0x02, drm_auth_t)
+#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, drm_auth_t)
#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, drm_irq_busid_t)
#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm_unique_t)
@@ -276,4 +327,41 @@ typedef struct drm_irq_busid {
#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, drm_lock_t)
#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, drm_lock_t)
+#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30)
+#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31)
+#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, drm_agp_mode_t)
+#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, drm_agp_info_t)
+#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, drm_agp_binding_t)
+#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, drm_agp_binding_t)
+
+/* Mga specific ioctls */
+#define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t)
+#define DRM_IOCTL_MGA_SWAP DRM_IOW( 0x41, drm_mga_swap_t)
+#define DRM_IOCTL_MGA_CLEAR DRM_IOW( 0x42, drm_mga_clear_t)
+#define DRM_IOCTL_MGA_ILOAD DRM_IOW( 0x43, drm_mga_iload_t)
+#define DRM_IOCTL_MGA_VERTEX DRM_IOW( 0x44, drm_mga_vertex_t)
+#define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x45, drm_lock_t )
+#define DRM_IOCTL_MGA_INDICES DRM_IOW( 0x46, drm_mga_indices_t)
+
+/* I810 specific ioctls */
+#define DRM_IOCTL_I810_INIT DRM_IOW( 0x40, drm_i810_init_t)
+#define DRM_IOCTL_I810_VERTEX DRM_IOW( 0x41, drm_i810_vertex_t)
+#define DRM_IOCTL_I810_CLEAR DRM_IOW( 0x42, drm_i810_clear_t)
+#define DRM_IOCTL_I810_FLUSH DRM_IO ( 0x43)
+#define DRM_IOCTL_I810_GETAGE DRM_IO ( 0x44)
+#define DRM_IOCTL_I810_GETBUF DRM_IOWR(0x45, drm_i810_dma_t)
+#define DRM_IOCTL_I810_SWAP DRM_IO ( 0x46)
+#define DRM_IOCTL_I810_COPY DRM_IOW( 0x47, drm_i810_copy_t)
+#define DRM_IOCTL_I810_DOCOPY DRM_IO ( 0x48)
+
+/* Rage 128 specific ioctls */
+#define DRM_IOCTL_R128_INIT DRM_IOW( 0x40, drm_r128_init_t)
+#define DRM_IOCTL_R128_RESET DRM_IO( 0x41)
+#define DRM_IOCTL_R128_FLUSH DRM_IO( 0x42)
+#define DRM_IOCTL_R128_IDLE DRM_IO( 0x43)
+#define DRM_IOCTL_R128_PACKET DRM_IOW( 0x44, drm_r128_packet_t)
+#define DRM_IOCTL_R128_VERTEX DRM_IOW( 0x45, drm_r128_vertex_t)
+
#endif
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
index b39fec3f7..d8791bf95 100644
--- a/drivers/char/drm/drmP.h
+++ b/drivers/char/drm/drmP.h
@@ -2,6 +2,7 @@
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
@@ -49,7 +50,15 @@
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+#define DRM_AGP
+#endif
+#ifdef DRM_AGP
+#include <linux/types.h>
+#include <linux/agp_backend.h>
+#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <linux/tqueue.h>
#include <linux/poll.h>
#endif
#include "drm.h"
@@ -69,21 +78,27 @@
#define DRM_FLAG_DEBUG 0x01
#define DRM_FLAG_NOCTX 0x02
-#define DRM_MEM_DMA 0
-#define DRM_MEM_SAREA 1
-#define DRM_MEM_DRIVER 2
-#define DRM_MEM_MAGIC 3
-#define DRM_MEM_IOCTLS 4
-#define DRM_MEM_MAPS 5
-#define DRM_MEM_VMAS 6
-#define DRM_MEM_BUFS 7
-#define DRM_MEM_SEGS 8
-#define DRM_MEM_PAGES 9
-#define DRM_MEM_FILES 10
-#define DRM_MEM_QUEUES 11
-#define DRM_MEM_CMDS 12
-#define DRM_MEM_MAPPINGS 13
-#define DRM_MEM_BUFLISTS 14
+#define DRM_MEM_DMA 0
+#define DRM_MEM_SAREA 1
+#define DRM_MEM_DRIVER 2
+#define DRM_MEM_MAGIC 3
+#define DRM_MEM_IOCTLS 4
+#define DRM_MEM_MAPS 5
+#define DRM_MEM_VMAS 6
+#define DRM_MEM_BUFS 7
+#define DRM_MEM_SEGS 8
+#define DRM_MEM_PAGES 9
+#define DRM_MEM_FILES 10
+#define DRM_MEM_QUEUES 11
+#define DRM_MEM_CMDS 12
+#define DRM_MEM_MAPPINGS 13
+#define DRM_MEM_BUFLISTS 14
+#define DRM_MEM_AGPLISTS 15
+#define DRM_MEM_TOTALAGP 16
+#define DRM_MEM_BOUNDAGP 17
+#define DRM_MEM_CTXBITMAP 18
+
+#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
/* Backward compatibility section */
/* _PAGE_WT changed to _PAGE_PWT in 2.2.6 */
@@ -117,6 +132,14 @@ typedef struct wait_queue *wait_queue_head_t;
#define NOPAGE_OOM 0
#endif
+ /* module_init/module_exit added in 2.3.13 */
+#ifndef module_init
+#define module_init(x) int init_module(void) { return x(); }
+#endif
+#ifndef module_exit
+#define module_exit(x) void cleanup_module(void) { x(); }
+#endif
+
/* Generic cmpxchg added in 2.3.x */
#ifndef __HAVE_ARCH_CMPXCHG
/* Include this here so that driver can be
@@ -213,8 +236,8 @@ typedef struct drm_magic_entry {
} drm_magic_entry_t;
typedef struct drm_magic_head {
- struct drm_magic_entry *head;
- struct drm_magic_entry *tail;
+ struct drm_magic_entry *head;
+ struct drm_magic_entry *tail;
} drm_magic_head_t;
typedef struct drm_vma_entry {
@@ -230,6 +253,7 @@ typedef struct drm_buf {
int used; /* Amount of buffer in use (for DMA) */
unsigned long offset; /* Byte offset (used internally) */
void *address; /* Address of buffer */
+ unsigned long bus_address; /* Bus address of buffer */
struct drm_buf *next; /* Kernel-only: used for free list */
__volatile__ int waiting; /* On kernel DMA queue */
__volatile__ int pending; /* On hardware DMA queue */
@@ -245,12 +269,16 @@ typedef struct drm_buf {
DRM_LIST_PRIO = 4,
DRM_LIST_RECLAIM = 5
} list; /* Which list we're on */
+
#if DRM_DMA_HISTOGRAM
cycles_t time_queued; /* Queued to kernel DMA queue */
cycles_t time_dispatched; /* Dispatched to hardware */
cycles_t time_completed; /* Completed by hardware */
cycles_t time_freed; /* Back on freelist */
#endif
+
+ int dev_priv_size; /* Size of buffer private stoarge */
+ void *dev_private; /* Per-buffer private storage */
} drm_buf_t;
#if DRM_DMA_HISTOGRAM
@@ -371,6 +399,9 @@ typedef struct drm_device_dma {
int page_count;
unsigned long *pagelist;
unsigned long byte_count;
+ enum {
+ _DRM_DMA_USE_AGP = 0x01
+ } flags;
/* DMA support */
drm_buf_t *this_buffer; /* Buffer being sent */
@@ -379,6 +410,41 @@ typedef struct drm_device_dma {
wait_queue_head_t waiting; /* Processes waiting on free bufs */
} drm_device_dma_t;
+#ifdef DRM_AGP
+typedef struct drm_agp_mem {
+ unsigned long handle;
+ agp_memory *memory;
+ unsigned long bound; /* address */
+ int pages;
+ struct drm_agp_mem *prev;
+ struct drm_agp_mem *next;
+} drm_agp_mem_t;
+
+typedef struct drm_agp_head {
+ agp_kern_info agp_info;
+ const char *chipset;
+ drm_agp_mem_t *memory;
+ unsigned long mode;
+ int enabled;
+ int acquired;
+ unsigned long base;
+ int agp_mtrr;
+} drm_agp_head_t;
+
+typedef struct {
+ void (*free_memory)(agp_memory *);
+ agp_memory *(*allocate_memory)(size_t, u32);
+ int (*bind_memory)(agp_memory *, off_t);
+ int (*unbind_memory)(agp_memory *);
+ void (*enable)(u32);
+ int (*acquire)(void);
+ void (*release)(void);
+ void (*copy_info)(agp_kern_info *);
+} drm_agp_func_t;
+
+extern drm_agp_func_t drm_agp;
+#endif
+
typedef struct drm_device {
const char *name; /* Simple driver name */
char *unique; /* Unique identifier: e.g., busid */
@@ -433,9 +499,9 @@ typedef struct drm_device {
/* Context support */
int irq; /* Interrupt used by board */
- __volatile__ long context_flag; /* Context swapping flag */
- __volatile__ long interrupt_flag;/* Interruption handler flag */
- __volatile__ long dma_flag; /* DMA dispatch flag */
+ __volatile__ int context_flag; /* Context swapping flag */
+ __volatile__ int interrupt_flag;/* Interruption handler flag */
+ __volatile__ int dma_flag; /* DMA dispatch flag */
struct timer_list timer; /* Timer for delaying ctx switch */
wait_queue_head_t context_wait; /* Processes waiting on ctx switch */
int last_checked; /* Last context checked for DMA */
@@ -457,6 +523,12 @@ typedef struct drm_device {
struct fasync_struct *buf_async;/* Processes waiting for SIGIO */
wait_queue_head_t buf_readers; /* Processes waiting to read */
wait_queue_head_t buf_writers; /* Processes waiting to ctx switch */
+
+#ifdef DRM_AGP
+ drm_agp_head_t *agp;
+#endif
+ unsigned long *ctx_bitmap;
+ void *dev_private;
} drm_device_t;
@@ -529,6 +601,14 @@ extern void drm_free_pages(unsigned long address, int order,
extern void *drm_ioremap(unsigned long offset, unsigned long size);
extern void drm_ioremapfree(void *pt, unsigned long size);
+#ifdef DRM_AGP
+extern agp_memory *drm_alloc_agp(int pages, u32 type);
+extern int drm_free_agp(agp_memory *handle, int pages);
+extern int drm_bind_agp(agp_memory *handle, unsigned int start);
+extern int drm_unbind_agp(agp_memory *handle);
+#endif
+
+
/* Buffer management support (bufs.c) */
extern int drm_order(unsigned long size);
extern int drm_addmap(struct inode *inode, struct file *filp,
@@ -638,5 +718,33 @@ extern int drm_flush_unblock(drm_device_t *dev, int context,
drm_lock_flags_t flags);
extern int drm_flush_block_and_flush(drm_device_t *dev, int context,
drm_lock_flags_t flags);
+
+ /* Context Bitmap support (ctxbitmap.c) */
+extern int drm_ctxbitmap_init(drm_device_t *dev);
+extern void drm_ctxbitmap_cleanup(drm_device_t *dev);
+extern int drm_ctxbitmap_next(drm_device_t *dev);
+extern void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle);
+
+#ifdef DRM_AGP
+ /* AGP/GART support (agpsupport.c) */
+extern drm_agp_head_t *drm_agp_init(void);
+extern void drm_agp_uninit(void);
+extern int drm_agp_acquire(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_agp_release(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_agp_enable(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_agp_info(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_agp_alloc(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_agp_free(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_agp_unbind(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int drm_agp_bind(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+#endif
#endif
#endif
diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c
index 76e0dc2c1..f3966d96b 100644
--- a/drivers/char/drm/fops.c
+++ b/drivers/char/drm/fops.c
@@ -1,7 +1,8 @@
/* fops.c -- File operations for DRM -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
*
- * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,13 +25,14 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
- * Daryll Strauss <daryll@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
+ * Daryll Strauss <daryll@valinux.com>
*
*/
#define __NO_VERSION__
#include "drmP.h"
+#include <linux/poll.h>
/* drm_open is called whenever a process opens /dev/drm. */
@@ -92,7 +94,7 @@ int drm_release(struct inode *inode, struct file *filp)
DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
current->pid, dev->device, dev->open_count);
- if (dev->lock.hw_lock != NULL
+ if (dev->lock.hw_lock
&& _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
&& dev->lock.pid == current->pid) {
DRM_ERROR("Process %d dead, freeing lock for context %d\n",
@@ -213,11 +215,23 @@ int drm_write_string(drm_device_t *dev, const char *s)
send -= count;
}
-#if LINUX_VERSION_CODE < 0x020315
+#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS)
+ /* The extra parameter to kill_fasync was added in 2.3.21, and is
+ _not_ present in _stock_ 2.2.14 and 2.2.15. However, some
+ distributions patch 2.2.x kernels to add this parameter. The
+ Makefile.linux attempts to detect this addition and defines
+ KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */
if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO);
#else
- /* Parameter added in 2.3.21 */
- kill_fasync(&dev->buf_async, SIGIO, POLL_IN);
+
+ /* Parameter added in 2.3.21. */
+#if LINUX_VERSION_CODE < 0x020400
+ if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN);
+#else
+ /* Type of first parameter changed in
+ Linux 2.4.0-test2... */
+ if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN);
+#endif
#endif
DRM_DEBUG("waking\n");
wake_up_interruptible(&dev->buf_readers);
diff --git a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c
index 1f8c0a7de..a99f24cae 100644
--- a/drivers/char/drm/gamma_dma.c
+++ b/drivers/char/drm/gamma_dma.c
@@ -2,6 +2,7 @@
* Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
@@ -87,13 +88,31 @@ static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address,
GAMMA_WRITE(GAMMA_DMACOUNT, length / 4);
}
-static inline void gamma_dma_quiescent(drm_device_t *dev)
+static inline void gamma_dma_quiescent_single(drm_device_t *dev)
{
while (GAMMA_READ(GAMMA_DMACOUNT))
;
while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
;
+
+ GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
+ GAMMA_WRITE(GAMMA_SYNC, 0);
+
+ do {
+ while (!GAMMA_READ(GAMMA_OUTFIFOWORDS))
+ ;
+ } while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
+}
+
+static inline void gamma_dma_quiescent_dual(drm_device_t *dev)
+{
+ while (GAMMA_READ(GAMMA_DMACOUNT))
+ ;
+ while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
+ ;
+
GAMMA_WRITE(GAMMA_BROADCASTMASK, 3);
+
GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
GAMMA_WRITE(GAMMA_SYNC, 0);
@@ -103,7 +122,6 @@ static inline void gamma_dma_quiescent(drm_device_t *dev)
;
} while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
-
/* Read from second MX */
do {
while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000))
@@ -788,8 +806,13 @@ int gamma_lock(struct inode *inode, struct file *filp, unsigned int cmd,
if (!ret) {
if (lock.flags & _DRM_LOCK_READY)
gamma_dma_ready(dev);
- if (lock.flags & _DRM_LOCK_QUIESCENT)
- gamma_dma_quiescent(dev);
+ if (lock.flags & _DRM_LOCK_QUIESCENT) {
+ if (gamma_found() == 1) {
+ gamma_dma_quiescent_single(dev);
+ } else {
+ gamma_dma_quiescent_dual(dev);
+ }
+ }
}
DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c
index 8809d1810..2450d3a7b 100644
--- a/drivers/char/drm/gamma_drv.c
+++ b/drivers/char/drm/gamma_drv.c
@@ -1,7 +1,8 @@
/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
*
- * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,37 +25,50 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
#include "drmP.h"
#include "gamma_drv.h"
+#include <linux/pci.h>
+#include <linux/smp_lock.h> /* For (un)lock_kernel */
EXPORT_SYMBOL(gamma_init);
EXPORT_SYMBOL(gamma_cleanup);
+#ifndef PCI_DEVICE_ID_3DLABS_GAMMA
+#define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008
+#endif
+#ifndef PCI_DEVICE_ID_3DLABS_MX
+#define PCI_DEVICE_ID_3DLABS_MX 0x0006
+#endif
+
#define GAMMA_NAME "gamma"
#define GAMMA_DESC "3dlabs GMX 2000"
-#define GAMMA_DATE "19990830"
-#define GAMMA_MAJOR 0
+#define GAMMA_DATE "20000719"
+#define GAMMA_MAJOR 1
#define GAMMA_MINOR 0
-#define GAMMA_PATCHLEVEL 5
+#define GAMMA_PATCHLEVEL 0
static drm_device_t gamma_device;
static struct file_operations gamma_fops = {
- owner: THIS_MODULE,
- open: gamma_open,
- flush: drm_flush,
- release: gamma_release,
- ioctl: gamma_ioctl,
- mmap: drm_mmap,
- read: drm_read,
- fasync: drm_fasync,
- poll: drm_poll,
+#if LINUX_VERSION_CODE >= 0x020322
+ /* This started being used approx. 2.3.34 */
+ owner: THIS_MODULE,
+#endif
+ open: gamma_open,
+ flush: drm_flush,
+ release: gamma_release,
+ ioctl: gamma_ioctl,
+ mmap: drm_mmap,
+ read: drm_read,
+ fasync: drm_fasync,
+ poll: drm_poll,
};
static struct miscdevice gamma_misc = {
@@ -98,46 +112,34 @@ static drm_ioctl_desc_t gamma_ioctls[] = {
#define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls)
#ifdef MODULE
-int init_module(void);
-void cleanup_module(void);
static char *gamma = NULL;
+#endif
+static int devices = 0;
-MODULE_AUTHOR("Precision Insight, Inc., Cedar Park, Texas.");
+MODULE_AUTHOR("VA Linux Systems, Inc.");
MODULE_DESCRIPTION("3dlabs GMX 2000");
MODULE_PARM(gamma, "s");
+MODULE_PARM(devices, "i");
+MODULE_PARM_DESC(devices,
+ "devices=x, where x is the number of MX chips on card\n");
-/* init_module is called when insmod is used to load the module */
-
-int init_module(void)
-{
- return gamma_init();
-}
-
-/* cleanup_module is called when rmmod is used to unload the module */
-
-void cleanup_module(void)
-{
- gamma_cleanup();
-}
-#endif
+module_init(gamma_init);
+module_exit(gamma_cleanup);
#ifndef MODULE
-/* gamma_setup is called by the kernel to parse command-line options passed
- * via the boot-loader (e.g., LILO). It calls the insmod option routine,
- * drm_parse_options.
- *
- * This is not currently supported, since it requires changes to
- * linux/init/main.c. */
+/* gamma_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO). It calls the insmod option
+ * routine, drm_parse_options.
+ */
-void __init gamma_setup(char *str, int *ints)
+static int __init gamma_options(char *str)
{
- if (ints[0] != 0) {
- DRM_ERROR("Illegal command line format, ignored\n");
- return;
- }
drm_parse_options(str);
+ return 1;
}
+
+__setup("gamma=", gamma_options);
#endif
static int gamma_setup(drm_device_t *dev)
@@ -274,6 +276,10 @@ static int gamma_takedown(drm_device_t *dev)
- PAGE_SHIFT,
DRM_MEM_SAREA);
break;
+ case _DRM_AGP:
+ /* Do nothing here, because this is all
+ handled in the AGP/GART driver. */
+ break;
}
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
}
@@ -313,6 +319,34 @@ static int gamma_takedown(drm_device_t *dev)
return 0;
}
+int gamma_found(void)
+{
+ return devices;
+}
+
+int gamma_find_devices(void)
+{
+ struct pci_dev *d = NULL, *one = NULL, *two = NULL;
+
+ d = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_GAMMA,d);
+ if (!d) return 0;
+
+ one = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,d);
+ if (!one) return 0;
+
+ /* Make sure it's on the same card, if not - no MX's found */
+ if (PCI_SLOT(d->devfn) != PCI_SLOT(one->devfn)) return 0;
+
+ two = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,one);
+ if (!two) return 1;
+
+ /* Make sure it's on the same card, if not - only 1 MX found */
+ if (PCI_SLOT(d->devfn) != PCI_SLOT(two->devfn)) return 1;
+
+ /* Two MX's found - we don't currently support more than 2 */
+ return 2;
+}
+
/* gamma_init is called via init_module at module load time, or via
* linux/init/main.c (this is not currently supported). */
@@ -330,6 +364,8 @@ int gamma_init(void)
#ifdef MODULE
drm_parse_options(gamma);
#endif
+ devices = gamma_find_devices();
+ if (devices == 0) return -1;
if ((retcode = misc_register(&gamma_misc))) {
DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME);
@@ -341,13 +377,14 @@ int gamma_init(void)
drm_mem_init();
drm_proc_init(dev);
- DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d with %d MX devices\n",
GAMMA_NAME,
GAMMA_MAJOR,
GAMMA_MINOR,
GAMMA_PATCHLEVEL,
GAMMA_DATE,
- gamma_misc.minor);
+ gamma_misc.minor,
+ devices);
return 0;
}
@@ -410,6 +447,7 @@ int gamma_open(struct inode *inode, struct file *filp)
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_open_helper(inode, filp, dev))) {
+ MOD_INC_USE_COUNT;
atomic_inc(&dev->total_open);
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
@@ -424,13 +462,13 @@ int gamma_open(struct inode *inode, struct file *filp)
int gamma_release(struct inode *inode, struct file *filp)
{
drm_file_t *priv = filp->private_data;
- drm_device_t *dev;
+ drm_device_t *dev = priv->dev;
int retcode = 0;
- lock_kernel();
- dev = priv->dev;
DRM_DEBUG("open_count = %d\n", dev->open_count);
+ lock_kernel();
if (!(retcode = drm_release(inode, filp))) {
+ MOD_DEC_USE_COUNT;
atomic_inc(&dev->total_close);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
diff --git a/drivers/char/drm/gamma_drv.h b/drivers/char/drm/gamma_drv.h
index a87655cb9..2cfbf6c8d 100644
--- a/drivers/char/drm/gamma_drv.h
+++ b/drivers/char/drm/gamma_drv.h
@@ -2,6 +2,7 @@
* Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
@@ -53,5 +54,7 @@ extern int gamma_irq_install(drm_device_t *dev, int irq);
extern int gamma_irq_uninstall(drm_device_t *dev);
extern int gamma_control(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
+extern int gamma_find_devices(void);
+extern int gamma_found(void);
#endif
diff --git a/drivers/char/drm/i810_bufs.c b/drivers/char/drm/i810_bufs.c
new file mode 100644
index 000000000..fa1f84dcd
--- /dev/null
+++ b/drivers/char/drm/i810_bufs.c
@@ -0,0 +1,334 @@
+/* i810_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+#include "linux/un.h"
+
+int i810_addbufs_agp(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_device_dma_t *dma = dev->dma;
+ drm_buf_desc_t request;
+ drm_buf_entry_t *entry;
+ drm_buf_t *buf;
+ unsigned long offset;
+ unsigned long agp_offset;
+ int count;
+ int order;
+ int size;
+ int alignment;
+ int page_order;
+ int total;
+ int byte_count;
+ int i;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ count = request.count;
+ order = drm_order(request.size);
+ size = 1 << order;
+ agp_offset = request.agp_start;
+ alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+ page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+ total = PAGE_SIZE << page_order;
+ byte_count = 0;
+
+ if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+ if (dev->queue_count) return -EBUSY; /* Not while in use */
+ spin_lock(&dev->count_lock);
+ if (dev->buf_use) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ atomic_inc(&dev->buf_alloc);
+ spin_unlock(&dev->count_lock);
+
+ down(&dev->struct_sem);
+ entry = &dma->bufs[order];
+ if (entry->buf_count) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM; /* May only call once for each order */
+ }
+
+ entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+ DRM_MEM_BUFS);
+ if (!entry->buflist) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+ memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+ entry->buf_size = size;
+ entry->page_order = page_order;
+ offset = 0;
+
+ while(entry->buf_count < count) {
+ buf = &entry->buflist[entry->buf_count];
+ buf->idx = dma->buf_count + entry->buf_count;
+ buf->total = alignment;
+ buf->order = order;
+ buf->used = 0;
+ buf->offset = offset;
+ buf->bus_address = dev->agp->base + agp_offset + offset;
+ buf->address = (void *)(agp_offset + offset + dev->agp->base);
+ buf->next = NULL;
+ buf->waiting = 0;
+ buf->pending = 0;
+ init_waitqueue_head(&buf->dma_wait);
+ buf->pid = 0;
+
+ buf->dev_private = drm_alloc(sizeof(drm_i810_buf_priv_t),
+ DRM_MEM_BUFS);
+ buf->dev_priv_size = sizeof(drm_i810_buf_priv_t);
+ memset(buf->dev_private, 0, sizeof(drm_i810_buf_priv_t));
+
+#if DRM_DMA_HISTOGRAM
+ buf->time_queued = 0;
+ buf->time_dispatched = 0;
+ buf->time_completed = 0;
+ buf->time_freed = 0;
+#endif
+ offset = offset + alignment;
+ entry->buf_count++;
+ byte_count += PAGE_SIZE << page_order;
+
+ DRM_DEBUG("buffer %d @ %p\n",
+ entry->buf_count, buf->address);
+ }
+
+ dma->buflist = drm_realloc(dma->buflist,
+ dma->buf_count * sizeof(*dma->buflist),
+ (dma->buf_count + entry->buf_count)
+ * sizeof(*dma->buflist),
+ DRM_MEM_BUFS);
+ for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+ dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+ dma->buf_count += entry->buf_count;
+ dma->byte_count += byte_count;
+ drm_freelist_create(&entry->freelist, entry->buf_count);
+ for (i = 0; i < entry->buf_count; i++) {
+ drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+ }
+
+ up(&dev->struct_sem);
+
+ request.count = entry->buf_count;
+ request.size = size;
+
+ copy_to_user_ret((drm_buf_desc_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ atomic_dec(&dev->buf_alloc);
+ dma->flags = _DRM_DMA_USE_AGP;
+ return 0;
+}
+
+int i810_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_buf_desc_t request;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ if(request.flags & _DRM_AGP_BUFFER)
+ return i810_addbufs_agp(inode, filp, cmd, arg);
+ else
+ return -EINVAL;
+}
+
+int i810_infobufs(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_device_dma_t *dma = dev->dma;
+ drm_buf_info_t request;
+ int i;
+ int count;
+
+ if (!dma) return -EINVAL;
+
+ spin_lock(&dev->count_lock);
+ if (atomic_read(&dev->buf_alloc)) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ ++dev->buf_use; /* Can't allocate more after this call */
+ spin_unlock(&dev->count_lock);
+
+ copy_from_user_ret(&request,
+ (drm_buf_info_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+ if (dma->bufs[i].buf_count) ++count;
+ }
+
+ DRM_DEBUG("count = %d\n", count);
+
+ if (request.count >= count) {
+ for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+ if (dma->bufs[i].buf_count) {
+ copy_to_user_ret(&request.list[count].count,
+ &dma->bufs[i].buf_count,
+ sizeof(dma->bufs[0]
+ .buf_count),
+ -EFAULT);
+ copy_to_user_ret(&request.list[count].size,
+ &dma->bufs[i].buf_size,
+ sizeof(dma->bufs[0].buf_size),
+ -EFAULT);
+ copy_to_user_ret(&request.list[count].low_mark,
+ &dma->bufs[i]
+ .freelist.low_mark,
+ sizeof(dma->bufs[0]
+ .freelist.low_mark),
+ -EFAULT);
+ copy_to_user_ret(&request.list[count]
+ .high_mark,
+ &dma->bufs[i]
+ .freelist.high_mark,
+ sizeof(dma->bufs[0]
+ .freelist.high_mark),
+ -EFAULT);
+ DRM_DEBUG("%d %d %d %d %d\n",
+ i,
+ dma->bufs[i].buf_count,
+ dma->bufs[i].buf_size,
+ dma->bufs[i].freelist.low_mark,
+ dma->bufs[i].freelist.high_mark);
+ ++count;
+ }
+ }
+ }
+ request.count = count;
+
+ copy_to_user_ret((drm_buf_info_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ return 0;
+}
+
+int i810_markbufs(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_device_dma_t *dma = dev->dma;
+ drm_buf_desc_t request;
+ int order;
+ drm_buf_entry_t *entry;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ DRM_DEBUG("%d, %d, %d\n",
+ request.size, request.low_mark, request.high_mark);
+ order = drm_order(request.size);
+ if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+ entry = &dma->bufs[order];
+
+ if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+ return -EINVAL;
+ if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+ return -EINVAL;
+
+ entry->freelist.low_mark = request.low_mark;
+ entry->freelist.high_mark = request.high_mark;
+
+ return 0;
+}
+
+int i810_freebufs(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_device_dma_t *dma = dev->dma;
+ drm_buf_free_t request;
+ int i;
+ int idx;
+ drm_buf_t *buf;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_free_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ DRM_DEBUG("%d\n", request.count);
+ for (i = 0; i < request.count; i++) {
+ copy_from_user_ret(&idx,
+ &request.list[i],
+ sizeof(idx),
+ -EFAULT);
+ if (idx < 0 || idx >= dma->buf_count) {
+ DRM_ERROR("Index %d (of %d max)\n",
+ idx, dma->buf_count - 1);
+ return -EINVAL;
+ }
+ buf = dma->buflist[idx];
+ if (buf->pid != current->pid) {
+ DRM_ERROR("Process %d freeing buffer owned by %d\n",
+ current->pid, buf->pid);
+ return -EINVAL;
+ }
+ drm_free_buffer(dev, buf);
+ }
+
+ return 0;
+}
+
diff --git a/drivers/char/drm/i810_context.c b/drivers/char/drm/i810_context.c
new file mode 100644
index 000000000..689814db5
--- /dev/null
+++ b/drivers/char/drm/i810_context.c
@@ -0,0 +1,205 @@
+/* i810_context.c -- IOCTLs for i810 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#include <linux/sched.h>
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+
+static int i810_alloc_queue(drm_device_t *dev)
+{
+ int temp = drm_ctxbitmap_next(dev);
+ DRM_DEBUG("i810_alloc_queue: %d\n", temp);
+ return temp;
+}
+
+int i810_context_switch(drm_device_t *dev, int old, int new)
+{
+ char buf[64];
+
+ atomic_inc(&dev->total_ctx);
+
+ if (test_and_set_bit(0, &dev->context_flag)) {
+ DRM_ERROR("Reentering -- FIXME\n");
+ return -EBUSY;
+ }
+
+#if DRM_DMA_HISTOGRAM
+ dev->ctx_start = get_cycles();
+#endif
+
+ DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+ if (new == dev->last_context) {
+ clear_bit(0, &dev->context_flag);
+ return 0;
+ }
+
+ if (drm_flags & DRM_FLAG_NOCTX) {
+ i810_context_switch_complete(dev, new);
+ } else {
+ sprintf(buf, "C %d %d\n", old, new);
+ drm_write_string(dev, buf);
+ }
+
+ return 0;
+}
+
+int i810_context_switch_complete(drm_device_t *dev, int new)
+{
+ dev->last_context = new; /* PRE/POST: This is the _only_ writer. */
+ dev->last_switch = jiffies;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("Lock isn't held after context switch\n");
+ }
+
+ /* If a context switch is ever initiated
+ when the kernel holds the lock, release
+ that lock here. */
+#if DRM_DMA_HISTOGRAM
+ atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+ - dev->ctx_start)]);
+
+#endif
+ clear_bit(0, &dev->context_flag);
+ wake_up(&dev->context_wait);
+
+ return 0;
+}
+
+int i810_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_ctx_res_t res;
+ drm_ctx_t ctx;
+ int i;
+
+ DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+ copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+ if (res.count >= DRM_RESERVED_CONTEXTS) {
+ memset(&ctx, 0, sizeof(ctx));
+ for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+ ctx.handle = i;
+ copy_to_user_ret(&res.contexts[i],
+ &i,
+ sizeof(i),
+ -EFAULT);
+ }
+ }
+ res.count = DRM_RESERVED_CONTEXTS;
+ copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+ return 0;
+}
+
+int i810_addctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ if ((ctx.handle = i810_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+ /* Skip kernel's context and get a new one. */
+ ctx.handle = i810_alloc_queue(dev);
+ }
+ if (ctx.handle == -1) {
+ DRM_DEBUG("Not enough free contexts.\n");
+ /* Should this return -EBUSY instead? */
+ return -ENOMEM;
+ }
+ DRM_DEBUG("%d\n", ctx.handle);
+ copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+ return 0;
+}
+
+int i810_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ /* This does nothing for the i810 */
+ return 0;
+}
+
+int i810_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+ /* This is 0, because we don't hanlde any context flags */
+ ctx.flags = 0;
+ copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+ return 0;
+}
+
+int i810_switchctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ return i810_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int i810_newctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ i810_context_switch_complete(dev, ctx.handle);
+
+ return 0;
+}
+
+int i810_rmctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ if(ctx.handle != DRM_KERNEL_CONTEXT) {
+ drm_ctxbitmap_free(dev, ctx.handle);
+ }
+
+ return 0;
+}
diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c
new file mode 100644
index 000000000..901be5593
--- /dev/null
+++ b/drivers/char/drm/i810_dma.c
@@ -0,0 +1,1411 @@
+/* i810_dma.c -- DMA support for the i810 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ * Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+
+#include <linux/interrupt.h> /* For task queue support */
+
+/* in case we don't have a 2.3.99-pre6 kernel or later: */
+#ifndef VM_DONTCOPY
+#define VM_DONTCOPY 0
+#endif
+
+#define I810_BUF_FREE 2
+#define I810_BUF_CLIENT 1
+#define I810_BUF_HARDWARE 0
+
+#define I810_BUF_UNMAPPED 0
+#define I810_BUF_MAPPED 1
+
+#define I810_REG(reg) 2
+#define I810_BASE(reg) ((unsigned long) \
+ dev->maplist[I810_REG(reg)]->handle)
+#define I810_ADDR(reg) (I810_BASE(reg) + reg)
+#define I810_DEREF(reg) *(__volatile__ int *)I810_ADDR(reg)
+#define I810_READ(reg) I810_DEREF(reg)
+#define I810_WRITE(reg,val) do { I810_DEREF(reg) = val; } while (0)
+#define I810_DEREF16(reg) *(__volatile__ u16 *)I810_ADDR(reg)
+#define I810_READ16(reg) I810_DEREF16(reg)
+#define I810_WRITE16(reg,val) do { I810_DEREF16(reg) = val; } while (0)
+
+#define RING_LOCALS unsigned int outring, ringmask; volatile char *virt;
+
+#define BEGIN_LP_RING(n) do { \
+ if (I810_VERBOSE) \
+ DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \
+ n, __FUNCTION__); \
+ if (dev_priv->ring.space < n*4) \
+ i810_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 (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n"); \
+ dev_priv->ring.tail = outring; \
+ I810_WRITE(LP_RING + RING_TAIL, outring); \
+} while(0)
+
+#define OUT_RING(n) do { \
+ if (I810_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \
+ *(volatile unsigned int *)(virt + outring) = n; \
+ outring += 4; \
+ outring &= ringmask; \
+} while (0);
+
+static inline void i810_print_status_page(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ u32 *temp = (u32 *)dev_priv->hw_status_page;
+ int i;
+
+ DRM_DEBUG( "hw_status: Interrupt Status : %x\n", temp[0]);
+ DRM_DEBUG( "hw_status: LpRing Head ptr : %x\n", temp[1]);
+ DRM_DEBUG( "hw_status: IRing Head ptr : %x\n", temp[2]);
+ DRM_DEBUG( "hw_status: Reserved : %x\n", temp[3]);
+ DRM_DEBUG( "hw_status: Driver Counter : %d\n", temp[5]);
+ for(i = 6; i < dma->buf_count + 6; i++) {
+ DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 6, temp[i]);
+ }
+}
+
+static drm_buf_t *i810_freelist_get(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ int i;
+ int used;
+
+ /* Linear search might not be the best solution */
+
+ for (i = 0; i < dma->buf_count; i++) {
+ drm_buf_t *buf = dma->buflist[ i ];
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+ /* In use is already a pointer */
+ used = cmpxchg(buf_priv->in_use, I810_BUF_FREE,
+ I810_BUF_CLIENT);
+ if(used == I810_BUF_FREE) {
+ return buf;
+ }
+ }
+ return NULL;
+}
+
+/* This should only be called if the buffer is not sent to the hardware
+ * yet, the hardware updates in use for us once its on the ring buffer.
+ */
+
+static int i810_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+ int used;
+
+ /* In use is already a pointer */
+ used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, I810_BUF_FREE);
+ if(used != I810_BUF_CLIENT) {
+ DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct file_operations i810_buffer_fops = {
+ open: i810_open,
+ flush: drm_flush,
+ release: i810_release,
+ ioctl: i810_ioctl,
+ mmap: i810_mmap_buffers,
+ read: drm_read,
+ fasync: drm_fasync,
+ poll: drm_poll,
+};
+
+int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ drm_buf_t *buf = dev_priv->mmap_buffer;
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+
+ vma->vm_flags |= (VM_IO | VM_DONTCOPY);
+ vma->vm_file = filp;
+
+ buf_priv->currently_mapped = I810_BUF_MAPPED;
+
+ if (remap_page_range(vma->vm_start,
+ VM_OFFSET(vma),
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot)) return -EAGAIN;
+ return 0;
+}
+
+static int i810_map_buffer(drm_buf_t *buf, struct file *filp)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ struct file_operations *old_fops;
+ int retcode = 0;
+
+ if(buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL;
+
+ if(VM_DONTCOPY != 0) {
+ down(&current->mm->mmap_sem);
+ old_fops = filp->f_op;
+ filp->f_op = &i810_buffer_fops;
+ dev_priv->mmap_buffer = buf;
+ buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED,
+ buf->bus_address);
+ dev_priv->mmap_buffer = NULL;
+ filp->f_op = old_fops;
+ if ((unsigned long)buf_priv->virtual > -1024UL) {
+ /* Real error */
+ DRM_DEBUG("mmap error\n");
+ retcode = (signed int)buf_priv->virtual;
+ buf_priv->virtual = 0;
+ }
+ up(&current->mm->mmap_sem);
+ } else {
+ buf_priv->virtual = buf_priv->kernel_virtual;
+ buf_priv->currently_mapped = I810_BUF_MAPPED;
+ }
+ return retcode;
+}
+
+static int i810_unmap_buffer(drm_buf_t *buf)
+{
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+ int retcode = 0;
+
+ if(VM_DONTCOPY != 0) {
+ if(buf_priv->currently_mapped != I810_BUF_MAPPED)
+ return -EINVAL;
+ down(&current->mm->mmap_sem);
+#if LINUX_VERSION_CODE < 0x020399
+ retcode = do_munmap((unsigned long)buf_priv->virtual,
+ (size_t) buf->total);
+#else
+ retcode = do_munmap(current->mm,
+ (unsigned long)buf_priv->virtual,
+ (size_t) buf->total);
+#endif
+ up(&current->mm->mmap_sem);
+ }
+ buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+ buf_priv->virtual = 0;
+
+ return retcode;
+}
+
+static int i810_dma_get_buffer(drm_device_t *dev, drm_i810_dma_t *d,
+ struct file *filp)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_buf_t *buf;
+ drm_i810_buf_priv_t *buf_priv;
+ int retcode = 0;
+
+ buf = i810_freelist_get(dev);
+ if (!buf) {
+ retcode = -ENOMEM;
+ DRM_DEBUG("%s retcode %d\n", __FUNCTION__, retcode);
+ goto out_get_buf;
+ }
+
+ retcode = i810_map_buffer(buf, filp);
+ if(retcode) {
+ i810_freelist_put(dev, buf);
+ DRM_DEBUG("mapbuf failed in %s retcode %d\n",
+ __FUNCTION__, retcode);
+ goto out_get_buf;
+ }
+ buf->pid = priv->pid;
+ buf_priv = buf->dev_private;
+ d->granted = 1;
+ d->request_idx = buf->idx;
+ d->request_size = buf->total;
+ d->virtual = buf_priv->virtual;
+
+out_get_buf:
+ return retcode;
+}
+
+static unsigned long i810_alloc_page(drm_device_t *dev)
+{
+ unsigned long address;
+
+ address = __get_free_page(GFP_KERNEL);
+ if(address == 0UL)
+ return 0;
+
+ atomic_inc(&mem_map[MAP_NR((void *) address)].count);
+ set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags);
+
+ return address;
+}
+
+static void i810_free_page(drm_device_t *dev, unsigned long page)
+{
+ if(page == 0UL)
+ return;
+
+ atomic_dec(&mem_map[MAP_NR((void *) page)].count);
+ clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags);
+ wake_up(&mem_map[MAP_NR((void *) page)].wait);
+ free_page(page);
+ return;
+}
+
+static int i810_dma_cleanup(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+
+ if(dev->dev_private) {
+ int i;
+ drm_i810_private_t *dev_priv =
+ (drm_i810_private_t *) dev->dev_private;
+
+ if(dev_priv->ring.virtual_start) {
+ drm_ioremapfree((void *) dev_priv->ring.virtual_start,
+ dev_priv->ring.Size);
+ }
+ if(dev_priv->hw_status_page != 0UL) {
+ i810_free_page(dev, dev_priv->hw_status_page);
+ /* Need to rewrite hardware status page */
+ I810_WRITE(0x02080, 0x1ffff000);
+ }
+ drm_free(dev->dev_private, sizeof(drm_i810_private_t),
+ DRM_MEM_DRIVER);
+ dev->dev_private = NULL;
+
+ for (i = 0; i < dma->buf_count; i++) {
+ drm_buf_t *buf = dma->buflist[ i ];
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+ drm_ioremapfree(buf_priv->kernel_virtual, buf->total);
+ }
+ }
+ return 0;
+}
+
+static int i810_wait_ring(drm_device_t *dev, int n)
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+ int iters = 0;
+ unsigned long end;
+ unsigned int last_head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+
+ end = jiffies + (HZ*3);
+ while (ring->space < n) {
+ int i;
+
+ ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+ ring->space = ring->head - (ring->tail+8);
+ if (ring->space < 0) ring->space += ring->Size;
+
+ if (ring->head != last_head)
+ end = jiffies + (HZ*3);
+
+ iters++;
+ if((signed)(end - jiffies) <= 0) {
+ DRM_ERROR("space: %d wanted %d\n", ring->space, n);
+ DRM_ERROR("lockup\n");
+ goto out_wait_ring;
+ }
+
+ for (i = 0 ; i < 2000 ; i++) ;
+ }
+
+out_wait_ring:
+ return iters;
+}
+
+static void i810_kernel_lost_context(drm_device_t *dev)
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+
+ ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+ ring->tail = I810_READ(LP_RING + RING_TAIL);
+ ring->space = ring->head - (ring->tail+8);
+ if (ring->space < 0) ring->space += ring->Size;
+}
+
+static int i810_freelist_init(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+ int my_idx = 24;
+ u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx);
+ int i;
+
+ if(dma->buf_count > 1019) {
+ /* Not enough space in the status page for the freelist */
+ return -EINVAL;
+ }
+
+ for (i = 0; i < dma->buf_count; i++) {
+ drm_buf_t *buf = dma->buflist[ i ];
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+
+ buf_priv->in_use = hw_status++;
+ buf_priv->my_use_idx = my_idx;
+ my_idx += 4;
+
+ *buf_priv->in_use = I810_BUF_FREE;
+
+ buf_priv->kernel_virtual = drm_ioremap(buf->bus_address,
+ buf->total);
+ }
+ return 0;
+}
+
+static int i810_dma_initialize(drm_device_t *dev,
+ drm_i810_private_t *dev_priv,
+ drm_i810_init_t *init)
+{
+ drm_map_t *sarea_map;
+
+ dev->dev_private = (void *) dev_priv;
+ memset(dev_priv, 0, sizeof(drm_i810_private_t));
+
+ if (init->ring_map_idx >= dev->map_count ||
+ init->buffer_map_idx >= dev->map_count) {
+ i810_dma_cleanup(dev);
+ DRM_ERROR("ring_map or buffer_map are invalid\n");
+ return -EINVAL;
+ }
+
+ dev_priv->ring_map_idx = init->ring_map_idx;
+ dev_priv->buffer_map_idx = init->buffer_map_idx;
+ sarea_map = dev->maplist[0];
+ dev_priv->sarea_priv = (drm_i810_sarea_t *)
+ ((u8 *)sarea_map->handle +
+ init->sarea_priv_offset);
+
+ atomic_set(&dev_priv->flush_done, 0);
+ init_waitqueue_head(&dev_priv->flush_queue);
+
+ dev_priv->ring.Start = init->ring_start;
+ dev_priv->ring.End = init->ring_end;
+ dev_priv->ring.Size = init->ring_size;
+
+ dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base +
+ init->ring_start,
+ init->ring_size);
+
+ dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
+
+ if (dev_priv->ring.virtual_start == NULL) {
+ i810_dma_cleanup(dev);
+ DRM_ERROR("can not ioremap virtual address for"
+ " ring buffer\n");
+ return -ENOMEM;
+ }
+
+ dev_priv->w = init->w;
+ dev_priv->h = init->h;
+ dev_priv->pitch = init->pitch;
+ dev_priv->back_offset = init->back_offset;
+ dev_priv->depth_offset = init->depth_offset;
+
+ dev_priv->front_di1 = init->front_offset | init->pitch_bits;
+ dev_priv->back_di1 = init->back_offset | init->pitch_bits;
+ dev_priv->zi1 = init->depth_offset | init->pitch_bits;
+
+
+ /* Program Hardware Status Page */
+ dev_priv->hw_status_page = i810_alloc_page(dev);
+ memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE);
+ if(dev_priv->hw_status_page == 0UL) {
+ i810_dma_cleanup(dev);
+ DRM_ERROR("Can not allocate hardware status page\n");
+ return -ENOMEM;
+ }
+ DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page);
+
+ I810_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page));
+ DRM_DEBUG("Enabled hardware status page\n");
+
+ /* Now we need to init our freelist */
+ if(i810_freelist_init(dev) != 0) {
+ i810_dma_cleanup(dev);
+ DRM_ERROR("Not enough space in the status page for"
+ " the freelist\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+int i810_dma_init(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_i810_private_t *dev_priv;
+ drm_i810_init_t init;
+ int retcode = 0;
+
+ copy_from_user_ret(&init, (drm_i810_init_t *)arg,
+ sizeof(init), -EFAULT);
+
+ switch(init.func) {
+ case I810_INIT_DMA:
+ dev_priv = drm_alloc(sizeof(drm_i810_private_t),
+ DRM_MEM_DRIVER);
+ if(dev_priv == NULL) return -ENOMEM;
+ retcode = i810_dma_initialize(dev, dev_priv, &init);
+ break;
+ case I810_CLEANUP_DMA:
+ retcode = i810_dma_cleanup(dev);
+ break;
+ default:
+ retcode = -EINVAL;
+ break;
+ }
+
+ return retcode;
+}
+
+
+
+/* Most efficient way to verify state for the i810 is as it is
+ * emitted. Non-conformant state is silently dropped.
+ *
+ * Use 'volatile' & local var tmp to force the emitted values to be
+ * identical to the verified ones.
+ */
+static void i810EmitContextVerified( drm_device_t *dev,
+ volatile unsigned int *code )
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ int i, j = 0;
+ unsigned int tmp;
+ RING_LOCALS;
+
+ BEGIN_LP_RING( I810_CTX_SETUP_SIZE );
+
+ OUT_RING( GFX_OP_COLOR_FACTOR );
+ OUT_RING( code[I810_CTXREG_CF1] );
+
+ OUT_RING( GFX_OP_STIPPLE );
+ OUT_RING( code[I810_CTXREG_ST1] );
+
+ for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) {
+ tmp = code[i];
+
+ if ((tmp & (7<<29)) == (3<<29) &&
+ (tmp & (0x1f<<24)) < (0x1d<<24))
+ {
+ OUT_RING( tmp );
+ j++;
+ }
+ }
+
+ if (j & 1)
+ OUT_RING( 0 );
+
+ ADVANCE_LP_RING();
+}
+
+static void i810EmitTexVerified( drm_device_t *dev,
+ volatile unsigned int *code )
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ int i, j = 0;
+ unsigned int tmp;
+ RING_LOCALS;
+
+ BEGIN_LP_RING( I810_TEX_SETUP_SIZE );
+
+ OUT_RING( GFX_OP_MAP_INFO );
+ OUT_RING( code[I810_TEXREG_MI1] );
+ OUT_RING( code[I810_TEXREG_MI2] );
+ OUT_RING( code[I810_TEXREG_MI3] );
+
+ for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) {
+ tmp = code[i];
+
+ if ((tmp & (7<<29)) == (3<<29) &&
+ (tmp & (0x1f<<24)) < (0x1d<<24))
+ {
+ OUT_RING( tmp );
+ j++;
+ }
+ }
+
+ if (j & 1)
+ OUT_RING( 0 );
+
+ ADVANCE_LP_RING();
+}
+
+
+/* Need to do some additional checking when setting the dest buffer.
+ */
+static void i810EmitDestVerified( drm_device_t *dev,
+ volatile unsigned int *code )
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ unsigned int tmp;
+ RING_LOCALS;
+
+ BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 );
+
+ tmp = code[I810_DESTREG_DI1];
+ if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) {
+ OUT_RING( CMD_OP_DESTBUFFER_INFO );
+ OUT_RING( tmp );
+ } else
+ DRM_DEBUG("bad di1 %x (allow %x or %x)\n",
+ tmp, dev_priv->front_di1, dev_priv->back_di1);
+
+ /* invarient:
+ */
+ OUT_RING( CMD_OP_Z_BUFFER_INFO );
+ OUT_RING( dev_priv->zi1 );
+
+ OUT_RING( GFX_OP_DESTBUFFER_VARS );
+ OUT_RING( code[I810_DESTREG_DV1] );
+
+ OUT_RING( GFX_OP_DRAWRECT_INFO );
+ OUT_RING( code[I810_DESTREG_DR1] );
+ OUT_RING( code[I810_DESTREG_DR2] );
+ OUT_RING( code[I810_DESTREG_DR3] );
+ OUT_RING( code[I810_DESTREG_DR4] );
+ OUT_RING( 0 );
+
+ ADVANCE_LP_RING();
+}
+
+
+
+static void i810EmitState( drm_device_t *dev )
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int dirty = sarea_priv->dirty;
+
+ if (dirty & I810_UPLOAD_BUFFERS) {
+ i810EmitDestVerified( dev, sarea_priv->BufferState );
+ sarea_priv->dirty &= ~I810_UPLOAD_BUFFERS;
+ }
+
+ if (dirty & I810_UPLOAD_CTX) {
+ i810EmitContextVerified( dev, sarea_priv->ContextState );
+ sarea_priv->dirty &= ~I810_UPLOAD_CTX;
+ }
+
+ if (dirty & I810_UPLOAD_TEX0) {
+ i810EmitTexVerified( dev, sarea_priv->TexState[0] );
+ sarea_priv->dirty &= ~I810_UPLOAD_TEX0;
+ }
+
+ if (dirty & I810_UPLOAD_TEX1) {
+ i810EmitTexVerified( dev, sarea_priv->TexState[1] );
+ sarea_priv->dirty &= ~I810_UPLOAD_TEX1;
+ }
+}
+
+
+
+/* need to verify
+ */
+static void i810_dma_dispatch_clear( drm_device_t *dev, int flags,
+ unsigned int clear_color,
+ unsigned int clear_zval )
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ int nbox = sarea_priv->nbox;
+ drm_clip_rect_t *pbox = sarea_priv->boxes;
+ int pitch = dev_priv->pitch;
+ int cpp = 2;
+ int i;
+ RING_LOCALS;
+
+ i810_kernel_lost_context(dev);
+
+ if (nbox > I810_NR_SAREA_CLIPRECTS)
+ nbox = I810_NR_SAREA_CLIPRECTS;
+
+ for (i = 0 ; i < nbox ; i++, pbox++) {
+ unsigned int x = pbox->x1;
+ unsigned int y = pbox->y1;
+ unsigned int width = (pbox->x2 - x) * cpp;
+ unsigned int height = pbox->y2 - y;
+ unsigned int start = y * pitch + x * cpp;
+
+ if (pbox->x1 > pbox->x2 ||
+ pbox->y1 > pbox->y2 ||
+ pbox->x2 > dev_priv->w ||
+ pbox->y2 > dev_priv->h)
+ continue;
+
+ if ( flags & I810_FRONT ) {
+ DRM_DEBUG("clear front\n");
+ BEGIN_LP_RING( 6 );
+ OUT_RING( BR00_BITBLT_CLIENT |
+ BR00_OP_COLOR_BLT | 0x3 );
+ OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+ OUT_RING( (height << 16) | width );
+ OUT_RING( start );
+ OUT_RING( clear_color );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+ }
+
+ if ( flags & I810_BACK ) {
+ DRM_DEBUG("clear back\n");
+ BEGIN_LP_RING( 6 );
+ OUT_RING( BR00_BITBLT_CLIENT |
+ BR00_OP_COLOR_BLT | 0x3 );
+ OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+ OUT_RING( (height << 16) | width );
+ OUT_RING( dev_priv->back_offset + start );
+ OUT_RING( clear_color );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+ }
+
+ if ( flags & I810_DEPTH ) {
+ DRM_DEBUG("clear depth\n");
+ BEGIN_LP_RING( 6 );
+ OUT_RING( BR00_BITBLT_CLIENT |
+ BR00_OP_COLOR_BLT | 0x3 );
+ OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+ OUT_RING( (height << 16) | width );
+ OUT_RING( dev_priv->depth_offset + start );
+ OUT_RING( clear_zval );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+ }
+ }
+}
+
+static void i810_dma_dispatch_swap( drm_device_t *dev )
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ int nbox = sarea_priv->nbox;
+ drm_clip_rect_t *pbox = sarea_priv->boxes;
+ int pitch = dev_priv->pitch;
+ int cpp = 2;
+ int ofs = dev_priv->back_offset;
+ int i;
+ RING_LOCALS;
+
+ DRM_DEBUG("swapbuffers\n");
+
+ i810_kernel_lost_context(dev);
+
+ if (nbox > I810_NR_SAREA_CLIPRECTS)
+ nbox = I810_NR_SAREA_CLIPRECTS;
+
+ for (i = 0 ; i < nbox; i++, pbox++)
+ {
+ unsigned int w = pbox->x2 - pbox->x1;
+ unsigned int h = pbox->y2 - pbox->y1;
+ unsigned int dst = pbox->x1*cpp + pbox->y1*pitch;
+ unsigned int start = ofs + dst;
+
+ if (pbox->x1 > pbox->x2 ||
+ pbox->y1 > pbox->y2 ||
+ pbox->x2 > dev_priv->w ||
+ pbox->y2 > dev_priv->h)
+ continue;
+
+ DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n",
+ pbox[i].x1, pbox[i].y1,
+ pbox[i].x2, pbox[i].y2);
+
+ BEGIN_LP_RING( 6 );
+ OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 );
+ OUT_RING( pitch | (0xCC << 16));
+ OUT_RING( (h << 16) | (w * cpp));
+ OUT_RING( dst );
+ OUT_RING( pitch );
+ OUT_RING( start );
+ ADVANCE_LP_RING();
+ }
+}
+
+
+static void i810_dma_dispatch_vertex(drm_device_t *dev,
+ drm_buf_t *buf,
+ int discard,
+ int used)
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+ drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ drm_clip_rect_t *box = sarea_priv->boxes;
+ int nbox = sarea_priv->nbox;
+ unsigned long address = (unsigned long)buf->bus_address;
+ unsigned long start = address - dev->agp->base;
+ int i = 0, u;
+ RING_LOCALS;
+
+ i810_kernel_lost_context(dev);
+
+ if (nbox > I810_NR_SAREA_CLIPRECTS)
+ nbox = I810_NR_SAREA_CLIPRECTS;
+
+ if (discard) {
+ u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT,
+ I810_BUF_HARDWARE);
+ if(u != I810_BUF_CLIENT) {
+ DRM_DEBUG("xxxx 2\n");
+ }
+ }
+
+ if (used > 4*1024)
+ used = 0;
+
+ if (sarea_priv->dirty)
+ i810EmitState( dev );
+
+ DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n",
+ address, used, nbox);
+
+ dev_priv->counter++;
+ DRM_DEBUG( "dispatch counter : %ld\n", dev_priv->counter);
+ DRM_DEBUG( "i810_dma_dispatch\n");
+ DRM_DEBUG( "start : %lx\n", start);
+ DRM_DEBUG( "used : %d\n", used);
+ DRM_DEBUG( "start + used - 4 : %ld\n", start + used - 4);
+
+ if (buf_priv->currently_mapped == I810_BUF_MAPPED) {
+ *(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE |
+ sarea_priv->vertex_prim |
+ ((used/4)-2));
+
+ if (used & 4) {
+ *(u32 *)((u32)buf_priv->virtual + used) = 0;
+ used += 4;
+ }
+
+ i810_unmap_buffer(buf);
+ }
+
+ if (used) {
+ do {
+ if (i < nbox) {
+ BEGIN_LP_RING(4);
+ OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR |
+ SC_ENABLE );
+ OUT_RING( GFX_OP_SCISSOR_INFO );
+ OUT_RING( box[i].x1 | (box[i].y1<<16) );
+ OUT_RING( (box[i].x2-1) | ((box[i].y2-1)<<16) );
+ ADVANCE_LP_RING();
+ }
+
+ BEGIN_LP_RING(4);
+ OUT_RING( CMD_OP_BATCH_BUFFER );
+ OUT_RING( start | BB1_PROTECTED );
+ OUT_RING( start + used - 4 );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+
+ } while (++i < nbox);
+ }
+
+ BEGIN_LP_RING(10);
+ OUT_RING( CMD_STORE_DWORD_IDX );
+ OUT_RING( 20 );
+ OUT_RING( dev_priv->counter );
+ OUT_RING( 0 );
+
+ if (discard) {
+ OUT_RING( CMD_STORE_DWORD_IDX );
+ OUT_RING( buf_priv->my_use_idx );
+ OUT_RING( I810_BUF_FREE );
+ OUT_RING( 0 );
+ }
+
+ OUT_RING( CMD_REPORT_HEAD );
+ OUT_RING( 0 );
+ ADVANCE_LP_RING();
+}
+
+
+/* Interrupts are only for flushing */
+static void i810_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+ drm_device_t *dev = (drm_device_t *)device;
+ u16 temp;
+
+ atomic_inc(&dev->total_irq);
+ temp = I810_READ16(I810REG_INT_IDENTITY_R);
+ temp = temp & ~(0x6000);
+ if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R,
+ temp); /* Clear all interrupts */
+ else
+ return;
+
+ queue_task(&dev->tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static void i810_dma_task_queue(void *device)
+{
+ drm_device_t *dev = (drm_device_t *) device;
+ drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+
+ atomic_set(&dev_priv->flush_done, 1);
+ wake_up_interruptible(&dev_priv->flush_queue);
+}
+
+int i810_irq_install(drm_device_t *dev, int irq)
+{
+ int retcode;
+ u16 temp;
+
+ if (!irq) return -EINVAL;
+
+ down(&dev->struct_sem);
+ if (dev->irq) {
+ up(&dev->struct_sem);
+ return -EBUSY;
+ }
+ dev->irq = irq;
+ up(&dev->struct_sem);
+
+ DRM_DEBUG( "Interrupt Install : %d\n", irq);
+ DRM_DEBUG("%d\n", irq);
+
+ dev->context_flag = 0;
+ dev->interrupt_flag = 0;
+ dev->dma_flag = 0;
+
+ dev->dma->next_buffer = NULL;
+ dev->dma->next_queue = NULL;
+ dev->dma->this_buffer = NULL;
+
+ dev->tq.next = NULL;
+ dev->tq.sync = 0;
+ dev->tq.routine = i810_dma_task_queue;
+ dev->tq.data = dev;
+
+ /* Before installing handler */
+ temp = I810_READ16(I810REG_HWSTAM);
+ temp = temp & 0x6000;
+ I810_WRITE16(I810REG_HWSTAM, temp);
+
+ temp = I810_READ16(I810REG_INT_MASK_R);
+ temp = temp & 0x6000;
+ I810_WRITE16(I810REG_INT_MASK_R, temp); /* Unmask interrupts */
+ temp = I810_READ16(I810REG_INT_ENABLE_R);
+ temp = temp & 0x6000;
+ I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */
+
+ /* Install handler */
+ if ((retcode = request_irq(dev->irq,
+ i810_dma_service,
+ SA_SHIRQ,
+ dev->devname,
+ dev))) {
+ down(&dev->struct_sem);
+ dev->irq = 0;
+ up(&dev->struct_sem);
+ return retcode;
+ }
+ temp = I810_READ16(I810REG_INT_ENABLE_R);
+ temp = temp & 0x6000;
+ temp = temp | 0x0003;
+ I810_WRITE16(I810REG_INT_ENABLE_R,
+ temp); /* Enable bp & user interrupts */
+ return 0;
+}
+
+int i810_irq_uninstall(drm_device_t *dev)
+{
+ int irq;
+ u16 temp;
+
+
+/* return 0; */
+
+ down(&dev->struct_sem);
+ irq = dev->irq;
+ dev->irq = 0;
+ up(&dev->struct_sem);
+
+ if (!irq) return -EINVAL;
+
+ DRM_DEBUG( "Interrupt UnInstall: %d\n", irq);
+ DRM_DEBUG("%d\n", irq);
+
+ temp = I810_READ16(I810REG_INT_IDENTITY_R);
+ temp = temp & ~(0x6000);
+ if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R,
+ temp); /* Clear all interrupts */
+
+ temp = I810_READ16(I810REG_INT_ENABLE_R);
+ temp = temp & 0x6000;
+ I810_WRITE16(I810REG_INT_ENABLE_R,
+ temp); /* Disable all interrupts */
+
+ free_irq(irq, dev);
+
+ return 0;
+}
+
+int i810_control(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_control_t ctl;
+ int retcode;
+
+ DRM_DEBUG( "i810_control\n");
+
+ copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT);
+
+ switch (ctl.func) {
+ case DRM_INST_HANDLER:
+ if ((retcode = i810_irq_install(dev, ctl.irq)))
+ return retcode;
+ break;
+ case DRM_UNINST_HANDLER:
+ if ((retcode = i810_irq_uninstall(dev)))
+ return retcode;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static inline void i810_dma_emit_flush(drm_device_t *dev)
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ RING_LOCALS;
+
+ i810_kernel_lost_context(dev);
+
+ BEGIN_LP_RING(2);
+ OUT_RING( CMD_REPORT_HEAD );
+ OUT_RING( GFX_OP_USER_INTERRUPT );
+ ADVANCE_LP_RING();
+
+/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/* atomic_set(&dev_priv->flush_done, 1); */
+/* wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static inline void i810_dma_quiescent_emit(drm_device_t *dev)
+{
+ drm_i810_private_t *dev_priv = dev->dev_private;
+ RING_LOCALS;
+
+ i810_kernel_lost_context(dev);
+
+ BEGIN_LP_RING(4);
+ OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE );
+ OUT_RING( CMD_REPORT_HEAD );
+ OUT_RING( 0 );
+ OUT_RING( GFX_OP_USER_INTERRUPT );
+ ADVANCE_LP_RING();
+
+/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/* atomic_set(&dev_priv->flush_done, 1); */
+/* wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static void i810_dma_quiescent(drm_device_t *dev)
+{
+ DECLARE_WAITQUEUE(entry, current);
+ drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+ unsigned long end;
+
+ if(dev_priv == NULL) {
+ return;
+ }
+ atomic_set(&dev_priv->flush_done, 0);
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&dev_priv->flush_queue, &entry);
+ end = jiffies + (HZ*3);
+
+ for (;;) {
+ i810_dma_quiescent_emit(dev);
+ if (atomic_read(&dev_priv->flush_done) == 1) break;
+ if((signed)(end - jiffies) <= 0) {
+ DRM_ERROR("lockup\n");
+ break;
+ }
+ schedule_timeout(HZ*3);
+ if (signal_pending(current)) {
+ break;
+ }
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev_priv->flush_queue, &entry);
+
+ return;
+}
+
+static int i810_flush_queue(drm_device_t *dev)
+{
+ DECLARE_WAITQUEUE(entry, current);
+ drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+ unsigned long end;
+ int i, ret = 0;
+
+ if(dev_priv == NULL) {
+ return 0;
+ }
+ atomic_set(&dev_priv->flush_done, 0);
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&dev_priv->flush_queue, &entry);
+ end = jiffies + (HZ*3);
+ for (;;) {
+ i810_dma_emit_flush(dev);
+ if (atomic_read(&dev_priv->flush_done) == 1) break;
+ if((signed)(end - jiffies) <= 0) {
+ DRM_ERROR("lockup\n");
+ break;
+ }
+ schedule_timeout(HZ*3);
+ if (signal_pending(current)) {
+ ret = -EINTR; /* Can't restart */
+ break;
+ }
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev_priv->flush_queue, &entry);
+
+
+ for (i = 0; i < dma->buf_count; i++) {
+ drm_buf_t *buf = dma->buflist[ i ];
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+
+ int used = cmpxchg(buf_priv->in_use, I810_BUF_HARDWARE,
+ I810_BUF_FREE);
+
+ if (used == I810_BUF_HARDWARE)
+ DRM_DEBUG("reclaimed from HARDWARE\n");
+ if (used == I810_BUF_CLIENT)
+ DRM_DEBUG("still on client HARDWARE\n");
+ }
+
+ return ret;
+}
+
+/* Must be called with the lock held */
+void i810_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+ drm_device_dma_t *dma = dev->dma;
+ int i;
+
+ if (!dma) return;
+ if (!dev->dev_private) return;
+ if (!dma->buflist) return;
+
+ i810_flush_queue(dev);
+
+ for (i = 0; i < dma->buf_count; i++) {
+ drm_buf_t *buf = dma->buflist[ i ];
+ drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+
+ if (buf->pid == pid && buf_priv) {
+ int used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT,
+ I810_BUF_FREE);
+
+ if (used == I810_BUF_CLIENT)
+ DRM_DEBUG("reclaimed from client\n");
+ if(buf_priv->currently_mapped == I810_BUF_MAPPED)
+ buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+ }
+ }
+}
+
+int i810_lock(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;
+
+ DECLARE_WAITQUEUE(entry, current);
+ int ret = 0;
+ drm_lock_t lock;
+
+ copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+ if (lock.context == DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("Process %d using kernel context %d\n",
+ current->pid, lock.context);
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+ lock.context, current->pid, dev->lock.hw_lock->lock,
+ lock.flags);
+
+ if (lock.context < 0) {
+ return -EINVAL;
+ }
+ /* Only one queue:
+ */
+
+ if (!ret) {
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ ret = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ lock.context)) {
+ dev->lock.pid = current->pid;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->total_locks);
+ break; /* Got lock */
+ }
+
+ /* Contention */
+ atomic_inc(&dev->total_sleeps);
+ current->state = TASK_INTERRUPTIBLE;
+ DRM_DEBUG("Calling lock schedule\n");
+ schedule();
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ }
+
+ if (!ret) {
+ if (lock.flags & _DRM_LOCK_QUIESCENT) {
+ DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+ DRM_DEBUG("fred\n");
+ i810_dma_quiescent(dev);
+ }
+ }
+ DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+ return ret;
+}
+
+int i810_flush_ioctl(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_DEBUG("i810_flush_ioctl\n");
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i810_flush_ioctl called without lock held\n");
+ return -EINVAL;
+ }
+
+ i810_flush_queue(dev);
+ return 0;
+}
+
+
+int i810_dma_vertex(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_device_dma_t *dma = dev->dma;
+ drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+ u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+ drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *)
+ dev_priv->sarea_priv;
+ drm_i810_vertex_t vertex;
+
+ copy_from_user_ret(&vertex, (drm_i810_vertex_t *)arg, sizeof(vertex),
+ -EFAULT);
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i810_dma_vertex called without lock held\n");
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n",
+ vertex.idx, vertex.used, vertex.discard);
+
+ i810_dma_dispatch_vertex( dev,
+ dma->buflist[ vertex.idx ],
+ vertex.discard, vertex.used );
+
+ atomic_add(vertex.used, &dma->total_bytes);
+ atomic_inc(&dma->total_dmas);
+ sarea_priv->last_enqueue = dev_priv->counter-1;
+ sarea_priv->last_dispatch = (int) hw_status[5];
+
+ return 0;
+}
+
+
+
+int i810_clear_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_i810_clear_t clear;
+
+ copy_from_user_ret(&clear, (drm_i810_clear_t *)arg, sizeof(clear),
+ -EFAULT);
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i810_clear_bufs called without lock held\n");
+ return -EINVAL;
+ }
+
+ i810_dma_dispatch_clear( dev, clear.flags,
+ clear.clear_color,
+ clear.clear_depth );
+ return 0;
+}
+
+int i810_swap_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_DEBUG("i810_swap_bufs\n");
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i810_swap_buf called without lock held\n");
+ return -EINVAL;
+ }
+
+ i810_dma_dispatch_swap( dev );
+ return 0;
+}
+
+int i810_getage(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_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+ u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+ drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *)
+ dev_priv->sarea_priv;
+
+ sarea_priv->last_dispatch = (int) hw_status[5];
+ return 0;
+}
+
+int i810_getbuf(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;
+ int retcode = 0;
+ drm_i810_dma_t d;
+ drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+ u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+ drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *)
+ dev_priv->sarea_priv;
+
+ DRM_DEBUG("getbuf\n");
+ copy_from_user_ret(&d, (drm_i810_dma_t *)arg, sizeof(d), -EFAULT);
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i810_dma called without lock held\n");
+ return -EINVAL;
+ }
+
+ d.granted = 0;
+
+ retcode = i810_dma_get_buffer(dev, &d, filp);
+
+ DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n",
+ current->pid, retcode, d.granted);
+
+ copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT);
+ sarea_priv->last_dispatch = (int) hw_status[5];
+
+ return retcode;
+}
+
+int i810_copybuf(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_i810_copy_t d;
+ drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+ u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+ drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *)
+ dev_priv->sarea_priv;
+ drm_buf_t *buf;
+ drm_i810_buf_priv_t *buf_priv;
+ drm_device_dma_t *dma = dev->dma;
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("i810_dma called without lock held\n");
+ return -EINVAL;
+ }
+
+ copy_from_user_ret(&d, (drm_i810_copy_t *)arg, sizeof(d), -EFAULT);
+
+ if(d.idx > dma->buf_count) return -EINVAL;
+ buf = dma->buflist[ d.idx ];
+ buf_priv = buf->dev_private;
+ if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EPERM;
+
+ copy_from_user_ret(buf_priv->virtual, d.address, d.used, -EFAULT);
+
+ sarea_priv->last_dispatch = (int) hw_status[5];
+
+ return 0;
+}
+
+int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ if(VM_DONTCOPY == 0) return 1;
+ return 0;
+}
diff --git a/drivers/char/drm/i810_drm.h b/drivers/char/drm/i810_drm.h
new file mode 100644
index 000000000..c5f51c9ad
--- /dev/null
+++ b/drivers/char/drm/i810_drm.h
@@ -0,0 +1,194 @@
+#ifndef _I810_DRM_H_
+#define _I810_DRM_H_
+
+/* WARNING: These defines must be the same as what the Xserver uses.
+ * if you change them, you must change the defines in the Xserver.
+ */
+
+#ifndef _I810_DEFINES_
+#define _I810_DEFINES_
+
+#define I810_DMA_BUF_ORDER 12
+#define I810_DMA_BUF_SZ (1<<I810_DMA_BUF_ORDER)
+#define I810_DMA_BUF_NR 256
+#define I810_NR_SAREA_CLIPRECTS 8
+
+/* Each region is a minimum of 64k, and there are at most 64 of them.
+ */
+#define I810_NR_TEX_REGIONS 64
+#define I810_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+#define I810_UPLOAD_TEX0IMAGE 0x1 /* handled clientside */
+#define I810_UPLOAD_TEX1IMAGE 0x2 /* handled clientside */
+#define I810_UPLOAD_CTX 0x4
+#define I810_UPLOAD_BUFFERS 0x8
+#define I810_UPLOAD_TEX0 0x10
+#define I810_UPLOAD_TEX1 0x20
+#define I810_UPLOAD_CLIPRECTS 0x40
+
+
+/* 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,
+ * or in a piecewise fashion as required.
+ */
+
+/* Destbuffer state
+ * - backbuffer linear offset and pitch -- invarient in the current dri
+ * - zbuffer linear offset and pitch -- also invarient
+ * - drawing origin in back and depth buffers.
+ *
+ * Keep the depth/back buffer state here to acommodate private buffers
+ * in the future.
+ */
+#define I810_DESTREG_DI0 0 /* CMD_OP_DESTBUFFER_INFO (2 dwords) */
+#define I810_DESTREG_DI1 1
+#define I810_DESTREG_DV0 2 /* GFX_OP_DESTBUFFER_VARS (2 dwords) */
+#define I810_DESTREG_DV1 3
+#define I810_DESTREG_DR0 4 /* GFX_OP_DRAWRECT_INFO (4 dwords) */
+#define I810_DESTREG_DR1 5
+#define I810_DESTREG_DR2 6
+#define I810_DESTREG_DR3 7
+#define I810_DESTREG_DR4 8
+#define I810_DEST_SETUP_SIZE 10
+
+/* Context state
+ */
+#define I810_CTXREG_CF0 0 /* GFX_OP_COLOR_FACTOR */
+#define I810_CTXREG_CF1 1
+#define I810_CTXREG_ST0 2 /* GFX_OP_STIPPLE */
+#define I810_CTXREG_ST1 3
+#define I810_CTXREG_VF 4 /* GFX_OP_VERTEX_FMT */
+#define I810_CTXREG_MT 5 /* GFX_OP_MAP_TEXELS */
+#define I810_CTXREG_MC0 6 /* GFX_OP_MAP_COLOR_STAGES - stage 0 */
+#define I810_CTXREG_MC1 7 /* GFX_OP_MAP_COLOR_STAGES - stage 1 */
+#define I810_CTXREG_MC2 8 /* GFX_OP_MAP_COLOR_STAGES - stage 2 */
+#define I810_CTXREG_MA0 9 /* GFX_OP_MAP_ALPHA_STAGES - stage 0 */
+#define I810_CTXREG_MA1 10 /* GFX_OP_MAP_ALPHA_STAGES - stage 1 */
+#define I810_CTXREG_MA2 11 /* GFX_OP_MAP_ALPHA_STAGES - stage 2 */
+#define I810_CTXREG_SDM 12 /* GFX_OP_SRC_DEST_MONO */
+#define I810_CTXREG_FOG 13 /* GFX_OP_FOG_COLOR */
+#define I810_CTXREG_B1 14 /* GFX_OP_BOOL_1 */
+#define I810_CTXREG_B2 15 /* GFX_OP_BOOL_2 */
+#define I810_CTXREG_LCS 16 /* GFX_OP_LINEWIDTH_CULL_SHADE_MODE */
+#define I810_CTXREG_PV 17 /* GFX_OP_PV_RULE -- Invarient! */
+#define I810_CTXREG_ZA 18 /* GFX_OP_ZBIAS_ALPHAFUNC */
+#define I810_CTXREG_AA 19 /* GFX_OP_ANTIALIAS */
+#define I810_CTX_SETUP_SIZE 20
+
+/* Texture state (per tex unit)
+ */
+#define I810_TEXREG_MI0 0 /* GFX_OP_MAP_INFO (4 dwords) */
+#define I810_TEXREG_MI1 1
+#define I810_TEXREG_MI2 2
+#define I810_TEXREG_MI3 3
+#define I810_TEXREG_MF 4 /* GFX_OP_MAP_FILTER */
+#define I810_TEXREG_MLC 5 /* GFX_OP_MAP_LOD_CTL */
+#define I810_TEXREG_MLL 6 /* GFX_OP_MAP_LOD_LIMITS */
+#define I810_TEXREG_MCS 7 /* GFX_OP_MAP_COORD_SETS ??? */
+#define I810_TEX_SETUP_SIZE 8
+
+#define I810_FRONT 0x1
+#define I810_BACK 0x2
+#define I810_DEPTH 0x4
+
+
+typedef struct _drm_i810_init {
+ enum {
+ I810_INIT_DMA = 0x01,
+ I810_CLEANUP_DMA = 0x02
+ } func;
+ int ring_map_idx;
+ int buffer_map_idx;
+ int sarea_priv_offset;
+ unsigned int ring_start;
+ unsigned int ring_end;
+ unsigned int ring_size;
+ unsigned int front_offset;
+ unsigned int back_offset;
+ unsigned int depth_offset;
+ unsigned int w;
+ unsigned int h;
+ unsigned int pitch;
+ unsigned int pitch_bits;
+} drm_i810_init_t;
+
+/* Warning: If you change the SAREA structure you must change the Xserver
+ * structure as well */
+
+typedef struct _drm_i810_tex_region {
+ unsigned char next, prev; /* indices to form a circular LRU */
+ unsigned char in_use; /* owned by a client, or free? */
+ int age; /* tracked by clients to update local LRU's */
+} drm_i810_tex_region_t;
+
+typedef struct _drm_i810_sarea {
+ unsigned int ContextState[I810_CTX_SETUP_SIZE];
+ unsigned int BufferState[I810_DEST_SETUP_SIZE];
+ unsigned int TexState[2][I810_TEX_SETUP_SIZE];
+ unsigned int dirty;
+
+ unsigned int nbox;
+ drm_clip_rect_t boxes[I810_NR_SAREA_CLIPRECTS];
+
+ /* Maintain an LRU of contiguous regions of texture space. If
+ * you think you own a region of texture memory, and it has an
+ * age different to the one you set, then you are mistaken and
+ * it has been stolen by another client. If global texAge
+ * hasn't changed, there is no need to walk the list.
+ *
+ * These regions can be used as a proxy for the fine-grained
+ * texture information of other clients - by maintaining them
+ * in the same lru which is used to age their own textures,
+ * clients have an approximate lru for the whole of global
+ * texture space, and can make informed decisions as to which
+ * areas to kick out. There is no need to choose whether to
+ * kick out your own texture or someone else's - simply eject
+ * them all in LRU order.
+ */
+
+ drm_i810_tex_region_t texList[I810_NR_TEX_REGIONS+1];
+ /* Last elt is sentinal */
+ int texAge; /* last time texture was uploaded */
+ int last_enqueue; /* last time a buffer was enqueued */
+ int last_dispatch; /* age of the most recently dispatched buffer */
+ int last_quiescent; /* */
+ int ctxOwner; /* last context to upload state */
+
+ int vertex_prim;
+
+} drm_i810_sarea_t;
+
+typedef struct _drm_i810_clear {
+ int clear_color;
+ int clear_depth;
+ int flags;
+} drm_i810_clear_t;
+
+
+
+/* These may be placeholders if we have more cliprects than
+ * I810_NR_SAREA_CLIPRECTS. In that case, the client sets discard to
+ * false, indicating that the buffer will be dispatched again with a
+ * new set of cliprects.
+ */
+typedef struct _drm_i810_vertex {
+ int idx; /* buffer index */
+ int used; /* nr bytes in use */
+ int discard; /* client is finished with the buffer? */
+} drm_i810_vertex_t;
+
+typedef struct _drm_i810_copy_t {
+ int idx; /* buffer index */
+ int used; /* nr bytes in use */
+ void *address; /* Address to copy from */
+} drm_i810_copy_t;
+
+typedef struct drm_i810_dma {
+ void *virtual;
+ int request_idx;
+ int request_size;
+ int granted;
+} drm_i810_dma_t;
+
+#endif /* _I810_DRM_H_ */
diff --git a/drivers/char/drm/i810_drv.c b/drivers/char/drm/i810_drv.c
new file mode 100644
index 000000000..d8c49a57d
--- /dev/null
+++ b/drivers/char/drm/i810_drv.c
@@ -0,0 +1,643 @@
+/* i810_drv.c -- I810 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
+#include "drmP.h"
+#include "i810_drv.h"
+
+
+EXPORT_SYMBOL(i810_init);
+EXPORT_SYMBOL(i810_cleanup);
+
+#define I810_NAME "i810"
+#define I810_DESC "Intel I810"
+#define I810_DATE "20000719"
+#define I810_MAJOR 1
+#define I810_MINOR 1
+#define I810_PATCHLEVEL 0
+
+static drm_device_t i810_device;
+drm_ctx_t i810_res_ctx;
+
+static struct file_operations i810_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+ /* This started being used approx. 2.3.34 */
+ owner: THIS_MODULE,
+#endif
+ open: i810_open,
+ flush: drm_flush,
+ release: i810_release,
+ ioctl: i810_ioctl,
+ mmap: drm_mmap,
+ read: drm_read,
+ fasync: drm_fasync,
+ poll: drm_poll,
+};
+
+static struct miscdevice i810_misc = {
+ minor: MISC_DYNAMIC_MINOR,
+ name: I810_NAME,
+ fops: &i810_fops,
+};
+
+static drm_ioctl_desc_t i810_ioctls[] = {
+ [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { i810_version, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { i810_control, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { i810_addbufs, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { i810_markbufs, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { i810_infobufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { i810_freebufs, 1, 0 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { i810_addctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { i810_rmctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { i810_modctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { i810_getctx, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { i810_switchctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { i810_newctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { i810_resctx, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { i810_lock, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { i810_unlock, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)] = { i810_clear_bufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)] = { i810_flush_ioctl,1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)] = { i810_swap_bufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)] = { i810_copybuf, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy, 1, 0 },
+};
+
+#define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls)
+
+#ifdef MODULE
+static char *i810 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Intel I810");
+MODULE_PARM(i810, "s");
+
+module_init(i810_init);
+module_exit(i810_cleanup);
+
+#ifndef MODULE
+/* i810_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO). It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init i810_options(char *str)
+{
+ drm_parse_options(str);
+ return 1;
+}
+
+__setup("i810=", i810_options);
+#endif
+
+static int i810_setup(drm_device_t *dev)
+{
+ int i;
+
+ atomic_set(&dev->ioctl_count, 0);
+ atomic_set(&dev->vma_count, 0);
+ dev->buf_use = 0;
+ atomic_set(&dev->buf_alloc, 0);
+
+ drm_dma_setup(dev);
+
+ atomic_set(&dev->total_open, 0);
+ atomic_set(&dev->total_close, 0);
+ atomic_set(&dev->total_ioctl, 0);
+ atomic_set(&dev->total_irq, 0);
+ atomic_set(&dev->total_ctx, 0);
+ atomic_set(&dev->total_locks, 0);
+ atomic_set(&dev->total_unlocks, 0);
+ atomic_set(&dev->total_contends, 0);
+ atomic_set(&dev->total_sleeps, 0);
+
+ for (i = 0; i < DRM_HASH_SIZE; i++) {
+ dev->magiclist[i].head = NULL;
+ dev->magiclist[i].tail = NULL;
+ }
+ dev->maplist = NULL;
+ dev->map_count = 0;
+ dev->vmalist = NULL;
+ dev->lock.hw_lock = NULL;
+ init_waitqueue_head(&dev->lock.lock_queue);
+ dev->queue_count = 0;
+ dev->queue_reserved = 0;
+ dev->queue_slots = 0;
+ dev->queuelist = NULL;
+ dev->irq = 0;
+ dev->context_flag = 0;
+ dev->interrupt_flag = 0;
+ dev->dma_flag = 0;
+ dev->last_context = 0;
+ dev->last_switch = 0;
+ dev->last_checked = 0;
+ init_timer(&dev->timer);
+ init_waitqueue_head(&dev->context_wait);
+#if DRM_DMA_HISTO
+ memset(&dev->histo, 0, sizeof(dev->histo));
+#endif
+ dev->ctx_start = 0;
+ dev->lck_start = 0;
+
+ dev->buf_rp = dev->buf;
+ dev->buf_wp = dev->buf;
+ dev->buf_end = dev->buf + DRM_BSZ;
+ dev->buf_async = NULL;
+ init_waitqueue_head(&dev->buf_readers);
+ init_waitqueue_head(&dev->buf_writers);
+
+ DRM_DEBUG("\n");
+
+ /* The kernel's context could be created here, but is now created
+ in drm_dma_enqueue. This is more resource-efficient for
+ hardware that does not do DMA, but may mean that
+ drm_select_queue fails between the time the interrupt is
+ initialized and the time the queues are initialized. */
+
+ return 0;
+}
+
+
+static int i810_takedown(drm_device_t *dev)
+{
+ int i;
+ drm_magic_entry_t *pt, *next;
+ drm_map_t *map;
+ drm_vma_entry_t *vma, *vma_next;
+
+ DRM_DEBUG("\n");
+
+ if (dev->irq) i810_irq_uninstall(dev);
+
+ down(&dev->struct_sem);
+ del_timer(&dev->timer);
+
+ if (dev->devname) {
+ drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+ dev->devname = NULL;
+ }
+
+ if (dev->unique) {
+ drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+ dev->unique = NULL;
+ dev->unique_len = 0;
+ }
+ /* Clear pid list */
+ for (i = 0; i < DRM_HASH_SIZE; i++) {
+ for (pt = dev->magiclist[i].head; pt; pt = next) {
+ next = pt->next;
+ drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+ }
+ dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+ }
+ /* Clear AGP information */
+ if (dev->agp) {
+ drm_agp_mem_t *entry;
+ drm_agp_mem_t *nexte;
+
+ /* Remove AGP resources, but leave dev->agp
+ intact until r128_cleanup is called. */
+ for (entry = dev->agp->memory; entry; entry = nexte) {
+ nexte = entry->next;
+ if (entry->bound) drm_unbind_agp(entry->memory);
+ drm_free_agp(entry->memory, entry->pages);
+ drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+ }
+ dev->agp->memory = NULL;
+
+ if (dev->agp->acquired && drm_agp.release)
+ (*drm_agp.release)();
+
+ dev->agp->acquired = 0;
+ dev->agp->enabled = 0;
+ }
+ /* Clear vma list (only built for debugging) */
+ if (dev->vmalist) {
+ for (vma = dev->vmalist; vma; vma = vma_next) {
+ vma_next = vma->next;
+ drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+ }
+ dev->vmalist = NULL;
+ }
+
+ /* Clear map area and mtrr information */
+ if (dev->maplist) {
+ for (i = 0; i < dev->map_count; i++) {
+ map = dev->maplist[i];
+ switch (map->type) {
+ case _DRM_REGISTERS:
+ case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+ if (map->mtrr >= 0) {
+ int retcode;
+ retcode = mtrr_del(map->mtrr,
+ map->offset,
+ map->size);
+ DRM_DEBUG("mtrr_del = %d\n", retcode);
+ }
+#endif
+ drm_ioremapfree(map->handle, map->size);
+ break;
+ case _DRM_SHM:
+ drm_free_pages((unsigned long)map->handle,
+ drm_order(map->size)
+ - PAGE_SHIFT,
+ DRM_MEM_SAREA);
+ break;
+ case _DRM_AGP:
+ break;
+ }
+ drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+ }
+ drm_free(dev->maplist,
+ dev->map_count * sizeof(*dev->maplist),
+ DRM_MEM_MAPS);
+ dev->maplist = NULL;
+ dev->map_count = 0;
+ }
+
+ if (dev->queuelist) {
+ for (i = 0; i < dev->queue_count; i++) {
+ drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+ if (dev->queuelist[i]) {
+ drm_free(dev->queuelist[i],
+ sizeof(*dev->queuelist[0]),
+ DRM_MEM_QUEUES);
+ dev->queuelist[i] = NULL;
+ }
+ }
+ drm_free(dev->queuelist,
+ dev->queue_slots * sizeof(*dev->queuelist),
+ DRM_MEM_QUEUES);
+ dev->queuelist = NULL;
+ }
+
+ drm_dma_takedown(dev);
+
+ dev->queue_count = 0;
+ if (dev->lock.hw_lock) {
+ dev->lock.hw_lock = NULL; /* SHM removed */
+ dev->lock.pid = 0;
+ wake_up_interruptible(&dev->lock.lock_queue);
+ }
+ up(&dev->struct_sem);
+
+ return 0;
+}
+
+/* i810_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+int i810_init(void)
+{
+ int retcode;
+ drm_device_t *dev = &i810_device;
+
+ DRM_DEBUG("\n");
+
+ memset((void *)dev, 0, sizeof(*dev));
+ dev->count_lock = SPIN_LOCK_UNLOCKED;
+ sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+ drm_parse_options(i810);
+#endif
+ DRM_DEBUG("doing misc_register\n");
+ if ((retcode = misc_register(&i810_misc))) {
+ DRM_ERROR("Cannot register \"%s\"\n", I810_NAME);
+ return retcode;
+ }
+ dev->device = MKDEV(MISC_MAJOR, i810_misc.minor);
+ dev->name = I810_NAME;
+
+ DRM_DEBUG("doing mem init\n");
+ drm_mem_init();
+ DRM_DEBUG("doing proc init\n");
+ drm_proc_init(dev);
+ DRM_DEBUG("doing agp init\n");
+ dev->agp = drm_agp_init();
+ if(dev->agp == NULL) {
+ DRM_INFO("The i810 drm module requires the agpgart module"
+ " to function correctly\nPlease load the agpgart"
+ " module before you load the i810 module\n");
+ drm_proc_cleanup();
+ misc_deregister(&i810_misc);
+ i810_takedown(dev);
+ return -ENOMEM;
+ }
+ DRM_DEBUG("doing ctxbitmap init\n");
+ if((retcode = drm_ctxbitmap_init(dev))) {
+ DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+ drm_proc_cleanup();
+ misc_deregister(&i810_misc);
+ i810_takedown(dev);
+ return retcode;
+ }
+
+ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+ I810_NAME,
+ I810_MAJOR,
+ I810_MINOR,
+ I810_PATCHLEVEL,
+ I810_DATE,
+ i810_misc.minor);
+
+ return 0;
+}
+
+/* i810_cleanup is called via cleanup_module at module unload time. */
+
+void i810_cleanup(void)
+{
+ drm_device_t *dev = &i810_device;
+
+ DRM_DEBUG("\n");
+
+ drm_proc_cleanup();
+ if (misc_deregister(&i810_misc)) {
+ DRM_ERROR("Cannot unload module\n");
+ } else {
+ DRM_INFO("Module unloaded\n");
+ }
+ drm_ctxbitmap_cleanup(dev);
+ i810_takedown(dev);
+ if (dev->agp) {
+ drm_agp_uninit();
+ drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+ dev->agp = NULL;
+ }
+}
+
+int i810_version(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_version_t version;
+ int len;
+
+ copy_from_user_ret(&version,
+ (drm_version_t *)arg,
+ sizeof(version),
+ -EFAULT);
+
+#define DRM_COPY(name,value) \
+ len = strlen(value); \
+ if (len > name##_len) len = name##_len; \
+ name##_len = strlen(value); \
+ if (len && name) { \
+ copy_to_user_ret(name, value, len, -EFAULT); \
+ }
+
+ version.version_major = I810_MAJOR;
+ version.version_minor = I810_MINOR;
+ version.version_patchlevel = I810_PATCHLEVEL;
+
+ DRM_COPY(version.name, I810_NAME);
+ DRM_COPY(version.date, I810_DATE);
+ DRM_COPY(version.desc, I810_DESC);
+
+ copy_to_user_ret((drm_version_t *)arg,
+ &version,
+ sizeof(version),
+ -EFAULT);
+ return 0;
+}
+
+int i810_open(struct inode *inode, struct file *filp)
+{
+ drm_device_t *dev = &i810_device;
+ int retcode = 0;
+
+ DRM_DEBUG("open_count = %d\n", dev->open_count);
+ if (!(retcode = drm_open_helper(inode, filp, dev))) {
+ MOD_INC_USE_COUNT;
+ atomic_inc(&dev->total_open);
+ spin_lock(&dev->count_lock);
+ if (!dev->open_count++) {
+ spin_unlock(&dev->count_lock);
+ return i810_setup(dev);
+ }
+ spin_unlock(&dev->count_lock);
+ }
+ return retcode;
+}
+
+int i810_release(struct inode *inode, struct file *filp)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ int retcode = 0;
+
+ DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+ current->pid, dev->device, dev->open_count);
+
+ if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+ && dev->lock.pid == current->pid) {
+ i810_reclaim_buffers(dev, priv->pid);
+ DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+ current->pid,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ drm_lock_free(dev,
+ &dev->lock.hw_lock->lock,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+
+ /* FIXME: may require heavy-handed reset of
+ hardware at this point, possibly
+ processed via a callback to the X
+ server. */
+ } else if (dev->lock.hw_lock) {
+ /* The lock is required to reclaim buffers */
+ DECLARE_WAITQUEUE(entry, current);
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ retcode = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ dev->lock.pid = priv->pid;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->total_locks);
+ break; /* Got lock */
+ }
+ /* Contention */
+ atomic_inc(&dev->total_sleeps);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (signal_pending(current)) {
+ retcode = -ERESTARTSYS;
+ break;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ if(!retcode) {
+ i810_reclaim_buffers(dev, priv->pid);
+ drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT);
+ }
+ }
+ drm_fasync(-1, filp, 0);
+
+ down(&dev->struct_sem);
+ if (priv->prev) priv->prev->next = priv->next;
+ else dev->file_first = priv->next;
+ if (priv->next) priv->next->prev = priv->prev;
+ else dev->file_last = priv->prev;
+ up(&dev->struct_sem);
+
+ drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+ MOD_DEC_USE_COUNT;
+ atomic_inc(&dev->total_close);
+ spin_lock(&dev->count_lock);
+ if (!--dev->open_count) {
+ if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+ DRM_ERROR("Device busy: %d %d\n",
+ atomic_read(&dev->ioctl_count),
+ dev->blocked);
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&dev->count_lock);
+ return i810_takedown(dev);
+ }
+ spin_unlock(&dev->count_lock);
+ return retcode;
+}
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int i810_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int nr = DRM_IOCTL_NR(cmd);
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ int retcode = 0;
+ drm_ioctl_desc_t *ioctl;
+ drm_ioctl_t *func;
+
+ atomic_inc(&dev->ioctl_count);
+ atomic_inc(&dev->total_ioctl);
+ ++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);
+
+ if (nr >= I810_IOCTL_COUNT) {
+ retcode = -EINVAL;
+ } else {
+ ioctl = &i810_ioctls[nr];
+ func = ioctl->func;
+
+ if (!func) {
+ DRM_DEBUG("no function\n");
+ retcode = -EINVAL;
+ } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+ || (ioctl->auth_needed && !priv->authenticated)) {
+ retcode = -EACCES;
+ } else {
+ retcode = (func)(inode, filp, cmd, arg);
+ }
+ }
+
+ atomic_dec(&dev->ioctl_count);
+ return retcode;
+}
+
+int i810_unlock(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_lock_t lock;
+
+ copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+ if (lock.context == DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("Process %d using kernel context %d\n",
+ current->pid, lock.context);
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("%d frees lock (%d holds)\n",
+ lock.context,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ atomic_inc(&dev->total_unlocks);
+ if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+ atomic_inc(&dev->total_contends);
+ drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+ if (!dev->context_flag) {
+ if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ DRM_ERROR("\n");
+ }
+ }
+#if DRM_DMA_HISTOGRAM
+ atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
+ - dev->lck_start)]);
+#endif
+
+ return 0;
+}
diff --git a/drivers/char/drm/i810_drv.h b/drivers/char/drm/i810_drv.h
new file mode 100644
index 000000000..f5411c0bc
--- /dev/null
+++ b/drivers/char/drm/i810_drv.h
@@ -0,0 +1,227 @@
+/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _I810_DRV_H_
+#define _I810_DRV_H_
+
+typedef struct drm_i810_buf_priv {
+ u32 *in_use;
+ int my_use_idx;
+ int currently_mapped;
+ void *virtual;
+ void *kernel_virtual;
+ int map_count;
+ struct vm_area_struct *vma;
+} drm_i810_buf_priv_t;
+
+typedef struct _drm_i810_ring_buffer{
+ int tail_mask;
+ unsigned long Start;
+ unsigned long End;
+ unsigned long Size;
+ u8 *virtual_start;
+ int head;
+ int tail;
+ int space;
+} drm_i810_ring_buffer_t;
+
+typedef struct drm_i810_private {
+ int ring_map_idx;
+ int buffer_map_idx;
+
+ drm_i810_ring_buffer_t ring;
+ drm_i810_sarea_t *sarea_priv;
+
+ unsigned long hw_status_page;
+ unsigned long counter;
+
+ atomic_t flush_done;
+ wait_queue_head_t flush_queue; /* Processes waiting until flush */
+ drm_buf_t *mmap_buffer;
+
+
+ u32 front_di1, back_di1, zi1;
+
+ int back_offset;
+ int depth_offset;
+ int w, h;
+ int pitch;
+} drm_i810_private_t;
+
+ /* i810_drv.c */
+extern int i810_init(void);
+extern void i810_cleanup(void);
+extern int i810_version(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_open(struct inode *inode, struct file *filp);
+extern int i810_release(struct inode *inode, struct file *filp);
+extern int i810_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_unlock(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+ /* i810_dma.c */
+extern int i810_dma_schedule(drm_device_t *dev, int locked);
+extern int i810_getbuf(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_irq_install(drm_device_t *dev, int irq);
+extern int i810_irq_uninstall(drm_device_t *dev);
+extern int i810_control(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_lock(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_dma_init(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_flush_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern void i810_reclaim_buffers(drm_device_t *dev, pid_t pid);
+extern int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg);
+extern int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma);
+extern int i810_copybuf(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_docopy(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+ /* i810_bufs.c */
+extern int i810_addbufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_infobufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_markbufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_freebufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_addmap(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+ /* i810_context.c */
+extern int i810_resctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_addctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_modctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_getctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_switchctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_newctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int i810_rmctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+extern int i810_context_switch(drm_device_t *dev, int old, int new);
+extern int i810_context_switch_complete(drm_device_t *dev, int new);
+
+#define I810_VERBOSE 0
+
+
+int i810_dma_vertex(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+int i810_swap_bufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+int i810_clear_bufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+#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 INST_PARSER_CLIENT 0x00000000
+#define INST_OP_FLUSH 0x02000000
+#define INST_FLUSH_MAP_CACHE 0x00000001
+
+
+#define BB1_START_ADDR_MASK (~0x7)
+#define BB1_PROTECTED (1<<0)
+#define BB1_UNPROTECTED (0<<0)
+#define BB2_END_ADDR_MASK (~0x7)
+
+#define I810REG_HWSTAM 0x02098
+#define I810REG_INT_IDENTITY_R 0x020a4
+#define I810REG_INT_MASK_R 0x020a8
+#define I810REG_INT_ENABLE_R 0x020a0
+
+#define LP_RING 0x2030
+#define HP_RING 0x2040
+#define RING_TAIL 0x00
+#define TAIL_ADDR 0x000FFFF8
+#define RING_HEAD 0x04
+#define HEAD_WRAP_COUNT 0xFFE00000
+#define HEAD_WRAP_ONE 0x00200000
+#define HEAD_ADDR 0x001FFFFC
+#define RING_START 0x08
+#define START_ADDR 0x00FFFFF8
+#define RING_LEN 0x0C
+#define RING_NR_PAGES 0x000FF000
+#define RING_REPORT_MASK 0x00000006
+#define RING_REPORT_64K 0x00000002
+#define RING_REPORT_128K 0x00000004
+#define RING_NO_REPORT 0x00000000
+#define RING_VALID_MASK 0x00000001
+#define RING_VALID 0x00000001
+#define RING_INVALID 0x00000000
+
+#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19))
+#define SC_UPDATE_SCISSOR (0x1<<1)
+#define SC_ENABLE_MASK (0x1<<0)
+#define SC_ENABLE (0x1<<0)
+
+#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1))
+#define SCI_YMIN_MASK (0xffff<<16)
+#define SCI_XMIN_MASK (0xffff<<0)
+#define SCI_YMAX_MASK (0xffff<<16)
+#define SCI_XMAX_MASK (0xffff<<0)
+
+#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0)
+#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16))
+#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x2)
+#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0)
+#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
+#define GFX_OP_PRIMITIVE ((0x3<<29)|(0x1f<<24))
+
+#define CMD_OP_Z_BUFFER_INFO ((0x0<<29)|(0x16<<23))
+#define CMD_OP_DESTBUFFER_INFO ((0x0<<29)|(0x15<<23))
+
+#define BR00_BITBLT_CLIENT 0x40000000
+#define BR00_OP_COLOR_BLT 0x10000000
+#define BR00_OP_SRC_COPY_BLT 0x10C00000
+#define BR13_SOLID_PATTERN 0x80000000
+
+
+
+#endif
+
diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c
index ad887490b..8de3dac9b 100644
--- a/drivers/char/drm/init.c
+++ b/drivers/char/drm/init.c
@@ -2,6 +2,7 @@
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
diff --git a/drivers/char/drm/ioctl.c b/drivers/char/drm/ioctl.c
index 13bb60659..b246f76e5 100644
--- a/drivers/char/drm/ioctl.c
+++ b/drivers/char/drm/ioctl.c
@@ -2,6 +2,7 @@
* Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
diff --git a/drivers/char/drm/lists.c b/drivers/char/drm/lists.c
index 212ed18ed..f62495aa2 100644
--- a/drivers/char/drm/lists.c
+++ b/drivers/char/drm/lists.c
@@ -2,6 +2,7 @@
* Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
diff --git a/drivers/char/drm/lock.c b/drivers/char/drm/lock.c
index 2523eb21a..550827278 100644
--- a/drivers/char/drm/lock.c
+++ b/drivers/char/drm/lock.c
@@ -2,6 +2,7 @@
* Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
diff --git a/drivers/char/drm/memory.c b/drivers/char/drm/memory.c
index d9ff66e34..f342bb4fe 100644
--- a/drivers/char/drm/memory.c
+++ b/drivers/char/drm/memory.c
@@ -2,6 +2,7 @@
* Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
@@ -44,21 +45,25 @@ static spinlock_t drm_mem_lock = SPIN_LOCK_UNLOCKED;
static unsigned long drm_ram_available = 0; /* In pages */
static unsigned long drm_ram_used = 0;
static drm_mem_stats_t drm_mem_stats[] = {
- [DRM_MEM_DMA] = { "dmabufs" },
- [DRM_MEM_SAREA] = { "sareas" },
- [DRM_MEM_DRIVER] = { "driver" },
- [DRM_MEM_MAGIC] = { "magic" },
- [DRM_MEM_IOCTLS] = { "ioctltab" },
- [DRM_MEM_MAPS] = { "maplist" },
- [DRM_MEM_VMAS] = { "vmalist" },
- [DRM_MEM_BUFS] = { "buflist" },
- [DRM_MEM_SEGS] = { "seglist" },
- [DRM_MEM_PAGES] = { "pagelist" },
- [DRM_MEM_FILES] = { "files" },
- [DRM_MEM_QUEUES] = { "queues" },
- [DRM_MEM_CMDS] = { "commands" },
- [DRM_MEM_MAPPINGS] = { "mappings" },
- [DRM_MEM_BUFLISTS] = { "buflists" },
+ [DRM_MEM_DMA] = { "dmabufs" },
+ [DRM_MEM_SAREA] = { "sareas" },
+ [DRM_MEM_DRIVER] = { "driver" },
+ [DRM_MEM_MAGIC] = { "magic" },
+ [DRM_MEM_IOCTLS] = { "ioctltab" },
+ [DRM_MEM_MAPS] = { "maplist" },
+ [DRM_MEM_VMAS] = { "vmalist" },
+ [DRM_MEM_BUFS] = { "buflist" },
+ [DRM_MEM_SEGS] = { "seglist" },
+ [DRM_MEM_PAGES] = { "pagelist" },
+ [DRM_MEM_FILES] = { "files" },
+ [DRM_MEM_QUEUES] = { "queues" },
+ [DRM_MEM_CMDS] = { "commands" },
+ [DRM_MEM_MAPPINGS] = { "mappings" },
+ [DRM_MEM_BUFLISTS] = { "buflists" },
+ [DRM_MEM_AGPLISTS] = { "agplist" },
+ [DRM_MEM_TOTALAGP] = { "totalagp" },
+ [DRM_MEM_BOUNDAGP] = { "boundagp" },
+ [DRM_MEM_CTXBITMAP] = { "ctxbitmap"},
{ NULL, 0, } /* Last entry must be null */
};
@@ -324,3 +329,120 @@ void drm_ioremapfree(void *pt, unsigned long size)
free_count, alloc_count);
}
}
+
+#ifdef DRM_AGP
+agp_memory *drm_alloc_agp(int pages, u32 type)
+{
+ agp_memory *handle;
+
+ if (!pages) {
+ DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n");
+ return NULL;
+ }
+
+ if (drm_agp.allocate_memory) {
+ if ((handle = (*drm_agp.allocate_memory)(pages,
+ type))) {
+ spin_lock(&drm_mem_lock);
+ ++drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+ drm_mem_stats[DRM_MEM_TOTALAGP].bytes_allocated
+ += pages << PAGE_SHIFT;
+ spin_unlock(&drm_mem_lock);
+ return handle;
+ }
+ }
+ spin_lock(&drm_mem_lock);
+ ++drm_mem_stats[DRM_MEM_TOTALAGP].fail_count;
+ spin_unlock(&drm_mem_lock);
+ return NULL;
+}
+
+int drm_free_agp(agp_memory *handle, int pages)
+{
+ int alloc_count;
+ int free_count;
+ int retval = -EINVAL;
+
+ if (!handle) {
+ DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+ "Attempt to free NULL AGP handle\n");
+ return retval;;
+ }
+
+ if (drm_agp.free_memory) {
+ (*drm_agp.free_memory)(handle);
+ spin_lock(&drm_mem_lock);
+ free_count = ++drm_mem_stats[DRM_MEM_TOTALAGP].free_count;
+ alloc_count = drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+ drm_mem_stats[DRM_MEM_TOTALAGP].bytes_freed
+ += pages << PAGE_SHIFT;
+ spin_unlock(&drm_mem_lock);
+ if (free_count > alloc_count) {
+ DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+ "Excess frees: %d frees, %d allocs\n",
+ free_count, alloc_count);
+ }
+ return 0;
+ }
+ return retval;
+}
+
+int drm_bind_agp(agp_memory *handle, unsigned int start)
+{
+ int retcode = -EINVAL;
+
+ DRM_DEBUG("drm_bind_agp called\n");
+ if (!handle) {
+ DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+ "Attempt to bind NULL AGP handle\n");
+ return retcode;
+ }
+
+ DRM_DEBUG("drm_agp.bind_memory : %p\n", drm_agp.bind_memory);
+ if (drm_agp.bind_memory) {
+ if (!(retcode = (*drm_agp.bind_memory)(handle, start))) {
+ spin_lock(&drm_mem_lock);
+ ++drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+ drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_allocated
+ += handle->page_count << PAGE_SHIFT;
+ spin_unlock(&drm_mem_lock);
+ DRM_DEBUG("drm_agp.bind_memory: retcode %d\n", retcode);
+ return retcode;
+ }
+ }
+ spin_lock(&drm_mem_lock);
+ ++drm_mem_stats[DRM_MEM_BOUNDAGP].fail_count;
+ spin_unlock(&drm_mem_lock);
+ return retcode;
+}
+
+int drm_unbind_agp(agp_memory *handle)
+{
+ int alloc_count;
+ int free_count;
+ int retcode = -EINVAL;
+
+ if (!handle) {
+ DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+ "Attempt to unbind NULL AGP handle\n");
+ return retcode;
+ }
+
+ if (drm_agp.unbind_memory) {
+ int c = handle->page_count;
+ if ((retcode = (*drm_agp.unbind_memory)(handle)))
+ return retcode;
+ spin_lock(&drm_mem_lock);
+ free_count = ++drm_mem_stats[DRM_MEM_BOUNDAGP].free_count;
+ alloc_count = drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+ drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_freed += c << PAGE_SHIFT;
+ spin_unlock(&drm_mem_lock);
+ if (free_count > alloc_count) {
+ DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+ "Excess frees: %d frees, %d allocs\n",
+ free_count, alloc_count);
+ }
+ }
+ return retcode;
+}
+#endif
diff --git a/drivers/char/drm/mga_bufs.c b/drivers/char/drm/mga_bufs.c
new file mode 100644
index 000000000..b97eb4959
--- /dev/null
+++ b/drivers/char/drm/mga_bufs.c
@@ -0,0 +1,639 @@
+/* mga_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "linux/un.h"
+
+
+int mga_addbufs_agp(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_device_dma_t *dma = dev->dma;
+ drm_buf_desc_t request;
+ drm_buf_entry_t *entry;
+ drm_buf_t *buf;
+ unsigned long offset;
+ unsigned long agp_offset;
+ int count;
+ int order;
+ int size;
+ int alignment;
+ int page_order;
+ int total;
+ int byte_count;
+ int i;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ count = request.count;
+ order = drm_order(request.size);
+ size = 1 << order;
+ agp_offset = request.agp_start;
+ alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+ page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+ total = PAGE_SIZE << page_order;
+ byte_count = 0;
+
+ DRM_DEBUG("count: %d\n", count);
+ DRM_DEBUG("order: %d\n", order);
+ DRM_DEBUG("size: %d\n", size);
+ DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+ DRM_DEBUG("alignment: %d\n", alignment);
+ DRM_DEBUG("page_order: %d\n", page_order);
+ DRM_DEBUG("total: %d\n", total);
+ DRM_DEBUG("byte_count: %d\n", byte_count);
+
+ if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+ if (dev->queue_count) return -EBUSY; /* Not while in use */
+ spin_lock(&dev->count_lock);
+ if (dev->buf_use) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ atomic_inc(&dev->buf_alloc);
+ spin_unlock(&dev->count_lock);
+
+ down(&dev->struct_sem);
+ entry = &dma->bufs[order];
+ if (entry->buf_count) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM; /* May only call once for each order */
+ }
+
+ entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+ DRM_MEM_BUFS);
+ if (!entry->buflist) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+ memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+ entry->buf_size = size;
+ entry->page_order = page_order;
+ offset = 0;
+
+
+ while(entry->buf_count < count) {
+ buf = &entry->buflist[entry->buf_count];
+ buf->idx = dma->buf_count + entry->buf_count;
+ buf->total = alignment;
+ buf->order = order;
+ buf->used = 0;
+
+ DRM_DEBUG("offset : %ld\n", offset);
+
+ buf->offset = offset; /* Hrm */
+ buf->bus_address = dev->agp->base + agp_offset + offset;
+ buf->address = (void *)(agp_offset + offset + dev->agp->base);
+ buf->next = NULL;
+ buf->waiting = 0;
+ buf->pending = 0;
+ init_waitqueue_head(&buf->dma_wait);
+ buf->pid = 0;
+
+ buf->dev_private = drm_alloc(sizeof(drm_mga_buf_priv_t), DRM_MEM_BUFS);
+ buf->dev_priv_size = sizeof(drm_mga_buf_priv_t);
+
+#if DRM_DMA_HISTOGRAM
+ buf->time_queued = 0;
+ buf->time_dispatched = 0;
+ buf->time_completed = 0;
+ buf->time_freed = 0;
+#endif
+ offset = offset + alignment;
+ entry->buf_count++;
+ byte_count += PAGE_SIZE << page_order;
+
+ DRM_DEBUG("buffer %d @ %p\n",
+ entry->buf_count, buf->address);
+ }
+
+ dma->buflist = drm_realloc(dma->buflist,
+ dma->buf_count * sizeof(*dma->buflist),
+ (dma->buf_count + entry->buf_count)
+ * sizeof(*dma->buflist),
+ DRM_MEM_BUFS);
+ for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+ dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+ dma->buf_count += entry->buf_count;
+
+ DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
+
+ dma->byte_count += byte_count;
+
+ DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
+
+ drm_freelist_create(&entry->freelist, entry->buf_count);
+ for (i = 0; i < entry->buf_count; i++) {
+ drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+ }
+
+ up(&dev->struct_sem);
+
+ request.count = entry->buf_count;
+ request.size = size;
+
+ copy_to_user_ret((drm_buf_desc_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ atomic_dec(&dev->buf_alloc);
+
+ DRM_DEBUG("count: %d\n", count);
+ DRM_DEBUG("order: %d\n", order);
+ DRM_DEBUG("size: %d\n", size);
+ DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+ DRM_DEBUG("alignment: %d\n", alignment);
+ DRM_DEBUG("page_order: %d\n", page_order);
+ DRM_DEBUG("total: %d\n", total);
+ DRM_DEBUG("byte_count: %d\n", byte_count);
+
+ dma->flags = _DRM_DMA_USE_AGP;
+
+ DRM_DEBUG("dma->flags : %x\n", dma->flags);
+
+ return 0;
+}
+
+int mga_addbufs_pci(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_device_dma_t *dma = dev->dma;
+ drm_buf_desc_t request;
+ int count;
+ int order;
+ int size;
+ int total;
+ int page_order;
+ drm_buf_entry_t *entry;
+ unsigned long page;
+ drm_buf_t *buf;
+ int alignment;
+ unsigned long offset;
+ int i;
+ int byte_count;
+ int page_count;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ count = request.count;
+ order = drm_order(request.size);
+ size = 1 << order;
+
+ DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
+ request.count, request.size, size, order, dev->queue_count);
+
+ if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+ if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+ alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+ page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+ total = PAGE_SIZE << page_order;
+
+ spin_lock(&dev->count_lock);
+ if (dev->buf_use) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ atomic_inc(&dev->buf_alloc);
+ spin_unlock(&dev->count_lock);
+
+ down(&dev->struct_sem);
+ entry = &dma->bufs[order];
+ if (entry->buf_count) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM; /* May only call once for each order */
+ }
+
+ entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+ DRM_MEM_BUFS);
+ if (!entry->buflist) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+ memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+ entry->seglist = drm_alloc(count * sizeof(*entry->seglist),
+ DRM_MEM_SEGS);
+ if (!entry->seglist) {
+ drm_free(entry->buflist,
+ count * sizeof(*entry->buflist),
+ DRM_MEM_BUFS);
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+ memset(entry->seglist, 0, count * sizeof(*entry->seglist));
+
+ dma->pagelist = drm_realloc(dma->pagelist,
+ dma->page_count * sizeof(*dma->pagelist),
+ (dma->page_count + (count << page_order))
+ * sizeof(*dma->pagelist),
+ DRM_MEM_PAGES);
+ DRM_DEBUG("pagelist: %d entries\n",
+ dma->page_count + (count << page_order));
+
+
+ entry->buf_size = size;
+ entry->page_order = page_order;
+ byte_count = 0;
+ page_count = 0;
+ while (entry->buf_count < count) {
+ if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break;
+ entry->seglist[entry->seg_count++] = page;
+ for (i = 0; i < (1 << page_order); i++) {
+ DRM_DEBUG("page %d @ 0x%08lx\n",
+ dma->page_count + page_count,
+ page + PAGE_SIZE * i);
+ dma->pagelist[dma->page_count + page_count++]
+ = page + PAGE_SIZE * i;
+ }
+ for (offset = 0;
+ offset + size <= total && entry->buf_count < count;
+ offset += alignment, ++entry->buf_count) {
+ buf = &entry->buflist[entry->buf_count];
+ buf->idx = dma->buf_count + entry->buf_count;
+ buf->total = alignment;
+ buf->order = order;
+ buf->used = 0;
+ buf->offset = (dma->byte_count + byte_count + offset);
+ buf->address = (void *)(page + offset);
+ buf->next = NULL;
+ buf->waiting = 0;
+ buf->pending = 0;
+ init_waitqueue_head(&buf->dma_wait);
+ buf->pid = 0;
+#if DRM_DMA_HISTOGRAM
+ buf->time_queued = 0;
+ buf->time_dispatched = 0;
+ buf->time_completed = 0;
+ buf->time_freed = 0;
+#endif
+ DRM_DEBUG("buffer %d @ %p\n",
+ entry->buf_count, buf->address);
+ }
+ byte_count += PAGE_SIZE << page_order;
+ }
+
+ dma->buflist = drm_realloc(dma->buflist,
+ dma->buf_count * sizeof(*dma->buflist),
+ (dma->buf_count + entry->buf_count)
+ * sizeof(*dma->buflist),
+ DRM_MEM_BUFS);
+ for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+ dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+ dma->buf_count += entry->buf_count;
+ dma->seg_count += entry->seg_count;
+ dma->page_count += entry->seg_count << page_order;
+ dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+
+ drm_freelist_create(&entry->freelist, entry->buf_count);
+ for (i = 0; i < entry->buf_count; i++) {
+ drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+ }
+
+ up(&dev->struct_sem);
+
+ request.count = entry->buf_count;
+ request.size = size;
+
+ copy_to_user_ret((drm_buf_desc_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ atomic_dec(&dev->buf_alloc);
+ return 0;
+}
+
+int mga_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_buf_desc_t request;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ if(request.flags & _DRM_AGP_BUFFER)
+ return mga_addbufs_agp(inode, filp, cmd, arg);
+ else
+ return mga_addbufs_pci(inode, filp, cmd, arg);
+}
+
+int mga_infobufs(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_device_dma_t *dma = dev->dma;
+ drm_buf_info_t request;
+ int i;
+ int count;
+
+ if (!dma) return -EINVAL;
+
+ spin_lock(&dev->count_lock);
+ if (atomic_read(&dev->buf_alloc)) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ ++dev->buf_use; /* Can't allocate more after this call */
+ spin_unlock(&dev->count_lock);
+
+ copy_from_user_ret(&request,
+ (drm_buf_info_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+ if (dma->bufs[i].buf_count) ++count;
+ }
+
+ DRM_DEBUG("count = %d\n", count);
+
+ if (request.count >= count) {
+ for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+ if (dma->bufs[i].buf_count) {
+ copy_to_user_ret(&request.list[count].count,
+ &dma->bufs[i].buf_count,
+ sizeof(dma->bufs[0]
+ .buf_count),
+ -EFAULT);
+ copy_to_user_ret(&request.list[count].size,
+ &dma->bufs[i].buf_size,
+ sizeof(dma->bufs[0].buf_size),
+ -EFAULT);
+ copy_to_user_ret(&request.list[count].low_mark,
+ &dma->bufs[i]
+ .freelist.low_mark,
+ sizeof(dma->bufs[0]
+ .freelist.low_mark),
+ -EFAULT);
+ copy_to_user_ret(&request.list[count]
+ .high_mark,
+ &dma->bufs[i]
+ .freelist.high_mark,
+ sizeof(dma->bufs[0]
+ .freelist.high_mark),
+ -EFAULT);
+ DRM_DEBUG("%d %d %d %d %d\n",
+ i,
+ dma->bufs[i].buf_count,
+ dma->bufs[i].buf_size,
+ dma->bufs[i].freelist.low_mark,
+ dma->bufs[i].freelist.high_mark);
+ ++count;
+ }
+ }
+ }
+ request.count = count;
+
+ copy_to_user_ret((drm_buf_info_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ return 0;
+}
+
+int mga_markbufs(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_device_dma_t *dma = dev->dma;
+ drm_buf_desc_t request;
+ int order;
+ drm_buf_entry_t *entry;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ DRM_DEBUG("%d, %d, %d\n",
+ request.size, request.low_mark, request.high_mark);
+ order = drm_order(request.size);
+ if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+ entry = &dma->bufs[order];
+
+ if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+ return -EINVAL;
+ if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+ return -EINVAL;
+
+ entry->freelist.low_mark = request.low_mark;
+ entry->freelist.high_mark = request.high_mark;
+
+ return 0;
+}
+
+int mga_freebufs(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_device_dma_t *dma = dev->dma;
+ drm_buf_free_t request;
+ int i;
+ int idx;
+ drm_buf_t *buf;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_free_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ DRM_DEBUG("%d\n", request.count);
+ for (i = 0; i < request.count; i++) {
+ copy_from_user_ret(&idx,
+ &request.list[i],
+ sizeof(idx),
+ -EFAULT);
+ if (idx < 0 || idx >= dma->buf_count) {
+ DRM_ERROR("Index %d (of %d max)\n",
+ idx, dma->buf_count - 1);
+ return -EINVAL;
+ }
+ buf = dma->buflist[idx];
+ if (buf->pid != current->pid) {
+ DRM_ERROR("Process %d freeing buffer owned by %d\n",
+ current->pid, buf->pid);
+ return -EINVAL;
+ }
+ drm_free_buffer(dev, buf);
+ }
+
+ return 0;
+}
+
+int mga_mapbufs(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_device_dma_t *dma = dev->dma;
+ int retcode = 0;
+ const int zero = 0;
+ unsigned long virtual;
+ unsigned long address;
+ drm_buf_map_t request;
+ int i;
+
+ if (!dma) return -EINVAL;
+
+ DRM_DEBUG("\n");
+
+ spin_lock(&dev->count_lock);
+ if (atomic_read(&dev->buf_alloc)) {
+ spin_unlock(&dev->count_lock);
+ DRM_DEBUG("Busy\n");
+ return -EBUSY;
+ }
+ ++dev->buf_use; /* Can't allocate more after this call */
+ spin_unlock(&dev->count_lock);
+
+ copy_from_user_ret(&request,
+ (drm_buf_map_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ DRM_DEBUG("mga_mapbufs\n");
+ DRM_DEBUG("dma->flags : %x\n", dma->flags);
+
+ if (request.count >= dma->buf_count) {
+ if(dma->flags & _DRM_DMA_USE_AGP) {
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_map_t *map = NULL;
+
+ map = dev->maplist[dev_priv->buffer_map_idx];
+ if (!map) {
+ DRM_DEBUG("map is null\n");
+ retcode = -EINVAL;
+ goto done;
+ }
+
+ DRM_DEBUG("map->offset : %lx\n", map->offset);
+ DRM_DEBUG("map->size : %lx\n", map->size);
+ DRM_DEBUG("map->type : %d\n", map->type);
+ DRM_DEBUG("map->flags : %x\n", map->flags);
+ DRM_DEBUG("map->handle : %p\n", map->handle);
+ DRM_DEBUG("map->mtrr : %d\n", map->mtrr);
+ down(&current->mm->mmap_sem);
+ virtual = do_mmap(filp, 0, map->size,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED,
+ (unsigned long)map->offset);
+ up(&current->mm->mmap_sem);
+ } else {
+ down(&current->mm->mmap_sem);
+ virtual = do_mmap(filp, 0, dma->byte_count,
+ PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+ up(&current->mm->mmap_sem);
+ }
+ if (virtual > -1024UL) {
+ /* Real error */
+ DRM_DEBUG("mmap error\n");
+ retcode = (signed long)virtual;
+ goto done;
+ }
+ request.virtual = (void *)virtual;
+
+ for (i = 0; i < dma->buf_count; i++) {
+ if (copy_to_user(&request.list[i].idx,
+ &dma->buflist[i]->idx,
+ sizeof(request.list[0].idx))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ if (copy_to_user(&request.list[i].total,
+ &dma->buflist[i]->total,
+ sizeof(request.list[0].total))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ if (copy_to_user(&request.list[i].used,
+ &zero,
+ sizeof(zero))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ address = virtual + dma->buflist[i]->offset;
+ if (copy_to_user(&request.list[i].address,
+ &address,
+ sizeof(address))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ done:
+ request.count = dma->buf_count;
+ DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+ copy_to_user_ret((drm_buf_map_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ DRM_DEBUG("retcode : %d\n", retcode);
+
+ return retcode;
+}
diff --git a/drivers/char/drm/mga_context.c b/drivers/char/drm/mga_context.c
new file mode 100644
index 000000000..d02592740
--- /dev/null
+++ b/drivers/char/drm/mga_context.c
@@ -0,0 +1,205 @@
+/* mga_context.c -- IOCTLs for mga contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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.
+ *
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#include <linux/sched.h>
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+static int mga_alloc_queue(drm_device_t *dev)
+{
+ int temp = drm_ctxbitmap_next(dev);
+ DRM_DEBUG("mga_alloc_queue: %d\n", temp);
+ return temp;
+}
+
+int mga_context_switch(drm_device_t *dev, int old, int new)
+{
+ char buf[64];
+
+ atomic_inc(&dev->total_ctx);
+
+ if (test_and_set_bit(0, &dev->context_flag)) {
+ DRM_ERROR("Reentering -- FIXME\n");
+ return -EBUSY;
+ }
+
+#if DRM_DMA_HISTOGRAM
+ dev->ctx_start = get_cycles();
+#endif
+
+ DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+ if (new == dev->last_context) {
+ clear_bit(0, &dev->context_flag);
+ return 0;
+ }
+
+ if (drm_flags & DRM_FLAG_NOCTX) {
+ mga_context_switch_complete(dev, new);
+ } else {
+ sprintf(buf, "C %d %d\n", old, new);
+ drm_write_string(dev, buf);
+ }
+
+ return 0;
+}
+
+int mga_context_switch_complete(drm_device_t *dev, int new)
+{
+ dev->last_context = new; /* PRE/POST: This is the _only_ writer. */
+ dev->last_switch = jiffies;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("Lock isn't held after context switch\n");
+ }
+
+ /* If a context switch is ever initiated
+ when the kernel holds the lock, release
+ that lock here. */
+#if DRM_DMA_HISTOGRAM
+ atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+ - dev->ctx_start)]);
+
+#endif
+ clear_bit(0, &dev->context_flag);
+ wake_up(&dev->context_wait);
+
+ return 0;
+}
+
+int mga_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_ctx_res_t res;
+ drm_ctx_t ctx;
+ int i;
+
+ DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+ copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+ if (res.count >= DRM_RESERVED_CONTEXTS) {
+ memset(&ctx, 0, sizeof(ctx));
+ for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+ ctx.handle = i;
+ copy_to_user_ret(&res.contexts[i],
+ &i,
+ sizeof(i),
+ -EFAULT);
+ }
+ }
+ res.count = DRM_RESERVED_CONTEXTS;
+ copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+ return 0;
+}
+
+int mga_addctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ if ((ctx.handle = mga_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+ /* Skip kernel's context and get a new one. */
+ ctx.handle = mga_alloc_queue(dev);
+ }
+ if (ctx.handle == -1) {
+ DRM_DEBUG("Not enough free contexts.\n");
+ /* Should this return -EBUSY instead? */
+ return -ENOMEM;
+ }
+ DRM_DEBUG("%d\n", ctx.handle);
+ copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+ return 0;
+}
+
+int mga_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ /* This does nothing for the mga */
+ return 0;
+}
+
+int mga_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+ /* This is 0, because we don't hanlde any context flags */
+ ctx.flags = 0;
+ copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+ return 0;
+}
+
+int mga_switchctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ return mga_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int mga_newctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ mga_context_switch_complete(dev, ctx.handle);
+
+ return 0;
+}
+
+int mga_rmctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ if(ctx.handle != DRM_KERNEL_CONTEXT) {
+ drm_ctxbitmap_free(dev, ctx.handle);
+ }
+
+ return 0;
+}
diff --git a/drivers/char/drm/mga_dma.c b/drivers/char/drm/mga_dma.c
new file mode 100644
index 000000000..28e8811c8
--- /dev/null
+++ b/drivers/char/drm/mga_dma.c
@@ -0,0 +1,1115 @@
+/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ * Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+#include <linux/interrupt.h> /* For task queue support */
+
+#define MGA_REG(reg) 2
+#define MGA_BASE(reg) ((unsigned long) \
+ ((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle)
+#define MGA_ADDR(reg) (MGA_BASE(reg) + reg)
+#define MGA_DEREF(reg) *(__volatile__ int *)MGA_ADDR(reg)
+#define MGA_READ(reg) MGA_DEREF(reg)
+#define MGA_WRITE(reg,val) do { MGA_DEREF(reg) = val; } while (0)
+
+#define PDEA_pagpxfer_enable 0x2
+
+static int mga_flush_queue(drm_device_t *dev);
+
+static unsigned long mga_alloc_page(drm_device_t *dev)
+{
+ unsigned long address;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ address = __get_free_page(GFP_KERNEL);
+ if(address == 0UL) {
+ return 0;
+ }
+ atomic_inc(&mem_map[MAP_NR((void *) address)].count);
+ set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags);
+
+ return address;
+}
+
+static void mga_free_page(drm_device_t *dev, unsigned long page)
+{
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if(page == 0UL) {
+ return;
+ }
+ atomic_dec(&mem_map[MAP_NR((void *) page)].count);
+ clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags);
+ wake_up(&mem_map[MAP_NR((void *) page)].wait);
+ free_page(page);
+ return;
+}
+
+static void mga_delay(void)
+{
+ return;
+}
+
+void mga_flush_write_combine(void)
+{
+ int xchangeDummy;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ __asm__ volatile(" push %%eax ; xchg %%eax, %0 ; pop %%eax" : : "m" (xchangeDummy));
+ __asm__ volatile(" push %%eax ; push %%ebx ; push %%ecx ; push %%edx ;"
+ " movl $0,%%eax ; cpuid ; pop %%edx ; pop %%ecx ; pop %%ebx ;"
+ " pop %%eax" : /* no outputs */ : /* no inputs */ );
+}
+
+/* These are two age tags that will never be sent to
+ * the hardware */
+#define MGA_BUF_USED 0xffffffff
+#define MGA_BUF_FREE 0
+
+static int mga_freelist_init(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ drm_buf_t *buf;
+ drm_mga_buf_priv_t *buf_priv;
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+ drm_mga_freelist_t *item;
+ int i;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+ if(dev_priv->head == NULL) return -ENOMEM;
+ memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t));
+ dev_priv->head->age = MGA_BUF_USED;
+
+ for (i = 0; i < dma->buf_count; i++) {
+ buf = dma->buflist[ i ];
+ buf_priv = buf->dev_private;
+ item = drm_alloc(sizeof(drm_mga_freelist_t),
+ DRM_MEM_DRIVER);
+ if(item == NULL) return -ENOMEM;
+ memset(item, 0, sizeof(drm_mga_freelist_t));
+ item->age = MGA_BUF_FREE;
+ item->prev = dev_priv->head;
+ item->next = dev_priv->head->next;
+ if(dev_priv->head->next != NULL)
+ dev_priv->head->next->prev = item;
+ if(item->next == NULL) dev_priv->tail = item;
+ item->buf = buf;
+ buf_priv->my_freelist = item;
+ buf_priv->discard = 0;
+ buf_priv->dispatched = 0;
+ dev_priv->head->next = item;
+ }
+
+ return 0;
+}
+
+static void mga_freelist_cleanup(drm_device_t *dev)
+{
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+ drm_mga_freelist_t *item;
+ drm_mga_freelist_t *prev;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ item = dev_priv->head;
+ while(item) {
+ prev = item;
+ item = item->next;
+ drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+ }
+
+ dev_priv->head = dev_priv->tail = NULL;
+}
+
+/* Frees dispatch lock */
+static inline void mga_dma_quiescent(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned long end;
+ int i;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ end = jiffies + (HZ*3);
+ while(1) {
+ if(!test_and_set_bit(MGA_IN_DISPATCH,
+ &dev_priv->dispatch_status)) {
+ break;
+ }
+ if((signed)(end - jiffies) <= 0) {
+ DRM_ERROR("irqs: %d wanted %d\n",
+ atomic_read(&dev->total_irq),
+ atomic_read(&dma->total_lost));
+ DRM_ERROR("lockup\n");
+ goto out_nolock;
+ }
+ for (i = 0 ; i < 2000 ; i++) mga_delay();
+ }
+ end = jiffies + (HZ*3);
+ DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS));
+ while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+ if((signed)(end - jiffies) <= 0) {
+ DRM_ERROR("irqs: %d wanted %d\n",
+ atomic_read(&dev->total_irq),
+ atomic_read(&dma->total_lost));
+ DRM_ERROR("lockup\n");
+ goto out_status;
+ }
+ for (i = 0 ; i < 2000 ; i++) mga_delay();
+ }
+ sarea_priv->dirty |= MGA_DMA_FLUSH;
+
+out_status:
+ clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+out_nolock:
+}
+
+static void mga_reset_freelist(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ drm_buf_t *buf;
+ drm_mga_buf_priv_t *buf_priv;
+ int i;
+
+ for (i = 0; i < dma->buf_count; i++) {
+ buf = dma->buflist[ i ];
+ buf_priv = buf->dev_private;
+ buf_priv->my_freelist->age = MGA_BUF_FREE;
+ }
+}
+
+/* Least recently used :
+ * These operations are not atomic b/c they are protected by the
+ * hardware lock */
+
+drm_buf_t *mga_freelist_get(drm_device_t *dev)
+{
+ DECLARE_WAITQUEUE(entry, current);
+ drm_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+ drm_mga_freelist_t *prev;
+ drm_mga_freelist_t *next;
+ static int failed = 0;
+
+ DRM_DEBUG("%s : tail->age : %d last_prim_age : %d\n", __FUNCTION__,
+ dev_priv->tail->age, dev_priv->last_prim_age);
+
+ if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) {
+ DRM_DEBUG("I'm waiting on the freelist!!! %d\n",
+ dev_priv->last_prim_age);
+ set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&dev_priv->buf_queue, &entry);
+ for (;;) {
+ mga_dma_schedule(dev, 0);
+ if(!test_bit(MGA_IN_GETBUF,
+ &dev_priv->dispatch_status))
+ break;
+ atomic_inc(&dev->total_sleeps);
+ schedule();
+ if (signal_pending(current)) {
+ clear_bit(MGA_IN_GETBUF,
+ &dev_priv->dispatch_status);
+ goto failed_getbuf;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev_priv->buf_queue, &entry);
+ }
+
+ if(dev_priv->tail->age < dev_priv->last_prim_age) {
+ prev = dev_priv->tail->prev;
+ next = dev_priv->tail;
+ prev->next = NULL;
+ next->prev = next->next = NULL;
+ dev_priv->tail = prev;
+ next->age = MGA_BUF_USED;
+ failed = 0;
+ return next->buf;
+ }
+
+failed_getbuf:
+ failed++;
+ return NULL;
+}
+
+int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+ drm_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+ drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+ drm_mga_freelist_t *prev;
+ drm_mga_freelist_t *head;
+ drm_mga_freelist_t *next;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if(buf_priv->my_freelist->age == MGA_BUF_USED) {
+ /* Discarded buffer, put it on the tail */
+ next = buf_priv->my_freelist;
+ next->age = MGA_BUF_FREE;
+ prev = dev_priv->tail;
+ prev->next = next;
+ next->prev = prev;
+ next->next = NULL;
+ dev_priv->tail = next;
+ DRM_DEBUG("Discarded\n");
+ } else {
+ /* Normally aged buffer, put it on the head + 1,
+ * as the real head is a sentinal element
+ */
+ next = buf_priv->my_freelist;
+ head = dev_priv->head;
+ prev = head->next;
+ head->next = next;
+ prev->prev = next;
+ next->prev = head;
+ next->next = prev;
+ }
+
+ return 0;
+}
+
+static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init)
+{
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_mga_prim_buf_t *prim_buffer;
+ int i, temp, size_of_buf;
+ int offset = init->reserved_map_agpstart;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) /
+ PAGE_SIZE) * PAGE_SIZE;
+ size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS;
+ dev_priv->warp_ucode_size = init->warp_ucode_size;
+ dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) *
+ (MGA_NUM_PRIM_BUFS + 1),
+ DRM_MEM_DRIVER);
+ if(dev_priv->prim_bufs == NULL) {
+ DRM_ERROR("Unable to allocate memory for prim_buf\n");
+ return -ENOMEM;
+ }
+ memset(dev_priv->prim_bufs,
+ 0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1));
+
+ temp = init->warp_ucode_size + dev_priv->primary_size;
+ temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+
+ dev_priv->ioremap = drm_ioremap(dev->agp->base + offset,
+ temp);
+ if(dev_priv->ioremap == NULL) {
+ DRM_DEBUG("Ioremap failed\n");
+ return -ENOMEM;
+ }
+ init_waitqueue_head(&dev_priv->wait_queue);
+
+ for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+ prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t),
+ DRM_MEM_DRIVER);
+ if(prim_buffer == NULL) return -ENOMEM;
+ memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t));
+ prim_buffer->phys_head = offset + dev->agp->base;
+ prim_buffer->current_dma_ptr =
+ prim_buffer->head =
+ (u32 *) (dev_priv->ioremap +
+ offset -
+ init->reserved_map_agpstart);
+ prim_buffer->num_dwords = 0;
+ prim_buffer->max_dwords = size_of_buf / sizeof(u32);
+ prim_buffer->max_dwords -= 5; /* Leave room for the softrap */
+ prim_buffer->sec_used = 0;
+ prim_buffer->idx = i;
+ prim_buffer->prim_age = i + 1;
+ offset = offset + size_of_buf;
+ dev_priv->prim_bufs[i] = prim_buffer;
+ }
+ dev_priv->current_prim_idx = 0;
+ dev_priv->next_prim =
+ dev_priv->last_prim =
+ dev_priv->current_prim =
+ dev_priv->prim_bufs[0];
+ dev_priv->next_prim_age = 2;
+ dev_priv->last_prim_age = 1;
+ set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status);
+ return 0;
+}
+
+void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim)
+{
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ int use_agp = PDEA_pagpxfer_enable;
+ unsigned long end;
+ int i;
+ int next_idx;
+ PRIMLOCALS;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ dev_priv->last_prim = prim;
+
+ /* We never check for overflow, b/c there is always room */
+ PRIMPTR(prim);
+ if(num_dwords <= 0) {
+ DRM_DEBUG("num_dwords == 0 when dispatched\n");
+ goto out_prim_wait;
+ }
+ PRIMOUTREG( MGAREG_DMAPAD, 0);
+ PRIMOUTREG( MGAREG_DMAPAD, 0);
+ PRIMOUTREG( MGAREG_DMAPAD, 0);
+ PRIMOUTREG( MGAREG_SOFTRAP, 0);
+ PRIMFINISH(prim);
+
+ end = jiffies + (HZ*3);
+ if(sarea_priv->dirty & MGA_DMA_FLUSH) {
+ DRM_DEBUG("Dma top flush\n");
+ while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+ if((signed)(end - jiffies) <= 0) {
+ DRM_ERROR("irqs: %d wanted %d\n",
+ atomic_read(&dev->total_irq),
+ atomic_read(&dma->total_lost));
+ DRM_ERROR("lockup in fire primary "
+ "(Dma Top Flush)\n");
+ goto out_prim_wait;
+ }
+
+ for (i = 0 ; i < 4096 ; i++) mga_delay();
+ }
+ sarea_priv->dirty &= ~(MGA_DMA_FLUSH);
+ } else {
+ DRM_DEBUG("Status wait\n");
+ while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) {
+ if((signed)(end - jiffies) <= 0) {
+ DRM_ERROR("irqs: %d wanted %d\n",
+ atomic_read(&dev->total_irq),
+ atomic_read(&dma->total_lost));
+ DRM_ERROR("lockup in fire primary "
+ "(Status Wait)\n");
+ goto out_prim_wait;
+ }
+
+ for (i = 0 ; i < 4096 ; i++) mga_delay();
+ }
+ }
+
+ mga_flush_write_combine();
+ atomic_inc(&dev_priv->pending_bufs);
+ MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+ MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp);
+ prim->num_dwords = 0;
+ sarea_priv->last_enqueue = prim->prim_age;
+
+ next_idx = prim->idx + 1;
+ if(next_idx >= MGA_NUM_PRIM_BUFS)
+ next_idx = 0;
+
+ dev_priv->next_prim = dev_priv->prim_bufs[next_idx];
+ return;
+
+ out_prim_wait:
+ prim->num_dwords = 0;
+ prim->sec_used = 0;
+ clear_bit(MGA_BUF_IN_USE, &prim->buffer_status);
+ wake_up_interruptible(&dev_priv->wait_queue);
+ clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status);
+ clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+}
+
+int mga_advance_primary(drm_device_t *dev)
+{
+ DECLARE_WAITQUEUE(entry, current);
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_mga_prim_buf_t *prim_buffer;
+ drm_device_dma_t *dma = dev->dma;
+ int next_prim_idx;
+ int ret = 0;
+
+ /* This needs to reset the primary buffer if available,
+ * we should collect stats on how many times it bites
+ * it's tail */
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ next_prim_idx = dev_priv->current_prim_idx + 1;
+ if(next_prim_idx >= MGA_NUM_PRIM_BUFS)
+ next_prim_idx = 0;
+ prim_buffer = dev_priv->prim_bufs[next_prim_idx];
+ set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+
+ /* In use is cleared in interrupt handler */
+
+ if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) {
+ add_wait_queue(&dev_priv->wait_queue, &entry);
+ current->state = TASK_INTERRUPTIBLE;
+
+ for (;;) {
+ mga_dma_schedule(dev, 0);
+ if(!test_and_set_bit(MGA_BUF_IN_USE,
+ &prim_buffer->buffer_status))
+ break;
+ atomic_inc(&dev->total_sleeps);
+ atomic_inc(&dma->total_missed_sched);
+ schedule();
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev_priv->wait_queue, &entry);
+ if(ret) return ret;
+ }
+ clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+
+ /* This primary buffer is now free to use */
+ prim_buffer->current_dma_ptr = prim_buffer->head;
+ prim_buffer->num_dwords = 0;
+ prim_buffer->sec_used = 0;
+ prim_buffer->prim_age = dev_priv->next_prim_age++;
+ if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) {
+ mga_flush_queue(dev);
+ mga_dma_quiescent(dev);
+ mga_reset_freelist(dev);
+ prim_buffer->prim_age = (dev_priv->next_prim_age += 2);
+ }
+
+ /* Reset all buffer status stuff */
+ clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status);
+ clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status);
+ clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status);
+
+ dev_priv->current_prim = prim_buffer;
+ dev_priv->current_prim_idx = next_prim_idx;
+ return 0;
+}
+
+/* More dynamic performance decisions */
+static inline int mga_decide_to_fire(drm_device_t *dev)
+{
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) {
+ atomic_inc(&dma->total_prio);
+ return 1;
+ }
+
+ if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) &&
+ dev_priv->next_prim->num_dwords) {
+ atomic_inc(&dma->total_prio);
+ return 1;
+ }
+
+ if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+ dev_priv->next_prim->num_dwords) {
+ atomic_inc(&dma->total_prio);
+ return 1;
+ }
+
+ if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) {
+ if(test_bit(MGA_BUF_SWAP_PENDING,
+ &dev_priv->next_prim->buffer_status)) {
+ atomic_inc(&dma->total_dmas);
+ return 1;
+ }
+ }
+
+ if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) {
+ if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) {
+ atomic_inc(&dma->total_hit);
+ return 1;
+ }
+ }
+
+ if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) {
+ if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) {
+ atomic_inc(&dma->total_missed_free);
+ return 1;
+ }
+ }
+
+ atomic_inc(&dma->total_tried);
+ return 0;
+}
+
+int mga_dma_schedule(drm_device_t *dev, int locked)
+{
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+ int retval = 0;
+
+ if (test_and_set_bit(0, &dev->dma_flag)) {
+ atomic_inc(&dma->total_missed_dma);
+ retval = -EBUSY;
+ goto sch_out_wakeup;
+ }
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) ||
+ test_bit(MGA_IN_WAIT, &dev_priv->dispatch_status) ||
+ test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) {
+ locked = 1;
+ }
+
+ if (!locked &&
+ !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) {
+ atomic_inc(&dma->total_missed_lock);
+ clear_bit(0, &dev->dma_flag);
+ DRM_DEBUG("Not locked\n");
+ retval = -EBUSY;
+ goto sch_out_wakeup;
+ }
+ DRM_DEBUG("I'm locked\n");
+
+ if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) {
+ /* Fire dma buffer */
+ if(mga_decide_to_fire(dev)) {
+ DRM_DEBUG("idx :%d\n", dev_priv->next_prim->idx);
+ clear_bit(MGA_BUF_FORCE_FIRE,
+ &dev_priv->next_prim->buffer_status);
+ if(dev_priv->current_prim == dev_priv->next_prim) {
+ /* Schedule overflow for a later time */
+ set_bit(MGA_BUF_NEEDS_OVERFLOW,
+ &dev_priv->next_prim->buffer_status);
+ }
+ mga_fire_primary(dev, dev_priv->next_prim);
+ } else {
+ clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+ }
+ } else {
+ DRM_DEBUG("I can't get the dispatch lock\n");
+ }
+
+ if (!locked) {
+ if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ DRM_ERROR("\n");
+ }
+ }
+
+sch_out_wakeup:
+ if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+ atomic_read(&dev_priv->pending_bufs) == 0) {
+ /* Everything has been processed by the hardware */
+ clear_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+ wake_up_interruptible(&dev_priv->flush_queue);
+ }
+
+ if(test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) &&
+ dev_priv->tail->age < dev_priv->last_prim_age) {
+ clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+ DRM_DEBUG("Waking up buf queue\n");
+ wake_up_interruptible(&dev_priv->buf_queue);
+ } else if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) {
+ DRM_DEBUG("Not waking buf_queue on %d %d\n",
+ atomic_read(&dev->total_irq),
+ dev_priv->last_prim_age);
+ }
+
+ clear_bit(0, &dev->dma_flag);
+ return retval;
+}
+
+static void mga_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+ drm_device_t *dev = (drm_device_t *)device;
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+ drm_mga_prim_buf_t *last_prim_buffer;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ atomic_inc(&dev->total_irq);
+ if((MGA_READ(MGAREG_STATUS) & 0x00000001) != 0x00000001) return;
+ MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+ last_prim_buffer = dev_priv->last_prim;
+ last_prim_buffer->num_dwords = 0;
+ last_prim_buffer->sec_used = 0;
+ dev_priv->sarea_priv->last_dispatch =
+ dev_priv->last_prim_age = last_prim_buffer->prim_age;
+ clear_bit(MGA_BUF_IN_USE, &last_prim_buffer->buffer_status);
+ wake_up_interruptible(&dev_priv->wait_queue);
+ clear_bit(MGA_BUF_SWAP_PENDING, &last_prim_buffer->buffer_status);
+ clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+ atomic_dec(&dev_priv->pending_bufs);
+ queue_task(&dev->tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static void mga_dma_task_queue(void *device)
+{
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ mga_dma_schedule((drm_device_t *)device, 0);
+}
+
+int mga_dma_cleanup(drm_device_t *dev)
+{
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if(dev->dev_private) {
+ drm_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+
+ if(dev_priv->ioremap) {
+ int temp = (dev_priv->warp_ucode_size +
+ dev_priv->primary_size +
+ PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
+
+ drm_ioremapfree((void *) dev_priv->ioremap, temp);
+ }
+ if(dev_priv->status_page != NULL) {
+ iounmap(dev_priv->status_page);
+ }
+ if(dev_priv->real_status_page != 0UL) {
+ mga_free_page(dev, dev_priv->real_status_page);
+ }
+ if(dev_priv->prim_bufs != NULL) {
+ int i;
+ for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+ if(dev_priv->prim_bufs[i] != NULL) {
+ drm_free(dev_priv->prim_bufs[i],
+ sizeof(drm_mga_prim_buf_t),
+ DRM_MEM_DRIVER);
+ }
+ }
+ drm_free(dev_priv->prim_bufs, sizeof(void *) *
+ (MGA_NUM_PRIM_BUFS + 1),
+ DRM_MEM_DRIVER);
+ }
+ if(dev_priv->head != NULL) {
+ mga_freelist_cleanup(dev);
+ }
+
+
+ drm_free(dev->dev_private, sizeof(drm_mga_private_t),
+ DRM_MEM_DRIVER);
+ dev->dev_private = NULL;
+ }
+
+ return 0;
+}
+
+static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) {
+ drm_mga_private_t *dev_priv;
+ drm_map_t *sarea_map = NULL;
+ int i;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
+ if(dev_priv == NULL) return -ENOMEM;
+ dev->dev_private = (void *) dev_priv;
+
+ memset(dev_priv, 0, sizeof(drm_mga_private_t));
+
+ if((init->reserved_map_idx >= dev->map_count) ||
+ (init->buffer_map_idx >= dev->map_count)) {
+ mga_dma_cleanup(dev);
+ DRM_DEBUG("reserved_map or buffer_map are invalid\n");
+ return -EINVAL;
+ }
+
+ dev_priv->reserved_map_idx = init->reserved_map_idx;
+ dev_priv->buffer_map_idx = init->buffer_map_idx;
+ sarea_map = dev->maplist[0];
+ dev_priv->sarea_priv = (drm_mga_sarea_t *)
+ ((u8 *)sarea_map->handle +
+ init->sarea_priv_offset);
+
+ /* Scale primary size to the next page */
+ dev_priv->chipset = init->chipset;
+ dev_priv->frontOffset = init->frontOffset;
+ dev_priv->backOffset = init->backOffset;
+ dev_priv->depthOffset = init->depthOffset;
+ dev_priv->textureOffset = init->textureOffset;
+ dev_priv->textureSize = init->textureSize;
+ dev_priv->cpp = init->cpp;
+ dev_priv->sgram = init->sgram;
+ dev_priv->stride = init->stride;
+
+ dev_priv->mAccess = init->mAccess;
+ init_waitqueue_head(&dev_priv->flush_queue);
+ init_waitqueue_head(&dev_priv->buf_queue);
+ dev_priv->WarpPipe = -1;
+
+ DRM_DEBUG("chipset: %d ucode_size: %d backOffset: %x depthOffset: %x\n",
+ dev_priv->chipset, dev_priv->warp_ucode_size,
+ dev_priv->backOffset, dev_priv->depthOffset);
+ DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n",
+ dev_priv->cpp, dev_priv->sgram, dev_priv->stride,
+ dev_priv->mAccess);
+
+ memcpy(&dev_priv->WarpIndex, &init->WarpIndex,
+ sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES);
+
+ for (i = 0 ; i < MGA_MAX_WARP_PIPES ; i++)
+ DRM_DEBUG("warp pipe %d: installed: %d phys: %lx size: %x\n",
+ i,
+ dev_priv->WarpIndex[i].installed,
+ dev_priv->WarpIndex[i].phys_addr,
+ dev_priv->WarpIndex[i].size);
+
+ if(mga_init_primary_bufs(dev, init) != 0) {
+ DRM_ERROR("Can not initialize primary buffers\n");
+ mga_dma_cleanup(dev);
+ return -ENOMEM;
+ }
+ dev_priv->real_status_page = mga_alloc_page(dev);
+ if(dev_priv->real_status_page == 0UL) {
+ mga_dma_cleanup(dev);
+ DRM_ERROR("Can not allocate status page\n");
+ return -ENOMEM;
+ }
+
+ dev_priv->status_page =
+ ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page),
+ PAGE_SIZE);
+
+ if(dev_priv->status_page == NULL) {
+ mga_dma_cleanup(dev);
+ DRM_ERROR("Can not remap status page\n");
+ return -ENOMEM;
+ }
+
+ /* Write status page when secend or softrap occurs */
+ MGA_WRITE(MGAREG_PRIMPTR,
+ virt_to_bus((void *)dev_priv->real_status_page) | 0x00000003);
+
+
+ /* Private is now filled in, initialize the hardware */
+ {
+ PRIMLOCALS;
+ PRIMGETPTR( dev_priv );
+
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DWGSYNC, 0x0100);
+ PRIMOUTREG(MGAREG_SOFTRAP, 0);
+ /* Poll for the first buffer to insure that
+ * the status register will be correct
+ */
+
+ mga_flush_write_combine();
+ MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+
+ MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) |
+ PDEA_pagpxfer_enable));
+
+ while(MGA_READ(MGAREG_DWGSYNC) != 0x0100) ;
+ }
+
+ if(mga_freelist_init(dev) != 0) {
+ DRM_ERROR("Could not initialize freelist\n");
+ mga_dma_cleanup(dev);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+int mga_dma_init(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_mga_init_t init;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ copy_from_user_ret(&init, (drm_mga_init_t *)arg, sizeof(init), -EFAULT);
+
+ switch(init.func) {
+ case MGA_INIT_DMA:
+ return mga_dma_initialize(dev, &init);
+ case MGA_CLEANUP_DMA:
+ return mga_dma_cleanup(dev);
+ }
+
+ return -EINVAL;
+}
+
+int mga_irq_install(drm_device_t *dev, int irq)
+{
+ int retcode;
+
+ if (!irq) return -EINVAL;
+
+ down(&dev->struct_sem);
+ if (dev->irq) {
+ up(&dev->struct_sem);
+ return -EBUSY;
+ }
+ dev->irq = irq;
+ up(&dev->struct_sem);
+
+ DRM_DEBUG("install irq handler %d\n", irq);
+
+ dev->context_flag = 0;
+ dev->interrupt_flag = 0;
+ dev->dma_flag = 0;
+ dev->dma->next_buffer = NULL;
+ dev->dma->next_queue = NULL;
+ dev->dma->this_buffer = NULL;
+ dev->tq.next = NULL;
+ dev->tq.sync = 0;
+ dev->tq.routine = mga_dma_task_queue;
+ dev->tq.data = dev;
+
+ /* Before installing handler */
+ MGA_WRITE(MGAREG_IEN, 0);
+ /* Install handler */
+ if ((retcode = request_irq(dev->irq,
+ mga_dma_service,
+ SA_SHIRQ,
+ dev->devname,
+ dev))) {
+ down(&dev->struct_sem);
+ dev->irq = 0;
+ up(&dev->struct_sem);
+ return retcode;
+ }
+ /* After installing handler */
+ MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+ MGA_WRITE(MGAREG_IEN, 0x00000001);
+ return 0;
+}
+
+int mga_irq_uninstall(drm_device_t *dev)
+{
+ int irq;
+
+ down(&dev->struct_sem);
+ irq = dev->irq;
+ dev->irq = 0;
+ up(&dev->struct_sem);
+
+ if (!irq) return -EINVAL;
+ DRM_DEBUG("remove irq handler %d\n", irq);
+ MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+ MGA_WRITE(MGAREG_IEN, 0);
+ free_irq(irq, dev);
+ return 0;
+}
+
+int mga_control(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_control_t ctl;
+
+ copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT);
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ switch (ctl.func) {
+ case DRM_INST_HANDLER:
+ return mga_irq_install(dev, ctl.irq);
+ case DRM_UNINST_HANDLER:
+ return mga_irq_uninstall(dev);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mga_flush_queue(drm_device_t *dev)
+{
+ DECLARE_WAITQUEUE(entry, current);
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+ int ret = 0;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if(dev_priv == NULL) {
+ return 0;
+ }
+
+ if(dev_priv->next_prim->num_dwords != 0) {
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&dev_priv->flush_queue, &entry);
+ set_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+ mga_dma_schedule(dev, 0);
+ for (;;) {
+ if (!test_bit(MGA_IN_FLUSH,
+ &dev_priv->dispatch_status))
+ break;
+ atomic_inc(&dev->total_sleeps);
+ schedule();
+ if (signal_pending(current)) {
+ ret = -EINTR; /* Can't restart */
+ clear_bit(MGA_IN_FLUSH,
+ &dev_priv->dispatch_status);
+ break;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev_priv->flush_queue, &entry);
+ }
+ return ret;
+}
+
+/* Must be called with the lock held */
+void mga_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+ drm_device_dma_t *dma = dev->dma;
+ int i;
+
+ if (!dma) return;
+ if(dev->dev_private == NULL) return;
+ if(dma->buflist == NULL) return;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ mga_flush_queue(dev);
+
+ for (i = 0; i < dma->buf_count; i++) {
+ drm_buf_t *buf = dma->buflist[ i ];
+ drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+
+ /* Only buffers that need to get reclaimed ever
+ * get set to free
+ */
+ if (buf->pid == pid && buf_priv) {
+ if(buf_priv->my_freelist->age == MGA_BUF_USED)
+ buf_priv->my_freelist->age = MGA_BUF_FREE;
+ }
+ }
+}
+
+int mga_lock(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;
+ DECLARE_WAITQUEUE(entry, current);
+ int ret = 0;
+ drm_lock_t lock;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+ if (lock.context == DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("Process %d using kernel context %d\n",
+ current->pid, lock.context);
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+ lock.context, current->pid, dev->lock.hw_lock->lock,
+ lock.flags);
+
+ if (lock.context < 0) {
+ return -EINVAL;
+ }
+
+ /* Only one queue:
+ */
+
+ if (!ret) {
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ ret = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ lock.context)) {
+ dev->lock.pid = current->pid;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->total_locks);
+ break; /* Got lock */
+ }
+
+ /* Contention */
+ atomic_inc(&dev->total_sleeps);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ }
+
+ if (!ret) {
+ if (lock.flags & _DRM_LOCK_QUIESCENT) {
+ DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+ mga_flush_queue(dev);
+ mga_dma_quiescent(dev);
+ }
+ }
+
+ DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+ return ret;
+}
+
+int mga_flush_ioctl(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_lock_t lock;
+ drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+ copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+ if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("mga_flush_ioctl called without lock held\n");
+ return -EINVAL;
+ }
+
+ if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) {
+ drm_mga_prim_buf_t *temp_buf;
+
+ temp_buf = dev_priv->current_prim;
+
+ if(temp_buf && temp_buf->num_dwords) {
+ set_bit(MGA_BUF_FORCE_FIRE, &temp_buf->buffer_status);
+ mga_advance_primary(dev);
+ }
+ mga_dma_schedule(dev, 1);
+ }
+ if(lock.flags & _DRM_LOCK_QUIESCENT) {
+ mga_flush_queue(dev);
+ mga_dma_quiescent(dev);
+ }
+
+ return 0;
+}
diff --git a/drivers/char/drm/mga_drm.h b/drivers/char/drm/mga_drm.h
new file mode 100644
index 000000000..e75e91a4f
--- /dev/null
+++ b/drivers/char/drm/mga_drm.h
@@ -0,0 +1,269 @@
+/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Jeff Hartmann <jhartmann@valinux.com>
+ * Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRM_H_
+#define _MGA_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmMga.h)
+ */
+#ifndef _MGA_DEFINES_
+#define _MGA_DEFINES_
+
+#define MGA_F 0x1 /* fog */
+#define MGA_A 0x2 /* alpha */
+#define MGA_S 0x4 /* specular */
+#define MGA_T2 0x8 /* multitexture */
+
+#define MGA_WARP_TGZ 0
+#define MGA_WARP_TGZF (MGA_F)
+#define MGA_WARP_TGZA (MGA_A)
+#define MGA_WARP_TGZAF (MGA_F|MGA_A)
+#define MGA_WARP_TGZS (MGA_S)
+#define MGA_WARP_TGZSF (MGA_S|MGA_F)
+#define MGA_WARP_TGZSA (MGA_S|MGA_A)
+#define MGA_WARP_TGZSAF (MGA_S|MGA_F|MGA_A)
+#define MGA_WARP_T2GZ (MGA_T2)
+#define MGA_WARP_T2GZF (MGA_T2|MGA_F)
+#define MGA_WARP_T2GZA (MGA_T2|MGA_A)
+#define MGA_WARP_T2GZAF (MGA_T2|MGA_A|MGA_F)
+#define MGA_WARP_T2GZS (MGA_T2|MGA_S)
+#define MGA_WARP_T2GZSF (MGA_T2|MGA_S|MGA_F)
+#define MGA_WARP_T2GZSA (MGA_T2|MGA_S|MGA_A)
+#define MGA_WARP_T2GZSAF (MGA_T2|MGA_S|MGA_F|MGA_A)
+
+#define MGA_MAX_G400_PIPES 16
+#define MGA_MAX_G200_PIPES 8 /* no multitex */
+#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES
+
+#define MGA_CARD_TYPE_G200 1
+#define MGA_CARD_TYPE_G400 2
+
+#define MGA_FRONT 0x1
+#define MGA_BACK 0x2
+#define MGA_DEPTH 0x4
+
+/* 3d state excluding texture units:
+ */
+#define MGA_CTXREG_DSTORG 0 /* validated */
+#define MGA_CTXREG_MACCESS 1
+#define MGA_CTXREG_PLNWT 2
+#define MGA_CTXREG_DWGCTL 3
+#define MGA_CTXREG_ALPHACTRL 4
+#define MGA_CTXREG_FOGCOLOR 5
+#define MGA_CTXREG_WFLAG 6
+#define MGA_CTXREG_TDUAL0 7
+#define MGA_CTXREG_TDUAL1 8
+#define MGA_CTXREG_FCOL 9
+#define MGA_CTX_SETUP_SIZE 10
+
+/* 2d state
+ */
+#define MGA_2DREG_PITCH 0
+#define MGA_2D_SETUP_SIZE 1
+
+/* Each texture unit has a state:
+ */
+#define MGA_TEXREG_CTL 0
+#define MGA_TEXREG_CTL2 1
+#define MGA_TEXREG_FILTER 2
+#define MGA_TEXREG_BORDERCOL 3
+#define MGA_TEXREG_ORG 4 /* validated */
+#define MGA_TEXREG_ORG1 5
+#define MGA_TEXREG_ORG2 6
+#define MGA_TEXREG_ORG3 7
+#define MGA_TEXREG_ORG4 8
+#define MGA_TEXREG_WIDTH 9
+#define MGA_TEXREG_HEIGHT 10
+#define MGA_TEX_SETUP_SIZE 11
+
+/* What needs to be changed for the current vertex dma buffer?
+ */
+#define MGA_UPLOAD_CTX 0x1
+#define MGA_UPLOAD_TEX0 0x2
+#define MGA_UPLOAD_TEX1 0x4
+#define MGA_UPLOAD_PIPE 0x8
+#define MGA_UPLOAD_TEX0IMAGE 0x10 /* handled client-side */
+#define MGA_UPLOAD_TEX1IMAGE 0x20 /* handled client-side */
+#define MGA_UPLOAD_2D 0x40
+#define MGA_WAIT_AGE 0x80 /* handled client-side */
+#define MGA_UPLOAD_CLIPRECTS 0x100 /* handled client-side */
+#define MGA_DMA_FLUSH 0x200 /* set when someone gets the lock
+ quiescent */
+
+/* 32 buffers of 64k each, total 2 meg.
+ */
+#define MGA_DMA_BUF_ORDER 16
+#define MGA_DMA_BUF_SZ (1<<MGA_DMA_BUF_ORDER)
+#define MGA_DMA_BUF_NR 31
+
+/* Keep these small for testing.
+ */
+#define MGA_NR_SAREA_CLIPRECTS 8
+
+/* 2 heaps (1 for card, 1 for agp), each divided into upto 128
+ * regions, subject to a minimum region size of (1<<16) == 64k.
+ *
+ * Clients may subdivide regions internally, but when sharing between
+ * clients, the region size is the minimum granularity.
+ */
+
+#define MGA_CARD_HEAP 0
+#define MGA_AGP_HEAP 1
+#define MGA_NR_TEX_HEAPS 2
+#define MGA_NR_TEX_REGIONS 16
+#define MGA_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+typedef struct _drm_mga_warp_index {
+ int installed;
+ unsigned long phys_addr;
+ int size;
+} drm_mga_warp_index_t;
+
+typedef struct drm_mga_init {
+ enum {
+ MGA_INIT_DMA = 0x01,
+ MGA_CLEANUP_DMA = 0x02
+ } func;
+ int reserved_map_agpstart;
+ int reserved_map_idx;
+ int buffer_map_idx;
+ int sarea_priv_offset;
+ int primary_size;
+ int warp_ucode_size;
+ unsigned int frontOffset;
+ unsigned int backOffset;
+ unsigned int depthOffset;
+ unsigned int textureOffset;
+ unsigned int textureSize;
+ unsigned int agpTextureOffset;
+ unsigned int agpTextureSize;
+ unsigned int cpp;
+ unsigned int stride;
+ int sgram;
+ int chipset;
+ drm_mga_warp_index_t WarpIndex[MGA_MAX_WARP_PIPES];
+ unsigned int mAccess;
+} drm_mga_init_t;
+
+/* Warning: if you change the sarea structure, you must change the Xserver
+ * structures as well */
+
+typedef struct _drm_mga_tex_region {
+ unsigned char next, prev;
+ unsigned char in_use;
+ unsigned int age;
+} drm_mga_tex_region_t;
+
+typedef struct _drm_mga_sarea {
+ /* The channel for communication of state information to the kernel
+ * on firing a vertex dma buffer.
+ */
+ unsigned int ContextState[MGA_CTX_SETUP_SIZE];
+ unsigned int ServerState[MGA_2D_SETUP_SIZE];
+ unsigned int TexState[2][MGA_TEX_SETUP_SIZE];
+ unsigned int WarpPipe;
+ unsigned int dirty;
+
+ unsigned int nbox;
+ drm_clip_rect_t boxes[MGA_NR_SAREA_CLIPRECTS];
+
+
+ /* Information about the most recently used 3d drawable. The
+ * client fills in the req_* fields, the server fills in the
+ * exported_ fields and puts the cliprects into boxes, above.
+ *
+ * The client clears the exported_drawable field before
+ * clobbering the boxes data.
+ */
+ unsigned int req_drawable; /* the X drawable id */
+ unsigned int req_draw_buffer; /* MGA_FRONT or MGA_BACK */
+
+ unsigned int exported_drawable;
+ unsigned int exported_index;
+ unsigned int exported_stamp;
+ unsigned int exported_buffers;
+ unsigned int exported_nfront;
+ unsigned int exported_nback;
+ int exported_back_x, exported_front_x, exported_w;
+ int exported_back_y, exported_front_y, exported_h;
+ drm_clip_rect_t exported_boxes[MGA_NR_SAREA_CLIPRECTS];
+
+ /* Counters for aging textures and for client-side throttling.
+ */
+ unsigned int last_enqueue; /* last time a buffer was enqueued */
+ unsigned int last_dispatch; /* age of the most recently dispatched buffer */
+ unsigned int last_quiescent; /* */
+
+
+ /* LRU lists for texture memory in agp space and on the card
+ */
+ drm_mga_tex_region_t texList[MGA_NR_TEX_HEAPS][MGA_NR_TEX_REGIONS+1];
+ unsigned int texAge[MGA_NR_TEX_HEAPS];
+
+ /* Mechanism to validate card state.
+ */
+ int ctxOwner;
+} drm_mga_sarea_t;
+
+/* Device specific ioctls:
+ */
+typedef struct _drm_mga_clear {
+ unsigned int clear_color;
+ unsigned int clear_depth;
+ unsigned int flags;
+} drm_mga_clear_t;
+
+typedef struct _drm_mga_swap {
+ int dummy;
+} drm_mga_swap_t;
+
+typedef struct _drm_mga_iload {
+ int idx;
+ int length;
+ unsigned int destOrg;
+} drm_mga_iload_t;
+
+typedef struct _drm_mga_vertex {
+ int idx; /* buffer to queue */
+ int used; /* bytes in use */
+ int discard; /* client finished with buffer? */
+} drm_mga_vertex_t;
+
+typedef struct _drm_mga_indices {
+ int idx; /* buffer to queue */
+ unsigned int start;
+ unsigned int end;
+ int discard; /* client finished with buffer? */
+} drm_mga_indices_t;
+
+#endif
diff --git a/drivers/char/drm/mga_drv.c b/drivers/char/drm/mga_drv.c
new file mode 100644
index 000000000..30c8880fb
--- /dev/null
+++ b/drivers/char/drm/mga_drv.c
@@ -0,0 +1,654 @@
+/* mga_drv.c -- Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ *
+ */
+
+#include <linux/config.h>
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
+#include "drmP.h"
+#include "mga_drv.h"
+EXPORT_SYMBOL(mga_init);
+EXPORT_SYMBOL(mga_cleanup);
+
+#define MGA_NAME "mga"
+#define MGA_DESC "Matrox g200/g400"
+#define MGA_DATE "20000719"
+#define MGA_MAJOR 1
+#define MGA_MINOR 0
+#define MGA_PATCHLEVEL 0
+
+static drm_device_t mga_device;
+drm_ctx_t mga_res_ctx;
+
+static struct file_operations mga_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+ /* This started being used approx. 2.3.34 */
+ owner: THIS_MODULE,
+#endif
+ open: mga_open,
+ flush: drm_flush,
+ release: mga_release,
+ ioctl: mga_ioctl,
+ mmap: drm_mmap,
+ read: drm_read,
+ fasync: drm_fasync,
+ poll: drm_poll,
+};
+
+static struct miscdevice mga_misc = {
+ minor: MISC_DYNAMIC_MINOR,
+ name: MGA_NAME,
+ fops: &mga_fops,
+};
+
+static drm_ioctl_desc_t mga_ioctls[] = {
+ [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { mga_version, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { mga_control, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { mga_addbufs, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { mga_markbufs, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { mga_infobufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { mga_mapbufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { mga_freebufs, 1, 0 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { mga_addctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { mga_rmctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { mga_modctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { mga_getctx, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { mga_switchctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { mga_newctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { mga_resctx, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { mga_dma, 1, 0 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { mga_lock, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { mga_unlock, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)] = { mga_dma_init, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)] = { mga_swap_bufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)] = { mga_clear_bufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)] = { mga_iload, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)] = { mga_vertex, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)] = { mga_flush_ioctl, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_indices, 1, 0 },
+};
+
+#define MGA_IOCTL_COUNT DRM_ARRAY_SIZE(mga_ioctls)
+
+#ifdef MODULE
+static char *mga = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Matrox g200/g400");
+MODULE_PARM(mga, "s");
+
+module_init(mga_init);
+module_exit(mga_cleanup);
+
+#ifndef MODULE
+/* mga_options is called by the kernel to parse command-line options passed
+ * via the boot-loader (e.g., LILO). It calls the insmod option routine,
+ * drm_parse_drm.
+ */
+
+static int __init mga_options(char *str)
+{
+ drm_parse_options(str);
+ return 1;
+}
+
+__setup("mga=", mga_options);
+#endif
+
+static int mga_setup(drm_device_t *dev)
+{
+ int i;
+
+ atomic_set(&dev->ioctl_count, 0);
+ atomic_set(&dev->vma_count, 0);
+ dev->buf_use = 0;
+ atomic_set(&dev->buf_alloc, 0);
+
+ drm_dma_setup(dev);
+
+ atomic_set(&dev->total_open, 0);
+ atomic_set(&dev->total_close, 0);
+ atomic_set(&dev->total_ioctl, 0);
+ atomic_set(&dev->total_irq, 0);
+ atomic_set(&dev->total_ctx, 0);
+ atomic_set(&dev->total_locks, 0);
+ atomic_set(&dev->total_unlocks, 0);
+ atomic_set(&dev->total_contends, 0);
+ atomic_set(&dev->total_sleeps, 0);
+
+ for (i = 0; i < DRM_HASH_SIZE; i++) {
+ dev->magiclist[i].head = NULL;
+ dev->magiclist[i].tail = NULL;
+ }
+ dev->maplist = NULL;
+ dev->map_count = 0;
+ dev->vmalist = NULL;
+ dev->lock.hw_lock = NULL;
+ init_waitqueue_head(&dev->lock.lock_queue);
+ dev->queue_count = 0;
+ dev->queue_reserved = 0;
+ dev->queue_slots = 0;
+ dev->queuelist = NULL;
+ dev->irq = 0;
+ dev->context_flag = 0;
+ dev->interrupt_flag = 0;
+ dev->dma_flag = 0;
+ dev->last_context = 0;
+ dev->last_switch = 0;
+ dev->last_checked = 0;
+ init_timer(&dev->timer);
+ init_waitqueue_head(&dev->context_wait);
+
+ dev->ctx_start = 0;
+ dev->lck_start = 0;
+
+ dev->buf_rp = dev->buf;
+ dev->buf_wp = dev->buf;
+ dev->buf_end = dev->buf + DRM_BSZ;
+ dev->buf_async = NULL;
+ init_waitqueue_head(&dev->buf_readers);
+ init_waitqueue_head(&dev->buf_writers);
+
+ DRM_DEBUG("\n");
+
+ /* The kernel's context could be created here, but is now created
+ in drm_dma_enqueue. This is more resource-efficient for
+ hardware that does not do DMA, but may mean that
+ drm_select_queue fails between the time the interrupt is
+ initialized and the time the queues are initialized. */
+
+ return 0;
+}
+
+
+static int mga_takedown(drm_device_t *dev)
+{
+ int i;
+ drm_magic_entry_t *pt, *next;
+ drm_map_t *map;
+ drm_vma_entry_t *vma, *vma_next;
+
+ DRM_DEBUG("\n");
+
+ if (dev->irq) mga_irq_uninstall(dev);
+
+ down(&dev->struct_sem);
+ del_timer(&dev->timer);
+
+ if (dev->devname) {
+ drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+ dev->devname = NULL;
+ }
+
+ if (dev->unique) {
+ drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+ dev->unique = NULL;
+ dev->unique_len = 0;
+ }
+ /* Clear pid list */
+ for (i = 0; i < DRM_HASH_SIZE; i++) {
+ for (pt = dev->magiclist[i].head; pt; pt = next) {
+ next = pt->next;
+ drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+ }
+ dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+ }
+ /* Clear AGP information */
+ if (dev->agp) {
+ drm_agp_mem_t *entry;
+ drm_agp_mem_t *nexte;
+
+ /* Remove AGP resources, but leave dev->agp
+ intact until cleanup is called. */
+ for (entry = dev->agp->memory; entry; entry = nexte) {
+ nexte = entry->next;
+ if (entry->bound) drm_unbind_agp(entry->memory);
+ drm_free_agp(entry->memory, entry->pages);
+ drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+ }
+ dev->agp->memory = NULL;
+
+ if (dev->agp->acquired && drm_agp.release)
+ (*drm_agp.release)();
+
+ dev->agp->acquired = 0;
+ dev->agp->enabled = 0;
+ }
+ /* Clear vma list (only built for debugging) */
+ if (dev->vmalist) {
+ for (vma = dev->vmalist; vma; vma = vma_next) {
+ vma_next = vma->next;
+ drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+ }
+ dev->vmalist = NULL;
+ }
+
+ /* Clear map area and mtrr information */
+ if (dev->maplist) {
+ for (i = 0; i < dev->map_count; i++) {
+ map = dev->maplist[i];
+ switch (map->type) {
+ case _DRM_REGISTERS:
+ case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+ if (map->mtrr >= 0) {
+ int retcode;
+ retcode = mtrr_del(map->mtrr,
+ map->offset,
+ map->size);
+ DRM_DEBUG("mtrr_del = %d\n", retcode);
+ }
+#endif
+ drm_ioremapfree(map->handle, map->size);
+ break;
+ case _DRM_SHM:
+ drm_free_pages((unsigned long)map->handle,
+ drm_order(map->size)
+ - PAGE_SHIFT,
+ DRM_MEM_SAREA);
+ break;
+ case _DRM_AGP:
+ break;
+ }
+ drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+ }
+ drm_free(dev->maplist,
+ dev->map_count * sizeof(*dev->maplist),
+ DRM_MEM_MAPS);
+ dev->maplist = NULL;
+ dev->map_count = 0;
+ }
+
+ if (dev->queuelist) {
+ for (i = 0; i < dev->queue_count; i++) {
+ drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+ if (dev->queuelist[i]) {
+ drm_free(dev->queuelist[i],
+ sizeof(*dev->queuelist[0]),
+ DRM_MEM_QUEUES);
+ dev->queuelist[i] = NULL;
+ }
+ }
+ drm_free(dev->queuelist,
+ dev->queue_slots * sizeof(*dev->queuelist),
+ DRM_MEM_QUEUES);
+ dev->queuelist = NULL;
+ }
+
+ drm_dma_takedown(dev);
+
+ dev->queue_count = 0;
+ if (dev->lock.hw_lock) {
+ dev->lock.hw_lock = NULL; /* SHM removed */
+ dev->lock.pid = 0;
+ wake_up_interruptible(&dev->lock.lock_queue);
+ }
+ up(&dev->struct_sem);
+
+ return 0;
+}
+
+/* mga_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+int mga_init(void)
+{
+ int retcode;
+ drm_device_t *dev = &mga_device;
+
+ DRM_DEBUG("\n");
+
+ memset((void *)dev, 0, sizeof(*dev));
+ dev->count_lock = SPIN_LOCK_UNLOCKED;
+ sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+ drm_parse_options(mga);
+#endif
+ DRM_DEBUG("doing misc_register\n");
+ if ((retcode = misc_register(&mga_misc))) {
+ DRM_ERROR("Cannot register \"%s\"\n", MGA_NAME);
+ return retcode;
+ }
+ dev->device = MKDEV(MISC_MAJOR, mga_misc.minor);
+ dev->name = MGA_NAME;
+
+ DRM_DEBUG("doing mem init\n");
+ drm_mem_init();
+ DRM_DEBUG("doing proc init\n");
+ drm_proc_init(dev);
+ DRM_DEBUG("doing agp init\n");
+ dev->agp = drm_agp_init();
+ if(dev->agp == NULL) {
+ DRM_INFO("The mga drm module requires the agpgart module"
+ " to function correctly\nPlease load the agpgart"
+ " module before you load the mga module\n");
+ drm_proc_cleanup();
+ misc_deregister(&mga_misc);
+ mga_takedown(dev);
+ return -ENOMEM;
+ }
+#ifdef CONFIG_MTRR
+ dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+ dev->agp->agp_info.aper_size * 1024 * 1024,
+ MTRR_TYPE_WRCOMB,
+ 1);
+#endif
+ DRM_DEBUG("doing ctxbitmap init\n");
+ if((retcode = drm_ctxbitmap_init(dev))) {
+ DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+ drm_proc_cleanup();
+ misc_deregister(&mga_misc);
+ mga_takedown(dev);
+ return retcode;
+ }
+
+ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+ MGA_NAME,
+ MGA_MAJOR,
+ MGA_MINOR,
+ MGA_PATCHLEVEL,
+ MGA_DATE,
+ mga_misc.minor);
+
+ return 0;
+}
+
+/* mga_cleanup is called via cleanup_module at module unload time. */
+
+void mga_cleanup(void)
+{
+ drm_device_t *dev = &mga_device;
+
+ DRM_DEBUG("\n");
+
+ drm_proc_cleanup();
+ if (misc_deregister(&mga_misc)) {
+ DRM_ERROR("Cannot unload module\n");
+ } else {
+ DRM_INFO("Module unloaded\n");
+ }
+ drm_ctxbitmap_cleanup(dev);
+ mga_dma_cleanup(dev);
+#ifdef CONFIG_MTRR
+ if(dev->agp && dev->agp->agp_mtrr) {
+ int retval;
+ retval = mtrr_del(dev->agp->agp_mtrr,
+ dev->agp->agp_info.aper_base,
+ dev->agp->agp_info.aper_size * 1024*1024);
+ DRM_DEBUG("mtrr_del = %d\n", retval);
+ }
+#endif
+
+ mga_takedown(dev);
+ if (dev->agp) {
+ drm_agp_uninit();
+ drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+ dev->agp = NULL;
+ }
+}
+
+int mga_version(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_version_t version;
+ int len;
+
+ copy_from_user_ret(&version,
+ (drm_version_t *)arg,
+ sizeof(version),
+ -EFAULT);
+
+#define DRM_COPY(name,value) \
+ len = strlen(value); \
+ if (len > name##_len) len = name##_len; \
+ name##_len = strlen(value); \
+ if (len && name) { \
+ copy_to_user_ret(name, value, len, -EFAULT); \
+ }
+
+ version.version_major = MGA_MAJOR;
+ version.version_minor = MGA_MINOR;
+ version.version_patchlevel = MGA_PATCHLEVEL;
+
+ DRM_COPY(version.name, MGA_NAME);
+ DRM_COPY(version.date, MGA_DATE);
+ DRM_COPY(version.desc, MGA_DESC);
+
+ copy_to_user_ret((drm_version_t *)arg,
+ &version,
+ sizeof(version),
+ -EFAULT);
+ return 0;
+}
+
+int mga_open(struct inode *inode, struct file *filp)
+{
+ drm_device_t *dev = &mga_device;
+ int retcode = 0;
+
+ DRM_DEBUG("open_count = %d\n", dev->open_count);
+ if (!(retcode = drm_open_helper(inode, filp, dev))) {
+ MOD_INC_USE_COUNT;
+ atomic_inc(&dev->total_open);
+ spin_lock(&dev->count_lock);
+ if (!dev->open_count++) {
+ spin_unlock(&dev->count_lock);
+ return mga_setup(dev);
+ }
+ spin_unlock(&dev->count_lock);
+ }
+ return retcode;
+}
+
+int mga_release(struct inode *inode, struct file *filp)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ int retcode = 0;
+
+ DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+ current->pid, dev->device, dev->open_count);
+
+ if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+ && dev->lock.pid == current->pid) {
+ mga_reclaim_buffers(dev, priv->pid);
+ DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+ current->pid,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ drm_lock_free(dev,
+ &dev->lock.hw_lock->lock,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+
+ /* FIXME: may require heavy-handed reset of
+ hardware at this point, possibly
+ processed via a callback to the X
+ server. */
+ } else if (dev->lock.hw_lock) {
+ /* The lock is required to reclaim buffers */
+ DECLARE_WAITQUEUE(entry, current);
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ retcode = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ dev->lock.pid = priv->pid;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->total_locks);
+ break; /* Got lock */
+ }
+ /* Contention */
+ atomic_inc(&dev->total_sleeps);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (signal_pending(current)) {
+ retcode = -ERESTARTSYS;
+ break;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ if(!retcode) {
+ mga_reclaim_buffers(dev, priv->pid);
+ drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT);
+ }
+ }
+ drm_fasync(-1, filp, 0);
+
+ down(&dev->struct_sem);
+ if (priv->prev) priv->prev->next = priv->next;
+ else dev->file_first = priv->next;
+ if (priv->next) priv->next->prev = priv->prev;
+ else dev->file_last = priv->prev;
+ up(&dev->struct_sem);
+
+ drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+ MOD_DEC_USE_COUNT;
+ atomic_inc(&dev->total_close);
+ spin_lock(&dev->count_lock);
+ if (!--dev->open_count) {
+ if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+ DRM_ERROR("Device busy: %d %d\n",
+ atomic_read(&dev->ioctl_count),
+ dev->blocked);
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&dev->count_lock);
+ return mga_takedown(dev);
+ }
+ spin_unlock(&dev->count_lock);
+ return retcode;
+}
+
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int nr = DRM_IOCTL_NR(cmd);
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ int retcode = 0;
+ drm_ioctl_desc_t *ioctl;
+ drm_ioctl_t *func;
+
+ atomic_inc(&dev->ioctl_count);
+ atomic_inc(&dev->total_ioctl);
+ ++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);
+
+ if (nr >= MGA_IOCTL_COUNT) {
+ retcode = -EINVAL;
+ } else {
+ ioctl = &mga_ioctls[nr];
+ func = ioctl->func;
+
+ if (!func) {
+ DRM_DEBUG("no function\n");
+ retcode = -EINVAL;
+ } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+ || (ioctl->auth_needed && !priv->authenticated)) {
+ retcode = -EACCES;
+ } else {
+ retcode = (func)(inode, filp, cmd, arg);
+ }
+ }
+
+ atomic_dec(&dev->ioctl_count);
+ return retcode;
+}
+
+int mga_unlock(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_lock_t lock;
+
+ copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+ if (lock.context == DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("Process %d using kernel context %d\n",
+ current->pid, lock.context);
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("%d frees lock (%d holds)\n",
+ lock.context,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ atomic_inc(&dev->total_unlocks);
+ if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+ atomic_inc(&dev->total_contends);
+ drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+ mga_dma_schedule(dev, 1);
+
+ if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ DRM_ERROR("\n");
+ }
+
+ return 0;
+}
diff --git a/drivers/char/drm/mga_drv.h b/drivers/char/drm/mga_drv.h
new file mode 100644
index 000000000..f217acb97
--- /dev/null
+++ b/drivers/char/drm/mga_drv.h
@@ -0,0 +1,447 @@
+/* mga_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRV_H_
+#define _MGA_DRV_H_
+
+#define MGA_BUF_IN_USE 0
+#define MGA_BUF_SWAP_PENDING 1
+#define MGA_BUF_FORCE_FIRE 2
+#define MGA_BUF_NEEDS_OVERFLOW 3
+
+typedef struct {
+ u32 buffer_status;
+ unsigned int num_dwords;
+ unsigned int max_dwords;
+ u32 *current_dma_ptr;
+ u32 *head;
+ u32 phys_head;
+ unsigned int prim_age;
+ int sec_used;
+ int idx;
+} drm_mga_prim_buf_t;
+
+typedef struct _drm_mga_freelist {
+ unsigned int age;
+ drm_buf_t *buf;
+ struct _drm_mga_freelist *next;
+ struct _drm_mga_freelist *prev;
+} drm_mga_freelist_t;
+
+#define MGA_IN_DISPATCH 0
+#define MGA_IN_FLUSH 1
+#define MGA_IN_WAIT 2
+#define MGA_IN_GETBUF 3
+
+typedef struct _drm_mga_private {
+ u32 dispatch_status;
+ unsigned int next_prim_age;
+ __volatile__ unsigned int last_prim_age;
+ int reserved_map_idx;
+ int buffer_map_idx;
+ drm_mga_sarea_t *sarea_priv;
+ int primary_size;
+ int warp_ucode_size;
+ int chipset;
+ unsigned int frontOffset;
+ unsigned int backOffset;
+ unsigned int depthOffset;
+ unsigned int textureOffset;
+ unsigned int textureSize;
+ int cpp;
+ unsigned int stride;
+ int sgram;
+ int use_agp;
+ drm_mga_warp_index_t WarpIndex[MGA_MAX_G400_PIPES];
+ unsigned int WarpPipe;
+ atomic_t pending_bufs;
+ void *status_page;
+ unsigned long real_status_page;
+ u8 *ioremap;
+ drm_mga_prim_buf_t **prim_bufs;
+ drm_mga_prim_buf_t *next_prim;
+ drm_mga_prim_buf_t *last_prim;
+ drm_mga_prim_buf_t *current_prim;
+ int current_prim_idx;
+ drm_mga_freelist_t *head;
+ drm_mga_freelist_t *tail;
+ wait_queue_head_t flush_queue; /* Processes waiting until flush */
+ wait_queue_head_t wait_queue; /* Processes waiting until interrupt */
+ wait_queue_head_t buf_queue; /* Processes waiting for a free buf */
+ /* Some validated register values:
+ */
+ u32 mAccess;
+} drm_mga_private_t;
+
+ /* mga_drv.c */
+extern int mga_init(void);
+extern void mga_cleanup(void);
+extern int mga_version(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_open(struct inode *inode, struct file *filp);
+extern int mga_release(struct inode *inode, struct file *filp);
+extern int mga_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_unlock(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+ /* mga_dma.c */
+extern int mga_dma_schedule(drm_device_t *dev, int locked);
+extern int mga_dma(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_irq_install(drm_device_t *dev, int irq);
+extern int mga_irq_uninstall(drm_device_t *dev);
+extern int mga_control(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_lock(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+/* mga_dma_init does init and release */
+extern int mga_dma_init(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_dma_cleanup(drm_device_t *dev);
+extern int mga_flush_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern void mga_flush_write_combine(void);
+extern unsigned int mga_create_sync_tag(drm_device_t *dev);
+extern drm_buf_t *mga_freelist_get(drm_device_t *dev);
+extern int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf);
+extern int mga_advance_primary(drm_device_t *dev);
+extern void mga_reclaim_buffers(drm_device_t *dev, pid_t pid);
+
+
+ /* mga_bufs.c */
+extern int mga_addbufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_infobufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_markbufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_freebufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_mapbufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_addmap(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+ /* mga_state.c */
+extern int mga_clear_bufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_swap_bufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_iload(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_vertex(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_indices(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+ /* mga_context.c */
+extern int mga_resctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_addctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_modctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_getctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_switchctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_newctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int mga_rmctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+extern int mga_context_switch(drm_device_t *dev, int old, int new);
+extern int mga_context_switch_complete(drm_device_t *dev, int new);
+
+
+typedef enum {
+ TT_GENERAL,
+ TT_BLIT,
+ TT_VECTOR,
+ TT_VERTEX
+} transferType_t;
+
+typedef struct {
+ drm_mga_freelist_t *my_freelist;
+ int discard;
+ int dispatched;
+} drm_mga_buf_priv_t;
+
+#define DWGREG0 0x1c00
+#define DWGREG0_END 0x1dff
+#define DWGREG1 0x2c00
+#define DWGREG1_END 0x2dff
+
+#define ISREG0(r) (r >= DWGREG0 && r <= DWGREG0_END)
+#define ADRINDEX0(r) (u8)((r - DWGREG0) >> 2)
+#define ADRINDEX1(r) (u8)(((r - DWGREG1) >> 2) | 0x80)
+#define ADRINDEX(r) (ISREG0(r) ? ADRINDEX0(r) : ADRINDEX1(r))
+
+#define MGA_VERBOSE 0
+#define MGA_NUM_PRIM_BUFS 8
+
+#define PRIMLOCALS u8 tempIndex[4]; u32 *dma_ptr; u32 phys_head; \
+ int outcount, num_dwords
+
+#define PRIM_OVERFLOW(dev, dev_priv, length) do { \
+ drm_mga_prim_buf_t *tmp_buf = \
+ dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+ if( test_bit(MGA_BUF_NEEDS_OVERFLOW, \
+ &tmp_buf->buffer_status)) { \
+ mga_advance_primary(dev); \
+ mga_dma_schedule(dev, 1); \
+ } else if( tmp_buf->max_dwords - tmp_buf->num_dwords < length ||\
+ tmp_buf->sec_used > MGA_DMA_BUF_NR/2) { \
+ set_bit(MGA_BUF_FORCE_FIRE, &tmp_buf->buffer_status); \
+ mga_advance_primary(dev); \
+ mga_dma_schedule(dev, 1); \
+ } \
+} while(0)
+
+#define PRIMGETPTR(dev_priv) do { \
+ drm_mga_prim_buf_t *tmp_buf = \
+ dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+ if(MGA_VERBOSE) \
+ DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__); \
+ dma_ptr = tmp_buf->current_dma_ptr; \
+ num_dwords = tmp_buf->num_dwords; \
+ phys_head = tmp_buf->phys_head; \
+ outcount = 0; \
+} while(0)
+
+#define PRIMPTR(prim_buf) do { \
+ if(MGA_VERBOSE) \
+ DRM_DEBUG("PRIMPTR in %s\n", __FUNCTION__); \
+ dma_ptr = prim_buf->current_dma_ptr; \
+ num_dwords = prim_buf->num_dwords; \
+ phys_head = prim_buf->phys_head; \
+ outcount = 0; \
+} while(0)
+
+#define PRIMFINISH(prim_buf) do { \
+ if (MGA_VERBOSE) { \
+ DRM_DEBUG( "PRIMFINISH in %s\n", __FUNCTION__); \
+ if (outcount & 3) \
+ DRM_DEBUG(" --- truncation\n"); \
+ } \
+ prim_buf->num_dwords = num_dwords; \
+ prim_buf->current_dma_ptr = dma_ptr; \
+} while(0)
+
+#define PRIMADVANCE(dev_priv) do { \
+drm_mga_prim_buf_t *tmp_buf = \
+ dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+ if (MGA_VERBOSE) { \
+ DRM_DEBUG("PRIMADVANCE in %s\n", __FUNCTION__); \
+ if (outcount & 3) \
+ DRM_DEBUG(" --- truncation\n"); \
+ } \
+ tmp_buf->num_dwords = num_dwords; \
+ tmp_buf->current_dma_ptr = dma_ptr; \
+} while (0)
+
+#define PRIMUPDATE(dev_priv) do { \
+ drm_mga_prim_buf_t *tmp_buf = \
+ dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+ tmp_buf->sec_used++; \
+} while (0)
+
+#define AGEBUF(dev_priv, buf_priv) do { \
+ drm_mga_prim_buf_t *tmp_buf = \
+ dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+ buf_priv->my_freelist->age = tmp_buf->prim_age; \
+} while (0)
+
+
+#define PRIMOUTREG(reg, val) do { \
+ tempIndex[outcount]=ADRINDEX(reg); \
+ dma_ptr[1+outcount] = val; \
+ if (MGA_VERBOSE) \
+ DRM_DEBUG(" PRIMOUT %d: 0x%x -- 0x%x\n", \
+ num_dwords + 1 + outcount, ADRINDEX(reg), val); \
+ if( ++outcount == 4) { \
+ outcount = 0; \
+ dma_ptr[0] = *(u32 *)tempIndex; \
+ dma_ptr+=5; \
+ num_dwords += 5; \
+ } \
+}while (0)
+
+/* A reduced set of the mga registers.
+ */
+
+#define MGAREG_MGA_EXEC 0x0100
+#define MGAREG_ALPHACTRL 0x2c7c
+#define MGAREG_AR0 0x1c60
+#define MGAREG_AR1 0x1c64
+#define MGAREG_AR2 0x1c68
+#define MGAREG_AR3 0x1c6c
+#define MGAREG_AR4 0x1c70
+#define MGAREG_AR5 0x1c74
+#define MGAREG_AR6 0x1c78
+#define MGAREG_CXBNDRY 0x1c80
+#define MGAREG_CXLEFT 0x1ca0
+#define MGAREG_CXRIGHT 0x1ca4
+#define MGAREG_DMAPAD 0x1c54
+#define MGAREG_DSTORG 0x2cb8
+#define MGAREG_DWGCTL 0x1c00
+#define MGAREG_DWGSYNC 0x2c4c
+#define MGAREG_FCOL 0x1c24
+#define MGAREG_FIFOSTATUS 0x1e10
+#define MGAREG_FOGCOL 0x1cf4
+#define MGAREG_FXBNDRY 0x1c84
+#define MGAREG_FXLEFT 0x1ca8
+#define MGAREG_FXRIGHT 0x1cac
+#define MGAREG_ICLEAR 0x1e18
+#define MGAREG_IEN 0x1e1c
+#define MGAREG_LEN 0x1c5c
+#define MGAREG_MACCESS 0x1c04
+#define MGAREG_PITCH 0x1c8c
+#define MGAREG_PLNWT 0x1c1c
+#define MGAREG_PRIMADDRESS 0x1e58
+#define MGAREG_PRIMEND 0x1e5c
+#define MGAREG_PRIMPTR 0x1e50
+#define MGAREG_SECADDRESS 0x2c40
+#define MGAREG_SECEND 0x2c44
+#define MGAREG_SETUPADDRESS 0x2cd0
+#define MGAREG_SETUPEND 0x2cd4
+#define MGAREG_SOFTRAP 0x2c48
+#define MGAREG_SRCORG 0x2cb4
+#define MGAREG_STATUS 0x1e14
+#define MGAREG_STENCIL 0x2cc8
+#define MGAREG_STENCILCTL 0x2ccc
+#define MGAREG_TDUALSTAGE0 0x2cf8
+#define MGAREG_TDUALSTAGE1 0x2cfc
+#define MGAREG_TEXBORDERCOL 0x2c5c
+#define MGAREG_TEXCTL 0x2c30
+#define MGAREG_TEXCTL2 0x2c3c
+#define MGAREG_TEXFILTER 0x2c58
+#define MGAREG_TEXHEIGHT 0x2c2c
+#define MGAREG_TEXORG 0x2c24
+#define MGAREG_TEXORG1 0x2ca4
+#define MGAREG_TEXORG2 0x2ca8
+#define MGAREG_TEXORG3 0x2cac
+#define MGAREG_TEXORG4 0x2cb0
+#define MGAREG_TEXTRANS 0x2c34
+#define MGAREG_TEXTRANSHIGH 0x2c38
+#define MGAREG_TEXWIDTH 0x2c28
+#define MGAREG_WACCEPTSEQ 0x1dd4
+#define MGAREG_WCODEADDR 0x1e6c
+#define MGAREG_WFLAG 0x1dc4
+#define MGAREG_WFLAG1 0x1de0
+#define MGAREG_WFLAGNB 0x1e64
+#define MGAREG_WFLAGNB1 0x1e08
+#define MGAREG_WGETMSB 0x1dc8
+#define MGAREG_WIADDR 0x1dc0
+#define MGAREG_WIADDR2 0x1dd8
+#define MGAREG_WMISC 0x1e70
+#define MGAREG_WVRTXSZ 0x1dcc
+#define MGAREG_YBOT 0x1c9c
+#define MGAREG_YDST 0x1c90
+#define MGAREG_YDSTLEN 0x1c88
+#define MGAREG_YDSTORG 0x1c94
+#define MGAREG_YTOP 0x1c98
+#define MGAREG_ZORG 0x1c0c
+
+#define PDEA_pagpxfer_enable 0x2
+
+#define WIA_wmode_suspend 0x0
+#define WIA_wmode_start 0x3
+#define WIA_wagp_agp 0x4
+
+#define DC_opcod_line_open 0x0
+#define DC_opcod_autoline_open 0x1
+#define DC_opcod_line_close 0x2
+#define DC_opcod_autoline_close 0x3
+#define DC_opcod_trap 0x4
+#define DC_opcod_texture_trap 0x6
+#define DC_opcod_bitblt 0x8
+#define DC_opcod_iload 0x9
+#define DC_atype_rpl 0x0
+#define DC_atype_rstr 0x10
+#define DC_atype_zi 0x30
+#define DC_atype_blk 0x40
+#define DC_atype_i 0x70
+#define DC_linear_xy 0x0
+#define DC_linear_linear 0x80
+#define DC_zmode_nozcmp 0x0
+#define DC_zmode_ze 0x200
+#define DC_zmode_zne 0x300
+#define DC_zmode_zlt 0x400
+#define DC_zmode_zlte 0x500
+#define DC_zmode_zgt 0x600
+#define DC_zmode_zgte 0x700
+#define DC_solid_disable 0x0
+#define DC_solid_enable 0x800
+#define DC_arzero_disable 0x0
+#define DC_arzero_enable 0x1000
+#define DC_sgnzero_disable 0x0
+#define DC_sgnzero_enable 0x2000
+#define DC_shftzero_disable 0x0
+#define DC_shftzero_enable 0x4000
+#define DC_bop_SHIFT 16
+#define DC_trans_SHIFT 20
+#define DC_bltmod_bmonolef 0x0
+#define DC_bltmod_bmonowf 0x8000000
+#define DC_bltmod_bplan 0x2000000
+#define DC_bltmod_bfcol 0x4000000
+#define DC_bltmod_bu32bgr 0x6000000
+#define DC_bltmod_bu32rgb 0xe000000
+#define DC_bltmod_bu24bgr 0x16000000
+#define DC_bltmod_bu24rgb 0x1e000000
+#define DC_pattern_disable 0x0
+#define DC_pattern_enable 0x20000000
+#define DC_transc_disable 0x0
+#define DC_transc_enable 0x40000000
+#define DC_clipdis_disable 0x0
+#define DC_clipdis_enable 0x80000000
+
+#define SETADD_mode_vertlist 0x0
+
+
+#define MGA_CLEAR_CMD (DC_opcod_trap | DC_arzero_enable | \
+ DC_sgnzero_enable | DC_shftzero_enable | \
+ (0xC << DC_bop_SHIFT) | DC_clipdis_enable | \
+ DC_solid_enable | DC_transc_enable)
+
+
+#define MGA_COPY_CMD (DC_opcod_bitblt | DC_atype_rpl | DC_linear_xy | \
+ DC_solid_disable | DC_arzero_disable | \
+ DC_sgnzero_enable | DC_shftzero_enable | \
+ (0xC << DC_bop_SHIFT) | DC_bltmod_bfcol | \
+ DC_pattern_disable | DC_transc_disable | \
+ DC_clipdis_enable) \
+
+#define MGA_FLUSH_CMD (DC_opcod_texture_trap | (0xF << DC_trans_SHIFT) |\
+ DC_arzero_enable | DC_sgnzero_enable | \
+ DC_atype_i)
+
+#endif
diff --git a/drivers/char/drm/mga_state.c b/drivers/char/drm/mga_state.c
new file mode 100644
index 000000000..723ccc539
--- /dev/null
+++ b/drivers/char/drm/mga_state.c
@@ -0,0 +1,1067 @@
+/* mga_state.c -- State support for mga g200/g400 -*- linux-c -*-
+ * Created: Thu Jan 27 02:53:43 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Jeff Hartmann <jhartmann@valinux.com>
+ * Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "drm.h"
+
+static void mgaEmitClipRect(drm_mga_private_t * dev_priv,
+ drm_clip_rect_t * box)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->ContextState;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ /* This takes 10 dwords */
+ PRIMGETPTR(dev_priv);
+
+ /* Force reset of dwgctl (eliminates clip disable) */
+#if 0
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DWGSYNC, 0);
+ PRIMOUTREG(MGAREG_DWGSYNC, 0);
+ PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+#else
+ PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+ PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+ PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+ PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+#endif
+
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_CXBNDRY, ((box->x2) << 16) | (box->x1));
+ PRIMOUTREG(MGAREG_YTOP, box->y1 * dev_priv->stride / 2);
+ PRIMOUTREG(MGAREG_YBOT, box->y2 * dev_priv->stride / 2);
+
+ PRIMADVANCE(dev_priv);
+}
+
+static void mgaEmitContext(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->ContextState;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ /* This takes a max of 15 dwords */
+ PRIMGETPTR(dev_priv);
+
+ PRIMOUTREG(MGAREG_DSTORG, regs[MGA_CTXREG_DSTORG]);
+ PRIMOUTREG(MGAREG_MACCESS, regs[MGA_CTXREG_MACCESS]);
+ PRIMOUTREG(MGAREG_PLNWT, regs[MGA_CTXREG_PLNWT]);
+ PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+ PRIMOUTREG(MGAREG_ALPHACTRL, regs[MGA_CTXREG_ALPHACTRL]);
+ PRIMOUTREG(MGAREG_FOGCOL, regs[MGA_CTXREG_FOGCOLOR]);
+ PRIMOUTREG(MGAREG_WFLAG, regs[MGA_CTXREG_WFLAG]);
+ PRIMOUTREG(MGAREG_ZORG, dev_priv->depthOffset); /* invarient */
+
+ if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+ PRIMOUTREG(MGAREG_WFLAG1, regs[MGA_CTXREG_WFLAG]);
+ PRIMOUTREG(MGAREG_TDUALSTAGE0, regs[MGA_CTXREG_TDUAL0]);
+ PRIMOUTREG(MGAREG_TDUALSTAGE1, regs[MGA_CTXREG_TDUAL1]);
+ PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+ } else {
+ PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ }
+
+ PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitTex(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->TexState[0];
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ PRIMGETPTR(dev_priv);
+
+ /* This takes 20 dwords */
+
+ PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2]);
+ PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+ PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+ PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+ PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+ PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+ PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+ PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+ PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+ PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+ PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+ PRIMOUTREG(0x2d00 + 24 * 4, regs[MGA_TEXREG_WIDTH]);
+
+ PRIMOUTREG(0x2d00 + 34 * 4, regs[MGA_TEXREG_HEIGHT]);
+ PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+ PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+ PRIMADVANCE(dev_priv);
+}
+
+static void mgaG400EmitTex0(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->TexState[0];
+ int multitex = sarea_priv->WarpPipe & MGA_T2;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ PRIMGETPTR(dev_priv);
+
+ /* This takes a max of 30 dwords */
+
+ PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+ PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+ PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+ PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+ PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+ PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+ PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+ PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+ PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+ PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+ PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+ PRIMOUTREG(0x2d00 + 49 * 4, 0);
+
+ PRIMOUTREG(0x2d00 + 57 * 4, 0);
+ PRIMOUTREG(0x2d00 + 53 * 4, 0);
+ PRIMOUTREG(0x2d00 + 61 * 4, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+ if (!multitex) {
+ PRIMOUTREG(0x2d00 + 52 * 4, 0x40);
+ PRIMOUTREG(0x2d00 + 60 * 4, 0x40);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ }
+
+ PRIMOUTREG(0x2d00 + 54 * 4, regs[MGA_TEXREG_WIDTH] | 0x40);
+ PRIMOUTREG(0x2d00 + 62 * 4, regs[MGA_TEXREG_HEIGHT] | 0x40);
+ PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+ PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+
+ PRIMADVANCE(dev_priv);
+}
+
+#define TMC_map1_enable 0x80000000
+
+static void mgaG400EmitTex1(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->TexState[1];
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ PRIMGETPTR(dev_priv);
+
+ /* This takes 25 dwords */
+
+ PRIMOUTREG(MGAREG_TEXCTL2,
+ regs[MGA_TEXREG_CTL2] | TMC_map1_enable | 0x00008000);
+ PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+ PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+ PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+ PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+ PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+ PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+ PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+ PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+ PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+ PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+ PRIMOUTREG(0x2d00 + 49 * 4, 0);
+
+ PRIMOUTREG(0x2d00 + 57 * 4, 0);
+ PRIMOUTREG(0x2d00 + 53 * 4, 0);
+ PRIMOUTREG(0x2d00 + 61 * 4, 0);
+ PRIMOUTREG(0x2d00 + 52 * 4, regs[MGA_TEXREG_WIDTH] | 0x40);
+
+ PRIMOUTREG(0x2d00 + 60 * 4, regs[MGA_TEXREG_HEIGHT] | 0x40);
+ PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+ PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+ PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+
+ PRIMADVANCE(dev_priv);
+}
+
+#define EMIT_PIPE 50
+static void mgaG400EmitPipe(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int pipe = sarea_priv->WarpPipe;
+ float fParam = 12800.0f;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ PRIMGETPTR(dev_priv);
+
+ /* This takes 50 dwords */
+
+ /* Establish vertex size.
+ */
+ PRIMOUTREG(MGAREG_WIADDR2, WIA_wmode_suspend);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+ if (pipe & MGA_T2) {
+ PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001e09);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x1e000000);
+ } else {
+ if (dev_priv->WarpPipe & MGA_T2) {
+ /* Flush the WARP pipe */
+ PRIMOUTREG(MGAREG_YDST, 0);
+ PRIMOUTREG(MGAREG_FXLEFT, 0);
+ PRIMOUTREG(MGAREG_FXRIGHT, 1);
+ PRIMOUTREG(MGAREG_DWGCTL, MGA_FLUSH_CMD);
+
+ PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 1);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+ PRIMOUTREG(MGAREG_TEXCTL2, 0 | 0x00008000);
+ PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+ PRIMOUTREG(MGAREG_TEXCTL2, 0x80 | 0x00008000);
+ PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+ }
+
+ PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001807);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+ PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x18000000);
+ }
+
+ PRIMOUTREG(MGAREG_WFLAG, 0);
+ PRIMOUTREG(MGAREG_WFLAG1, 0);
+ PRIMOUTREG(0x2d00 + 56 * 4, *((u32 *) (&fParam)));
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+ PRIMOUTREG(0x2d00 + 49 * 4, 0); /* Tex stage 0 */
+ PRIMOUTREG(0x2d00 + 57 * 4, 0); /* Tex stage 0 */
+ PRIMOUTREG(0x2d00 + 53 * 4, 0); /* Tex stage 1 */
+ PRIMOUTREG(0x2d00 + 61 * 4, 0); /* Tex stage 1 */
+
+ PRIMOUTREG(0x2d00 + 54 * 4, 0x40); /* Tex stage 0 : w */
+ PRIMOUTREG(0x2d00 + 62 * 4, 0x40); /* Tex stage 0 : h */
+ PRIMOUTREG(0x2d00 + 52 * 4, 0x40); /* Tex stage 1 : w */
+ PRIMOUTREG(0x2d00 + 60 * 4, 0x40); /* Tex stage 1 : h */
+
+ /* Dma pading required due to hw bug */
+ PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+ PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+ PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+ PRIMOUTREG(MGAREG_WIADDR2,
+ (u32) (dev_priv->WarpIndex[pipe].
+ phys_addr | WIA_wmode_start | WIA_wagp_agp));
+ PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitPipe(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int pipe = sarea_priv->WarpPipe;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ PRIMGETPTR(dev_priv);
+
+ /* This takes 15 dwords */
+
+ PRIMOUTREG(MGAREG_WIADDR, WIA_wmode_suspend);
+ PRIMOUTREG(MGAREG_WVRTXSZ, 7);
+ PRIMOUTREG(MGAREG_WFLAG, 0);
+ PRIMOUTREG(0x2d00 + 24 * 4, 0); /* tex w/h */
+
+ PRIMOUTREG(0x2d00 + 25 * 4, 0x100);
+ PRIMOUTREG(0x2d00 + 34 * 4, 0); /* tex w/h */
+ PRIMOUTREG(0x2d00 + 42 * 4, 0xFFFF);
+ PRIMOUTREG(0x2d00 + 60 * 4, 0xFFFF);
+
+ /* Dma pading required due to hw bug */
+ PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+ PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+ PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+ PRIMOUTREG(MGAREG_WIADDR,
+ (u32) (dev_priv->WarpIndex[pipe].
+ phys_addr | WIA_wmode_start | WIA_wagp_agp));
+
+ PRIMADVANCE( dev_priv );
+}
+
+static void mgaEmitState(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int dirty = sarea_priv->dirty;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+ int multitex = sarea_priv->WarpPipe & MGA_T2;
+
+ if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+ mgaG400EmitPipe(dev_priv);
+ dev_priv->WarpPipe = sarea_priv->WarpPipe;
+ }
+
+ if (dirty & MGA_UPLOAD_CTX) {
+ mgaEmitContext(dev_priv);
+ sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+ }
+
+ if (dirty & MGA_UPLOAD_TEX0) {
+ mgaG400EmitTex0(dev_priv);
+ sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+ }
+
+ if ((dirty & MGA_UPLOAD_TEX1) && multitex) {
+ mgaG400EmitTex1(dev_priv);
+ sarea_priv->dirty &= ~MGA_UPLOAD_TEX1;
+ }
+ } else {
+ if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+ mgaG200EmitPipe(dev_priv);
+ dev_priv->WarpPipe = sarea_priv->WarpPipe;
+ }
+
+ if (dirty & MGA_UPLOAD_CTX) {
+ mgaEmitContext(dev_priv);
+ sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+ }
+
+ if (dirty & MGA_UPLOAD_TEX0) {
+ mgaG200EmitTex(dev_priv);
+ sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+ }
+ }
+}
+
+
+/* Disallow all write destinations except the front and backbuffer.
+ */
+static int mgaVerifyContext(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->ContextState;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (regs[MGA_CTXREG_DSTORG] != dev_priv->frontOffset &&
+ regs[MGA_CTXREG_DSTORG] != dev_priv->backOffset) {
+ DRM_DEBUG("BAD DSTORG: %x (front %x, back %x)\n\n",
+ regs[MGA_CTXREG_DSTORG], dev_priv->frontOffset,
+ dev_priv->backOffset);
+ regs[MGA_CTXREG_DSTORG] = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Disallow texture reads from PCI space.
+ */
+static int mgaVerifyTex(drm_mga_private_t * dev_priv, int unit)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if ((sarea_priv->TexState[unit][MGA_TEXREG_ORG] & 0x3) == 0x1) {
+ DRM_DEBUG("BAD TEXREG_ORG: %x, unit %d\n",
+ sarea_priv->TexState[unit][MGA_TEXREG_ORG],
+ unit);
+ sarea_priv->TexState[unit][MGA_TEXREG_ORG] = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mgaVerifyState(drm_mga_private_t * dev_priv)
+{
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int dirty = sarea_priv->dirty;
+ int rv = 0;
+
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+ sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+ if (dirty & MGA_UPLOAD_CTX)
+ rv |= mgaVerifyContext(dev_priv);
+
+ if (dirty & MGA_UPLOAD_TEX0)
+ rv |= mgaVerifyTex(dev_priv, 0);
+
+ if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+ if (dirty & MGA_UPLOAD_TEX1)
+ rv |= mgaVerifyTex(dev_priv, 1);
+
+ if (dirty & MGA_UPLOAD_PIPE)
+ rv |= (sarea_priv->WarpPipe > MGA_MAX_G400_PIPES);
+ } else {
+ if (dirty & MGA_UPLOAD_PIPE)
+ rv |= (sarea_priv->WarpPipe > MGA_MAX_G200_PIPES);
+ }
+
+ return rv == 0;
+}
+
+static int mgaVerifyIload(drm_mga_private_t * dev_priv,
+ unsigned long bus_address,
+ unsigned int dstOrg, int length)
+{
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (dstOrg < dev_priv->textureOffset ||
+ dstOrg + length >
+ (dev_priv->textureOffset + dev_priv->textureSize)) {
+ return -EINVAL;
+ }
+ if (length % 64) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* This copies a 64 byte aligned agp region to the frambuffer
+ * with a standard blit, the ioctl needs to do checking */
+
+static void mga_dma_dispatch_tex_blit(drm_device_t * dev,
+ unsigned long bus_address,
+ int length, unsigned int destOrg)
+{
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ int use_agp = PDEA_pagpxfer_enable | 0x00000001;
+ u16 y2;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ y2 = length / 64;
+
+ PRIM_OVERFLOW(dev, dev_priv, 30);
+ PRIMGETPTR(dev_priv);
+
+ PRIMOUTREG(MGAREG_DSTORG, destOrg);
+ PRIMOUTREG(MGAREG_MACCESS, 0x00000000);
+ DRM_DEBUG("srcorg : %lx\n", bus_address | use_agp);
+ PRIMOUTREG(MGAREG_SRCORG, (u32) bus_address | use_agp);
+ PRIMOUTREG(MGAREG_AR5, 64);
+
+ PRIMOUTREG(MGAREG_PITCH, 64);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+ PRIMOUTREG(MGAREG_AR0, 63);
+ PRIMOUTREG(MGAREG_AR3, 0);
+ PRIMOUTREG(MGAREG_FXBNDRY, (63 << 16));
+ PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, y2);
+
+ PRIMOUTREG(MGAREG_SRCORG, 0);
+ PRIMOUTREG(MGAREG_PITCH, dev_priv->stride / dev_priv->cpp);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_vertex(drm_device_t * dev, drm_buf_t * buf)
+{
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned long address = (unsigned long) buf->bus_address;
+ int length = buf->used;
+ int use_agp = PDEA_pagpxfer_enable;
+ int i = 0;
+ int primary_needed;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ DRM_DEBUG("dispatch vertex %d addr 0x%lx, "
+ "length 0x%x nbox %d dirty %x\n",
+ buf->idx, address, length,
+ sarea_priv->nbox, sarea_priv->dirty);
+
+ DRM_DEBUG("used : %d, total : %d\n", buf->used, buf->total);
+
+ if (buf->used) {
+ /* WARNING: if you change any of the state functions verify
+ * these numbers (Overestimating this doesn't hurt).
+ */
+ buf_priv->dispatched = 1;
+ primary_needed = (50 + 15 + 15 + 30 + 25 +
+ 10 + 15 * MGA_NR_SAREA_CLIPRECTS);
+ PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+ mgaEmitState(dev_priv);
+ do {
+ if (i < sarea_priv->nbox) {
+ DRM_DEBUG("idx %d Emit box %d/%d:"
+ "%d,%d - %d,%d\n",
+ buf->idx,
+ i, sarea_priv->nbox,
+ sarea_priv->boxes[i].x1,
+ sarea_priv->boxes[i].y1,
+ sarea_priv->boxes[i].x2,
+ sarea_priv->boxes[i].y2);
+
+ mgaEmitClipRect(dev_priv,
+ &sarea_priv->boxes[i]);
+ }
+
+ PRIMGETPTR(dev_priv);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_SECADDRESS,
+ ((u32) address) | TT_VERTEX);
+ PRIMOUTREG(MGAREG_SECEND,
+ (((u32) (address + length)) | use_agp));
+ PRIMADVANCE(dev_priv);
+ } while (++i < sarea_priv->nbox);
+ }
+ if (buf_priv->discard) {
+ if (buf_priv->dispatched == 1)
+ AGEBUF(dev_priv, buf_priv);
+ buf_priv->dispatched = 0;
+ mga_freelist_put(dev, buf);
+ }
+
+
+}
+
+
+static void mga_dma_dispatch_indices(drm_device_t * dev,
+ drm_buf_t * buf,
+ unsigned int start, unsigned int end)
+{
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int address = (unsigned int) buf->bus_address;
+ int use_agp = PDEA_pagpxfer_enable;
+ int i = 0;
+ int primary_needed;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ DRM_DEBUG("dispatch indices %d addr 0x%x, "
+ "start 0x%x end 0x%x nbox %d dirty %x\n",
+ buf->idx, address, start, end,
+ sarea_priv->nbox, sarea_priv->dirty);
+
+ if (start != end) {
+ /* WARNING: if you change any of the state functions verify
+ * these numbers (Overestimating this doesn't hurt).
+ */
+ buf_priv->dispatched = 1;
+ primary_needed = (50 + 15 + 15 + 30 + 25 +
+ 10 + 15 * MGA_NR_SAREA_CLIPRECTS);
+ PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+ mgaEmitState(dev_priv);
+
+ do {
+ if (i < sarea_priv->nbox) {
+ DRM_DEBUG("idx %d Emit box %d/%d:"
+ "%d,%d - %d,%d\n",
+ buf->idx,
+ i, sarea_priv->nbox,
+ sarea_priv->boxes[i].x1,
+ sarea_priv->boxes[i].y1,
+ sarea_priv->boxes[i].x2,
+ sarea_priv->boxes[i].y2);
+
+ mgaEmitClipRect(dev_priv,
+ &sarea_priv->boxes[i]);
+ }
+
+ PRIMGETPTR(dev_priv);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_SETUPADDRESS,
+ ((address + start) |
+ SETADD_mode_vertlist));
+ PRIMOUTREG(MGAREG_SETUPEND,
+ ((address + end) | use_agp));
+ PRIMADVANCE(dev_priv);
+ } while (++i < sarea_priv->nbox);
+ }
+ if (buf_priv->discard) {
+ if (buf_priv->dispatched == 1)
+ AGEBUF(dev_priv, buf_priv);
+ buf_priv->dispatched = 0;
+ mga_freelist_put(dev, buf);
+ }
+}
+
+
+static void mga_dma_dispatch_clear(drm_device_t * dev, int flags,
+ unsigned int clear_color,
+ unsigned int clear_zval)
+{
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->ContextState;
+ int nbox = sarea_priv->nbox;
+ drm_clip_rect_t *pbox = sarea_priv->boxes;
+ unsigned int cmd;
+ int i;
+ int primary_needed;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (dev_priv->sgram)
+ cmd = MGA_CLEAR_CMD | DC_atype_blk;
+ else
+ cmd = MGA_CLEAR_CMD | DC_atype_rstr;
+
+ primary_needed = nbox * 70;
+ if (primary_needed == 0)
+ primary_needed = 70;
+ PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+ PRIMGETPTR(dev_priv);
+
+ for (i = 0; i < nbox; i++) {
+ unsigned int height = pbox[i].y2 - pbox[i].y1;
+
+ DRM_DEBUG("dispatch clear %d,%d-%d,%d flags %x!\n",
+ pbox[i].x1, pbox[i].y1, pbox[i].x2,
+ pbox[i].y2, flags);
+
+ if (flags & MGA_FRONT) {
+ DRM_DEBUG("clear front\n");
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_YDSTLEN,
+ (pbox[i].y1 << 16) | height);
+ PRIMOUTREG(MGAREG_FXBNDRY,
+ (pbox[i].x2 << 16) | pbox[i].x1);
+
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_FCOL, clear_color);
+ PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+ PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+ }
+
+ if (flags & MGA_BACK) {
+ DRM_DEBUG("clear back\n");
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_YDSTLEN,
+ (pbox[i].y1 << 16) | height);
+ PRIMOUTREG(MGAREG_FXBNDRY,
+ (pbox[i].x2 << 16) | pbox[i].x1);
+
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_FCOL, clear_color);
+ PRIMOUTREG(MGAREG_DSTORG, dev_priv->backOffset);
+ PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+ }
+
+ if (flags & MGA_DEPTH) {
+ DRM_DEBUG("clear depth\n");
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_YDSTLEN,
+ (pbox[i].y1 << 16) | height);
+ PRIMOUTREG(MGAREG_FXBNDRY,
+ (pbox[i].x2 << 16) | pbox[i].x1);
+
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_FCOL, clear_zval);
+ PRIMOUTREG(MGAREG_DSTORG, dev_priv->depthOffset);
+ PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+ }
+ }
+
+ /* Force reset of DWGCTL */
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+ PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_swap(drm_device_t * dev)
+{
+ drm_mga_private_t *dev_priv = dev->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ unsigned int *regs = sarea_priv->ContextState;
+ int nbox = sarea_priv->nbox;
+ drm_clip_rect_t *pbox = sarea_priv->boxes;
+ int i;
+ int primary_needed;
+ PRIMLOCALS;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ primary_needed = nbox * 5;
+ primary_needed += 65;
+ PRIM_OVERFLOW(dev, dev_priv, primary_needed);
+ PRIMGETPTR(dev_priv);
+
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DWGSYNC, 0x7100);
+ PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+
+ PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+ PRIMOUTREG(MGAREG_MACCESS, dev_priv->mAccess);
+ PRIMOUTREG(MGAREG_SRCORG, dev_priv->backOffset);
+ PRIMOUTREG(MGAREG_AR5, dev_priv->stride / 2);
+
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+ for (i = 0; i < nbox; i++) {
+ unsigned int h = pbox[i].y2 - pbox[i].y1;
+ unsigned int start = pbox[i].y1 * dev_priv->stride / 2;
+
+ DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n",
+ pbox[i].x1, pbox[i].y1, pbox[i].x2, pbox[i].y2);
+
+ PRIMOUTREG(MGAREG_AR0, start + pbox[i].x2 - 1);
+ PRIMOUTREG(MGAREG_AR3, start + pbox[i].x1);
+ PRIMOUTREG(MGAREG_FXBNDRY,
+ pbox[i].x1 | ((pbox[i].x2 - 1) << 16));
+ PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC,
+ (pbox[i].y1 << 16) | h);
+ }
+
+ /* Force reset of DWGCTL */
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_DMAPAD, 0);
+ PRIMOUTREG(MGAREG_SRCORG, 0);
+ PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+ PRIMADVANCE(dev_priv);
+}
+
+int mga_clear_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_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ drm_mga_clear_t clear;
+
+ copy_from_user_ret(&clear, (drm_mga_clear_t *) arg, sizeof(clear),
+ -EFAULT);
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("mga_clear_bufs called without lock held\n");
+ return -EINVAL;
+ }
+
+ if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+ sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+ /* Make sure we restore the 3D state next time.
+ */
+ dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+ mga_dma_dispatch_clear(dev, clear.flags,
+ clear.clear_color, clear.clear_depth);
+ PRIMUPDATE(dev_priv);
+ mga_flush_write_combine();
+ mga_dma_schedule(dev, 1);
+ return 0;
+}
+
+int mga_swap_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_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("mga_swap_bufs called without lock held\n");
+ return -EINVAL;
+ }
+
+ if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+ sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+ /* Make sure we restore the 3D state next time.
+ */
+ dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+ mga_dma_dispatch_swap(dev);
+ PRIMUPDATE(dev_priv);
+ set_bit(MGA_BUF_SWAP_PENDING,
+ &dev_priv->current_prim->buffer_status);
+ mga_flush_write_combine();
+ mga_dma_schedule(dev, 1);
+ return 0;
+}
+
+int mga_iload(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_device_dma_t *dma = dev->dma;
+ drm_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ drm_buf_t *buf;
+ drm_mga_buf_priv_t *buf_priv;
+ drm_mga_iload_t iload;
+ unsigned long bus_address;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ DRM_DEBUG("Starting Iload\n");
+ copy_from_user_ret(&iload, (drm_mga_iload_t *) arg, sizeof(iload),
+ -EFAULT);
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("mga_iload called without lock held\n");
+ return -EINVAL;
+ }
+
+ buf = dma->buflist[iload.idx];
+ buf_priv = buf->dev_private;
+ bus_address = buf->bus_address;
+ DRM_DEBUG("bus_address %lx, length %d, destorg : %x\n",
+ bus_address, iload.length, iload.destOrg);
+
+ if (mgaVerifyIload(dev_priv,
+ bus_address, iload.destOrg, iload.length)) {
+ mga_freelist_put(dev, buf);
+ return -EINVAL;
+ }
+
+ sarea_priv->dirty |= MGA_UPLOAD_CTX;
+
+ mga_dma_dispatch_tex_blit(dev, bus_address, iload.length,
+ iload.destOrg);
+ AGEBUF(dev_priv, buf_priv);
+ buf_priv->discard = 1;
+ mga_freelist_put(dev, buf);
+ mga_flush_write_combine();
+ mga_dma_schedule(dev, 1);
+ return 0;
+}
+
+int mga_vertex(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_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+ drm_buf_t *buf;
+ drm_mga_buf_priv_t *buf_priv;
+ drm_mga_vertex_t vertex;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ copy_from_user_ret(&vertex, (drm_mga_vertex_t *) arg,
+ sizeof(vertex), -EFAULT);
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("mga_vertex called without lock held\n");
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("mga_vertex\n");
+
+ buf = dma->buflist[vertex.idx];
+ buf_priv = buf->dev_private;
+
+ buf->used = vertex.used;
+ buf_priv->discard = vertex.discard;
+
+ if (!mgaVerifyState(dev_priv)) {
+ if (vertex.discard) {
+ if (buf_priv->dispatched == 1)
+ AGEBUF(dev_priv, buf_priv);
+ buf_priv->dispatched = 0;
+ mga_freelist_put(dev, buf);
+ }
+ DRM_DEBUG("bad state\n");
+ return -EINVAL;
+ }
+
+ mga_dma_dispatch_vertex(dev, buf);
+
+ PRIMUPDATE(dev_priv);
+ mga_flush_write_combine();
+ mga_dma_schedule(dev, 1);
+ return 0;
+}
+
+
+int mga_indices(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_mga_private_t *dev_priv =
+ (drm_mga_private_t *) dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+ drm_buf_t *buf;
+ drm_mga_buf_priv_t *buf_priv;
+ drm_mga_indices_t indices;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ copy_from_user_ret(&indices, (drm_mga_indices_t *) arg,
+ sizeof(indices), -EFAULT);
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("mga_indices called without lock held\n");
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("mga_indices\n");
+
+ buf = dma->buflist[indices.idx];
+ buf_priv = buf->dev_private;
+
+ buf_priv->discard = indices.discard;
+
+ if (!mgaVerifyState(dev_priv)) {
+ if (indices.discard) {
+ if (buf_priv->dispatched == 1)
+ AGEBUF(dev_priv, buf_priv);
+ buf_priv->dispatched = 0;
+ mga_freelist_put(dev, buf);
+ }
+ return -EINVAL;
+ }
+
+ mga_dma_dispatch_indices(dev, buf, indices.start, indices.end);
+
+ PRIMUPDATE(dev_priv);
+ mga_flush_write_combine();
+ mga_dma_schedule(dev, 1);
+ return 0;
+}
+
+
+
+static int mga_dma_get_buffers(drm_device_t * dev, drm_dma_t * d)
+{
+ int i;
+ drm_buf_t *buf;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ for (i = d->granted_count; i < d->request_count; i++) {
+ buf = mga_freelist_get(dev);
+ if (!buf)
+ break;
+ buf->pid = current->pid;
+ copy_to_user_ret(&d->request_indices[i],
+ &buf->idx, sizeof(buf->idx), -EFAULT);
+ copy_to_user_ret(&d->request_sizes[i],
+ &buf->total, sizeof(buf->total), -EFAULT);
+ ++d->granted_count;
+ }
+ return 0;
+}
+
+int mga_dma(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_device_dma_t *dma = dev->dma;
+ int retcode = 0;
+ drm_dma_t d;
+ DRM_DEBUG("%s\n", __FUNCTION__);
+
+ copy_from_user_ret(&d, (drm_dma_t *) arg, sizeof(d), -EFAULT);
+ DRM_DEBUG("%d %d: %d send, %d req\n",
+ current->pid, d.context, d.send_count, d.request_count);
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("mga_dma called without lock held\n");
+ return -EINVAL;
+ }
+
+ /* Please don't send us buffers.
+ */
+ if (d.send_count != 0) {
+ DRM_ERROR
+ ("Process %d trying to send %d buffers via drmDMA\n",
+ current->pid, d.send_count);
+ return -EINVAL;
+ }
+
+ /* We'll send you buffers.
+ */
+ if (d.request_count < 0 || d.request_count > dma->buf_count) {
+ DRM_ERROR
+ ("Process %d trying to get %d buffers (of %d max)\n",
+ current->pid, d.request_count, dma->buf_count);
+ return -EINVAL;
+ }
+
+ d.granted_count = 0;
+
+ if (d.request_count) {
+ retcode = mga_dma_get_buffers(dev, &d);
+ }
+
+ DRM_DEBUG("%d returning, granted = %d\n",
+ current->pid, d.granted_count);
+ copy_to_user_ret((drm_dma_t *) arg, &d, sizeof(d), -EFAULT);
+ return retcode;
+}
diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c
index 7f85da2c9..24dfe5f35 100644
--- a/drivers/char/drm/proc.c
+++ b/drivers/char/drm/proc.c
@@ -2,6 +2,7 @@
* Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -22,10 +23,9 @@
* 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:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
*
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*/
#define __NO_VERSION__
@@ -164,7 +164,10 @@ static int _drm_vm_info(char *buf, char **start, off_t offset, int len,
{
drm_device_t *dev = (drm_device_t *)data;
drm_map_t *map;
- const char *types[] = { "FB", "REG", "SHM" };
+ /* Hardcoded from _DRM_FRAME_BUFFER,
+ _DRM_REGISTERS, _DRM_SHM, and
+ _DRM_AGP. */
+ const char *types[] = { "FB", "REG", "SHM", "AGP" };
const char *type;
int i;
@@ -175,7 +178,7 @@ static int _drm_vm_info(char *buf, char **start, off_t offset, int len,
"address mtrr\n\n");
for (i = 0; i < dev->map_count; i++) {
map = dev->maplist[i];
- if (map->type < 0 || map->type > 2) type = "??";
+ if (map->type < 0 || map->type > 3) type = "??";
else type = types[map->type];
DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s 0x%02x 0x%08lx ",
i,
@@ -348,17 +351,21 @@ static int drm_clients_info(char *buf, char **start, off_t offset, int len,
#if DRM_DEBUG_CODE
+#define DRM_VMA_VERBOSE 0
+
static int _drm_vma_info(char *buf, char **start, off_t offset, int len,
int *eof, void *data)
{
drm_device_t *dev = (drm_device_t *)data;
drm_vma_entry_t *pt;
+ struct vm_area_struct *vma;
+#if DRM_VMA_VERBOSE
+ unsigned long i;
+ unsigned long address;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
- unsigned long i;
- struct vm_area_struct *vma;
- unsigned long address;
+#endif
#if defined(__i386__)
unsigned int pgprot;
#endif
@@ -397,6 +404,7 @@ static int _drm_vma_info(char *buf, char **start, off_t offset, int len,
pgprot & _PAGE_GLOBAL ? 'g' : 'l' );
#endif
DRM_PROC_PRINT("\n");
+#if 0
for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) {
pgd = pgd_offset(vma->vm_mm, i);
pmd = pmd_offset(pgd, i);
@@ -417,6 +425,7 @@ static int _drm_vma_info(char *buf, char **start, off_t offset, int len,
DRM_PROC_PRINT(" 0x%08lx\n", i);
}
}
+#endif
}
return len;
@@ -512,9 +521,9 @@ static int _drm_histo_info(char *buf, char **start, off_t offset, int len,
} else {
DRM_PROC_PRINT("lock none\n");
}
- DRM_PROC_PRINT("context_flag 0x%08lx\n", dev->context_flag);
- DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag);
- DRM_PROC_PRINT("dma_flag 0x%08lx\n", dev->dma_flag);
+ DRM_PROC_PRINT("context_flag 0x%08x\n", dev->context_flag);
+ DRM_PROC_PRINT("interrupt_flag 0x%08x\n", dev->interrupt_flag);
+ DRM_PROC_PRINT("dma_flag 0x%08x\n", dev->dma_flag);
DRM_PROC_PRINT("queue_count %10d\n", dev->queue_count);
DRM_PROC_PRINT("last_context %10d\n", dev->last_context);
diff --git a/drivers/char/drm/r128_bufs.c b/drivers/char/drm/r128_bufs.c
new file mode 100644
index 000000000..e8ff4df43
--- /dev/null
+++ b/drivers/char/drm/r128_bufs.c
@@ -0,0 +1,308 @@
+/* r128_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Wed Apr 12 16:19:08 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Kevin E. Martin <martin@valinux.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
+ * Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+#include "linux/un.h"
+
+
+#ifdef DRM_AGP
+int r128_addbufs_agp(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_device_dma_t *dma = dev->dma;
+ drm_buf_desc_t request;
+ drm_buf_entry_t *entry;
+ drm_buf_t *buf;
+ unsigned long offset;
+ unsigned long agp_offset;
+ int count;
+ int order;
+ int size;
+ int alignment;
+ int page_order;
+ int total;
+ int byte_count;
+ int i;
+
+ if (!dma) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ count = request.count;
+ order = drm_order(request.size);
+ size = 1 << order;
+
+ alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+ page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+ total = PAGE_SIZE << page_order;
+
+ byte_count = 0;
+ agp_offset = dev->agp->base + request.agp_start;
+
+ DRM_DEBUG("count: %d\n", count);
+ DRM_DEBUG("order: %d\n", order);
+ DRM_DEBUG("size: %d\n", size);
+ DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+ DRM_DEBUG("alignment: %d\n", alignment);
+ DRM_DEBUG("page_order: %d\n", page_order);
+ DRM_DEBUG("total: %d\n", total);
+
+ if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+ if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+ spin_lock(&dev->count_lock);
+ if (dev->buf_use) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ atomic_inc(&dev->buf_alloc);
+ spin_unlock(&dev->count_lock);
+
+ down(&dev->struct_sem);
+ entry = &dma->bufs[order];
+ if (entry->buf_count) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM; /* May only call once for each order */
+ }
+
+ entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+ DRM_MEM_BUFS);
+ if (!entry->buflist) {
+ up(&dev->struct_sem);
+ atomic_dec(&dev->buf_alloc);
+ return -ENOMEM;
+ }
+ memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+ entry->buf_size = size;
+ entry->page_order = page_order;
+ offset = 0;
+
+ for (offset = 0;
+ entry->buf_count < count;
+ offset += alignment, ++entry->buf_count) {
+ buf = &entry->buflist[entry->buf_count];
+ buf->idx = dma->buf_count + entry->buf_count;
+ buf->total = alignment;
+ buf->order = order;
+ buf->used = 0;
+ buf->offset = (dma->byte_count + offset);
+ buf->address = (void *)(agp_offset + offset);
+ buf->next = NULL;
+ buf->waiting = 0;
+ buf->pending = 0;
+ init_waitqueue_head(&buf->dma_wait);
+ buf->pid = 0;
+
+ buf->dev_priv_size = sizeof(drm_r128_buf_priv_t);
+ buf->dev_private = drm_alloc(sizeof(drm_r128_buf_priv_t),
+ DRM_MEM_BUFS);
+ memset(buf->dev_private, 0, buf->dev_priv_size);
+
+#if DRM_DMA_HISTOGRAM
+ buf->time_queued = 0;
+ buf->time_dispatched = 0;
+ buf->time_completed = 0;
+ buf->time_freed = 0;
+#endif
+
+ byte_count += PAGE_SIZE << page_order;
+
+ DRM_DEBUG("buffer %d @ %p\n",
+ entry->buf_count, buf->address);
+ }
+
+ DRM_DEBUG("byte_count: %d\n", byte_count);
+
+ dma->buflist = drm_realloc(dma->buflist,
+ dma->buf_count * sizeof(*dma->buflist),
+ (dma->buf_count + entry->buf_count)
+ * sizeof(*dma->buflist),
+ DRM_MEM_BUFS);
+ for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+ dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+ dma->buf_count += entry->buf_count;
+ dma->byte_count += byte_count;
+
+ drm_freelist_create(&entry->freelist, entry->buf_count);
+ for (i = 0; i < entry->buf_count; i++) {
+ drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+ }
+
+ up(&dev->struct_sem);
+
+ request.count = entry->buf_count;
+ request.size = size;
+
+ copy_to_user_ret((drm_buf_desc_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ dma->flags = _DRM_DMA_USE_AGP;
+
+ atomic_dec(&dev->buf_alloc);
+ return 0;
+}
+#endif
+
+int r128_addbufs(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_r128_private_t *dev_priv = dev->dev_private;
+ drm_buf_desc_t request;
+
+ if (!dev_priv || dev_priv->is_pci) return -EINVAL;
+
+ copy_from_user_ret(&request,
+ (drm_buf_desc_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+#ifdef DRM_AGP
+ if (request.flags & _DRM_AGP_BUFFER)
+ return r128_addbufs_agp(inode, filp, cmd, arg);
+ else
+#endif
+ return -EINVAL;
+}
+
+int r128_mapbufs(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_r128_private_t *dev_priv = dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+ int retcode = 0;
+ const int zero = 0;
+ unsigned long virtual;
+ unsigned long address;
+ drm_buf_map_t request;
+ int i;
+
+ if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL;
+
+ DRM_DEBUG("\n");
+
+ spin_lock(&dev->count_lock);
+ if (atomic_read(&dev->buf_alloc)) {
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ ++dev->buf_use; /* Can't allocate more after this call */
+ spin_unlock(&dev->count_lock);
+
+ copy_from_user_ret(&request,
+ (drm_buf_map_t *)arg,
+ sizeof(request),
+ -EFAULT);
+
+ if (request.count >= dma->buf_count) {
+ if (dma->flags & _DRM_DMA_USE_AGP) {
+ drm_map_t *map;
+
+ map = dev_priv->agp_vertbufs;
+ if (!map) {
+ retcode = -EINVAL;
+ goto done;
+ }
+
+ down(&current->mm->mmap_sem);
+ virtual = do_mmap(filp, 0, map->size,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED,
+ (unsigned long)map->offset);
+ up(&current->mm->mmap_sem);
+ } else {
+ down(&current->mm->mmap_sem);
+ virtual = do_mmap(filp, 0, dma->byte_count,
+ PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+ up(&current->mm->mmap_sem);
+ }
+ if (virtual > -1024UL) {
+ /* Real error */
+ retcode = (signed long)virtual;
+ goto done;
+ }
+ request.virtual = (void *)virtual;
+
+ for (i = 0; i < dma->buf_count; i++) {
+ if (copy_to_user(&request.list[i].idx,
+ &dma->buflist[i]->idx,
+ sizeof(request.list[0].idx))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ if (copy_to_user(&request.list[i].total,
+ &dma->buflist[i]->total,
+ sizeof(request.list[0].total))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ if (copy_to_user(&request.list[i].used,
+ &zero,
+ sizeof(zero))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ address = virtual + dma->buflist[i]->offset;
+ if (copy_to_user(&request.list[i].address,
+ &address,
+ sizeof(address))) {
+ retcode = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ done:
+ request.count = dma->buf_count;
+ DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+ copy_to_user_ret((drm_buf_map_t *)arg,
+ &request,
+ sizeof(request),
+ -EFAULT);
+
+ return retcode;
+}
diff --git a/drivers/char/drm/r128_context.c b/drivers/char/drm/r128_context.c
new file mode 100644
index 000000000..f11453ba1
--- /dev/null
+++ b/drivers/char/drm/r128_context.c
@@ -0,0 +1,213 @@
+/* r128_context.c -- IOCTLs for r128 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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.
+ *
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#include <linux/sched.h>
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+extern drm_ctx_t r128_res_ctx;
+
+static int r128_alloc_queue(drm_device_t *dev)
+{
+#if 0
+ static int context = 0;
+#endif
+
+ return drm_ctxbitmap_next(dev);
+}
+
+int r128_context_switch(drm_device_t *dev, int old, int new)
+{
+ char buf[64];
+
+ atomic_inc(&dev->total_ctx);
+
+ if (test_and_set_bit(0, &dev->context_flag)) {
+ DRM_ERROR("Reentering -- FIXME\n");
+ return -EBUSY;
+ }
+
+#if DRM_DMA_HISTOGRAM
+ dev->ctx_start = get_cycles();
+#endif
+
+ DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+ if (new == dev->last_context) {
+ clear_bit(0, &dev->context_flag);
+ return 0;
+ }
+
+ if (drm_flags & DRM_FLAG_NOCTX) {
+ r128_context_switch_complete(dev, new);
+ } else {
+ sprintf(buf, "C %d %d\n", old, new);
+ drm_write_string(dev, buf);
+ }
+
+ return 0;
+}
+
+int r128_context_switch_complete(drm_device_t *dev, int new)
+{
+ dev->last_context = new; /* PRE/POST: This is the _only_ writer. */
+ dev->last_switch = jiffies;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ DRM_ERROR("Lock isn't held after context switch\n");
+ }
+
+ /* If a context switch is ever initiated
+ when the kernel holds the lock, release
+ that lock here. */
+#if DRM_DMA_HISTOGRAM
+ atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+ - dev->ctx_start)]);
+
+#endif
+ clear_bit(0, &dev->context_flag);
+ wake_up(&dev->context_wait);
+
+ return 0;
+}
+
+
+int r128_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_ctx_res_t res;
+ drm_ctx_t ctx;
+ int i;
+
+ DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+ copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT);
+ if (res.count >= DRM_RESERVED_CONTEXTS) {
+ memset(&ctx, 0, sizeof(ctx));
+ for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+ ctx.handle = i;
+ copy_to_user_ret(&res.contexts[i],
+ &i,
+ sizeof(i),
+ -EFAULT);
+ }
+ }
+ res.count = DRM_RESERVED_CONTEXTS;
+ copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT);
+ return 0;
+}
+
+
+int r128_addctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ if ((ctx.handle = r128_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+ /* Skip kernel's context and get a new one. */
+ ctx.handle = r128_alloc_queue(dev);
+ }
+ DRM_DEBUG("%d\n", ctx.handle);
+ if (ctx.handle == -1) {
+ DRM_DEBUG("Not enough free contexts.\n");
+ /* Should this return -EBUSY instead? */
+ return -ENOMEM;
+ }
+
+ copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
+ return 0;
+}
+
+int r128_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+ if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+ r128_res_ctx.handle=ctx.handle;
+ return 0;
+}
+
+int r128_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT);
+ /* This is 0, because we don't hanlde any context flags */
+ ctx.flags = 0;
+ copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT);
+ return 0;
+}
+
+int r128_switchctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ return r128_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int r128_newctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ r128_context_switch_complete(dev, ctx.handle);
+
+ return 0;
+}
+
+int r128_rmctx(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_ctx_t ctx;
+
+ copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
+ DRM_DEBUG("%d\n", ctx.handle);
+ drm_ctxbitmap_free(dev, ctx.handle);
+
+ return 0;
+}
diff --git a/drivers/char/drm/r128_dma.c b/drivers/char/drm/r128_dma.c
new file mode 100644
index 000000000..16f79c1f6
--- /dev/null
+++ b/drivers/char/drm/r128_dma.c
@@ -0,0 +1,922 @@
+/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+#include <linux/interrupt.h> /* For task queue support */
+#include <linux/delay.h>
+
+
+
+#define DO_REMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size)
+
+#define DO_REMAPFREE(_m) \
+ do { \
+ if ((_m)->handle && (_m)->size) \
+ drm_ioremapfree((_m)->handle, (_m)->size); \
+ } while (0)
+
+#define DO_FIND_MAP(_m, _o) \
+ do { \
+ int _i; \
+ for (_i = 0; _i < dev->map_count; _i++) { \
+ if (dev->maplist[_i]->offset == _o) { \
+ _m = dev->maplist[_i]; \
+ break; \
+ } \
+ } \
+ } while (0)
+
+
+#define R128_MAX_VBUF_AGE 0x10000000
+#define R128_VB_AGE_REG R128_GUI_SCRATCH_REG0
+
+int R128_READ_PLL(drm_device_t *dev, int addr)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+
+ R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f);
+ return R128_READ(R128_CLOCK_CNTL_DATA);
+}
+
+static void r128_flush_write_combine(void)
+{
+ int xchangeDummy;
+
+ __asm__ volatile("push %%eax ;"
+ "xchg %%eax, %0 ;"
+ "pop %%eax" : : "m" (xchangeDummy));
+ __asm__ volatile("push %%eax ;"
+ "push %%ebx ;"
+ "push %%ecx ;"
+ "push %%edx ;"
+ "movl $0,%%eax ;"
+ "cpuid ;"
+ "pop %%edx ;"
+ "pop %%ecx ;"
+ "pop %%ebx ;"
+ "pop %%eax" : /* no outputs */ : /* no inputs */ );
+}
+
+static void r128_status(drm_device_t *dev)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+
+ printk("GUI_STAT = 0x%08x\n",
+ (unsigned int)R128_READ(R128_GUI_STAT));
+ printk("PM4_STAT = 0x%08x\n",
+ (unsigned int)R128_READ(R128_PM4_STAT));
+ printk("PM4_BUFFER_DL_WPTR = 0x%08x\n",
+ (unsigned int)R128_READ(R128_PM4_BUFFER_DL_WPTR));
+ printk("PM4_BUFFER_DL_RPTR = 0x%08x\n",
+ (unsigned int)R128_READ(R128_PM4_BUFFER_DL_RPTR));
+}
+
+static int r128_do_cleanup_cce(drm_device_t *dev)
+{
+ if (dev->dev_private) {
+ drm_r128_private_t *dev_priv = dev->dev_private;
+
+ if (!dev_priv->is_pci) {
+ DO_REMAPFREE(dev_priv->agp_ring);
+ DO_REMAPFREE(dev_priv->agp_read_ptr);
+ DO_REMAPFREE(dev_priv->agp_vertbufs);
+ DO_REMAPFREE(dev_priv->agp_indbufs);
+ DO_REMAPFREE(dev_priv->agp_textures);
+ }
+
+ drm_free(dev->dev_private, sizeof(drm_r128_private_t),
+ DRM_MEM_DRIVER);
+ dev->dev_private = NULL;
+ }
+
+ return 0;
+}
+
+static int r128_do_init_cce(drm_device_t *dev, drm_r128_init_t *init)
+{
+ drm_r128_private_t *dev_priv;
+ int i;
+
+ dev_priv = drm_alloc(sizeof(drm_r128_private_t), DRM_MEM_DRIVER);
+ if (dev_priv == NULL) return -ENOMEM;
+ dev->dev_private = (void *)dev_priv;
+
+ memset(dev_priv, 0, sizeof(drm_r128_private_t));
+
+ dev_priv->is_pci = init->is_pci;
+
+ dev_priv->usec_timeout = init->usec_timeout;
+ if (dev_priv->usec_timeout < 1 ||
+ dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT) {
+ drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+ dev->dev_private = NULL;
+ return -EINVAL;
+ }
+
+ dev_priv->cce_mode = init->cce_mode;
+ dev_priv->cce_fifo_size = init->cce_fifo_size;
+ dev_priv->cce_is_bm_mode =
+ ((init->cce_mode == R128_PM4_192BM) ||
+ (init->cce_mode == R128_PM4_128BM_64INDBM) ||
+ (init->cce_mode == R128_PM4_64BM_128INDBM) ||
+ (init->cce_mode == R128_PM4_64BM_64VCBM_64INDBM));
+ dev_priv->cce_secure = init->cce_secure;
+
+ if (dev_priv->cce_is_bm_mode && dev_priv->is_pci) {
+ drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+ dev->dev_private = NULL;
+ return -EINVAL;
+ }
+
+ for (i = 0; i < dev->map_count; i++) {
+ if (dev->maplist[i]->type == _DRM_SHM) {
+ dev_priv->sarea = dev->maplist[i];
+ break;
+ }
+ }
+
+ DO_FIND_MAP(dev_priv->fb, init->fb_offset);
+ if (!dev_priv->is_pci) {
+ DO_FIND_MAP(dev_priv->agp_ring, init->agp_ring_offset);
+ DO_FIND_MAP(dev_priv->agp_read_ptr, init->agp_read_ptr_offset);
+ DO_FIND_MAP(dev_priv->agp_vertbufs, init->agp_vertbufs_offset);
+ DO_FIND_MAP(dev_priv->agp_indbufs, init->agp_indbufs_offset);
+ DO_FIND_MAP(dev_priv->agp_textures, init->agp_textures_offset);
+ }
+ DO_FIND_MAP(dev_priv->mmio, init->mmio_offset);
+
+ dev_priv->sarea_priv =
+ (drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle +
+ init->sarea_priv_offset);
+
+ if (!dev_priv->is_pci) {
+ DO_REMAP(dev_priv->agp_ring);
+ DO_REMAP(dev_priv->agp_read_ptr);
+ DO_REMAP(dev_priv->agp_vertbufs);
+#if 0
+ DO_REMAP(dev_priv->agp_indirectbufs);
+ DO_REMAP(dev_priv->agp_textures);
+#endif
+
+ dev_priv->ring_size = init->ring_size;
+ dev_priv->ring_sizel2qw = drm_order(init->ring_size/8);
+ dev_priv->ring_entries = init->ring_size/sizeof(u32);
+ dev_priv->ring_read_ptr = ((__volatile__ u32 *)
+ dev_priv->agp_read_ptr->handle);
+ dev_priv->ring_start = (u32 *)dev_priv->agp_ring->handle;
+ dev_priv->ring_end = ((u32 *)dev_priv->agp_ring->handle
+ + dev_priv->ring_entries);
+ }
+
+ dev_priv->submit_age = 0;
+ R128_WRITE(R128_VB_AGE_REG, dev_priv->submit_age);
+
+ return 0;
+}
+
+int r128_init_cce(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_r128_init_t init;
+
+ copy_from_user_ret(&init, (drm_r128_init_t *)arg, sizeof(init),
+ -EFAULT);
+
+ switch (init.func) {
+ case R128_INIT_CCE:
+ return r128_do_init_cce(dev, &init);
+ case R128_CLEANUP_CCE:
+ return r128_do_cleanup_cce(dev);
+ }
+
+ return -EINVAL;
+}
+
+static void r128_mark_vertbufs_done(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ int i;
+
+ for (i = 0; i < dma->buf_count; i++) {
+ drm_buf_t *buf = dma->buflist[i];
+ drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+ buf_priv->age = 0;
+ }
+}
+
+static int r128_do_pixcache_flush(drm_device_t *dev)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ u32 tmp;
+ int i;
+
+ tmp = R128_READ(R128_PC_NGUI_CTLSTAT) | R128_PC_FLUSH_ALL;
+ R128_WRITE(R128_PC_NGUI_CTLSTAT, tmp);
+
+ for (i = 0; i < dev_priv->usec_timeout; i++) {
+ if (!(R128_READ(R128_PC_NGUI_CTLSTAT) & R128_PC_BUSY))
+ return 0;
+ udelay(1);
+ }
+
+ return -EBUSY;
+}
+
+static int r128_do_wait_for_fifo(drm_device_t *dev, int entries)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int i;
+
+ for (i = 0; i < dev_priv->usec_timeout; i++) {
+ int slots = R128_READ(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK;
+ if (slots >= entries) return 0;
+ udelay(1);
+ }
+ return -EBUSY;
+}
+
+static int r128_do_wait_for_idle(drm_device_t *dev)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int i, ret;
+
+ if (!(ret = r128_do_wait_for_fifo(dev, 64))) return ret;
+
+ for (i = 0; i < dev_priv->usec_timeout; i++) {
+ if (!(R128_READ(R128_GUI_STAT) & R128_GUI_ACTIVE)) {
+ (void)r128_do_pixcache_flush(dev);
+ return 0;
+ }
+ udelay(1);
+ }
+ return -EBUSY;
+}
+
+int r128_do_engine_reset(drm_device_t *dev)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ u32 clock_cntl_index, mclk_cntl, gen_reset_cntl;
+
+ (void)r128_do_pixcache_flush(dev);
+
+ clock_cntl_index = R128_READ(R128_CLOCK_CNTL_INDEX);
+ mclk_cntl = R128_READ_PLL(dev, R128_MCLK_CNTL);
+
+ R128_WRITE_PLL(R128_MCLK_CNTL,
+ mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP);
+
+ gen_reset_cntl = R128_READ(R128_GEN_RESET_CNTL);
+
+ R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl | R128_SOFT_RESET_GUI);
+ (void)R128_READ(R128_GEN_RESET_CNTL);
+ R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl & ~R128_SOFT_RESET_GUI);
+ (void)R128_READ(R128_GEN_RESET_CNTL);
+
+ R128_WRITE_PLL(R128_MCLK_CNTL, mclk_cntl);
+ R128_WRITE(R128_CLOCK_CNTL_INDEX, clock_cntl_index);
+ R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl);
+
+ /* For CCE ring buffer only */
+ if (dev_priv->cce_is_bm_mode) {
+ R128_WRITE(R128_PM4_BUFFER_DL_WPTR, 0);
+ R128_WRITE(R128_PM4_BUFFER_DL_RPTR, 0);
+ *dev_priv->ring_read_ptr = 0;
+ dev_priv->sarea_priv->ring_write = 0;
+ }
+
+ /* Reset the CCE mode */
+ (void)r128_do_wait_for_idle(dev);
+ R128_WRITE(R128_PM4_BUFFER_CNTL,
+ dev_priv->cce_mode | dev_priv->ring_sizel2qw);
+ (void)R128_READ(R128_PM4_BUFFER_ADDR); /* as per the sample code */
+ R128_WRITE(R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN);
+
+ r128_mark_vertbufs_done(dev);
+ return 0;
+}
+
+int r128_eng_reset(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;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+ dev->lock.pid != current->pid) {
+ DRM_ERROR("r128_eng_reset called without holding the lock\n");
+ return -EINVAL;
+ }
+
+ return r128_do_engine_reset(dev);
+}
+
+static int r128_do_engine_flush(drm_device_t *dev)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ u32 tmp;
+
+ tmp = R128_READ(R128_PM4_BUFFER_DL_WPTR);
+ R128_WRITE(R128_PM4_BUFFER_DL_WPTR, tmp | R128_PM4_BUFFER_DL_DONE);
+
+ return 0;
+}
+
+int r128_eng_flush(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;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+ dev->lock.pid != current->pid) {
+ DRM_ERROR("r128_eng_flush called without holding the lock\n");
+ return -EINVAL;
+ }
+
+ return r128_do_engine_flush(dev);
+}
+
+static int r128_do_cce_wait_for_fifo(drm_device_t *dev, int entries)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int i;
+
+ for (i = 0; i < dev_priv->usec_timeout; i++) {
+ int slots = R128_READ(R128_PM4_STAT) & R128_PM4_FIFOCNT_MASK;
+ if (slots >= entries) return 0;
+ udelay(1);
+ }
+ return -EBUSY;
+}
+
+int r128_do_cce_wait_for_idle(drm_device_t *dev)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int i;
+
+ if (dev_priv->cce_is_bm_mode) {
+ for (i = 0; i < dev_priv->usec_timeout; i++) {
+ if (*dev_priv->ring_read_ptr == dev_priv->sarea_priv->ring_write) {
+ int pm4stat = R128_READ(R128_PM4_STAT);
+ if ((pm4stat & R128_PM4_FIFOCNT_MASK) >= dev_priv->cce_fifo_size &&
+ !(pm4stat & (R128_PM4_BUSY | R128_PM4_GUI_ACTIVE))) {
+ return r128_do_pixcache_flush(dev);
+ }
+ }
+ udelay(1);
+ }
+ return -EBUSY;
+ } else {
+ int ret = r128_do_cce_wait_for_fifo(dev, dev_priv->cce_fifo_size);
+ if (ret < 0) return ret;
+
+ for (i = 0; i < dev_priv->usec_timeout; i++) {
+ int pm4stat = R128_READ(R128_PM4_STAT);
+ if (!(pm4stat & (R128_PM4_BUSY | R128_PM4_GUI_ACTIVE))) {
+ return r128_do_pixcache_flush(dev);
+ }
+ udelay(1);
+ }
+ return -EBUSY;
+ }
+}
+
+int r128_cce_idle(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;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+ dev->lock.pid != current->pid) {
+ DRM_ERROR("r128_wait_idle called without holding the lock\n");
+ return -EINVAL;
+ }
+
+ return r128_do_cce_wait_for_idle(dev);
+}
+
+static int r128_submit_packets_ring_secure(drm_device_t *dev,
+ u32 *commands, int *count)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int write = dev_priv->sarea_priv->ring_write;
+ int *write_ptr = dev_priv->ring_start + write;
+ int c = *count;
+ u32 tmp = 0;
+ int psize = 0;
+ int writing = 1;
+ int timeout;
+
+ while (c > 0) {
+ tmp = *commands++;
+ if (!psize) {
+ writing = 1;
+
+ if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET0) {
+ if ((tmp & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2)) {
+ if ((tmp & R128_CCE_PACKET0_REG_MASK) !=
+ (R128_PM4_VC_FPU_SETUP >> 2)) {
+ writing = 0;
+ }
+ }
+ psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+ } else if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET1) {
+ if ((tmp & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2)) {
+ if ((tmp & R128_CCE_PACKET1_REG0_MASK) !=
+ (R128_PM4_VC_FPU_SETUP >> 2)) {
+ writing = 0;
+ }
+ } else if ((tmp & R128_CCE_PACKET1_REG1_MASK) <=
+ (0x1004 << 9)) {
+ if ((tmp & R128_CCE_PACKET1_REG1_MASK) !=
+ (R128_PM4_VC_FPU_SETUP << 9)) {
+ writing = 0;
+ }
+ }
+ psize = 3;
+ } else {
+ psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+ }
+ }
+ psize--;
+
+ if (writing) {
+ write++;
+ *write_ptr++ = tmp;
+ }
+ if (write >= dev_priv->ring_entries) {
+ write = 0;
+ write_ptr = dev_priv->ring_start;
+ }
+ timeout = 0;
+ while (write == *dev_priv->ring_read_ptr) {
+ (void)R128_READ(R128_PM4_BUFFER_DL_RPTR);
+ if (timeout++ >= dev_priv->usec_timeout)
+ return -EBUSY;
+ udelay(1);
+ }
+ c--;
+ }
+
+ if (write < 32)
+ memcpy(dev_priv->ring_end,
+ dev_priv->ring_start,
+ write * sizeof(u32));
+
+ /* Make sure WC cache has been flushed */
+ r128_flush_write_combine();
+
+ dev_priv->sarea_priv->ring_write = write;
+ R128_WRITE(R128_PM4_BUFFER_DL_WPTR, write);
+
+ *count = 0;
+
+ return 0;
+}
+
+static int r128_submit_packets_pio_secure(drm_device_t *dev,
+ u32 *commands, int *count)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ u32 tmp = 0;
+ int psize = 0;
+ int writing = 1;
+ int addr = R128_PM4_FIFO_DATA_EVEN;
+ int ret;
+
+ while (*count > 0) {
+ tmp = *commands++;
+ if (!psize) {
+ writing = 1;
+
+ if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET0) {
+ if ((tmp & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2)) {
+ if ((tmp & R128_CCE_PACKET0_REG_MASK) !=
+ (R128_PM4_VC_FPU_SETUP >> 2)) {
+ writing = 0;
+ }
+ }
+ psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+ } else if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET1) {
+ if ((tmp & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2)) {
+ if ((tmp & R128_CCE_PACKET1_REG0_MASK) !=
+ (R128_PM4_VC_FPU_SETUP >> 2)) {
+ writing = 0;
+ }
+ } else if ((tmp & R128_CCE_PACKET1_REG1_MASK) <=
+ (0x1004 << 9)) {
+ if ((tmp & R128_CCE_PACKET1_REG1_MASK) !=
+ (R128_PM4_VC_FPU_SETUP << 9)) {
+ writing = 0;
+ }
+ }
+ psize = 3;
+ } else {
+ psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+ }
+ }
+ psize--;
+
+ if (writing) {
+ if ((ret = r128_do_cce_wait_for_fifo(dev, 1)) < 0)
+ return ret;
+ R128_WRITE(addr, tmp);
+ addr ^= 0x0004;
+ }
+
+ *count -= 1;
+ }
+
+ if (addr == R128_PM4_FIFO_DATA_ODD) {
+ if ((ret = r128_do_cce_wait_for_fifo(dev, 1)) < 0) return ret;
+ R128_WRITE(addr, R128_CCE_PACKET2);
+ }
+
+ return 0;
+}
+
+static int r128_submit_packets_ring(drm_device_t *dev,
+ u32 *commands, int *count)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int write = dev_priv->sarea_priv->ring_write;
+ int *write_ptr = dev_priv->ring_start + write;
+ int c = *count;
+ int timeout;
+
+ while (c > 0) {
+ write++;
+ *write_ptr++ = *commands++;
+ if (write >= dev_priv->ring_entries) {
+ write = 0;
+ write_ptr = dev_priv->ring_start;
+ }
+ timeout = 0;
+ while (write == *dev_priv->ring_read_ptr) {
+ (void)R128_READ(R128_PM4_BUFFER_DL_RPTR);
+ if (timeout++ >= dev_priv->usec_timeout)
+ return -EBUSY;
+ udelay(1);
+ }
+ c--;
+ }
+
+ if (write < 32)
+ memcpy(dev_priv->ring_end,
+ dev_priv->ring_start,
+ write * sizeof(u32));
+
+ /* Make sure WC cache has been flushed */
+ r128_flush_write_combine();
+
+ dev_priv->sarea_priv->ring_write = write;
+ R128_WRITE(R128_PM4_BUFFER_DL_WPTR, write);
+
+ *count = 0;
+
+ return 0;
+}
+
+static int r128_submit_packets_pio(drm_device_t *dev,
+ u32 *commands, int *count)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int ret;
+
+ while (*count > 1) {
+ if ((ret = r128_do_cce_wait_for_fifo(dev, 2)) < 0) return ret;
+ R128_WRITE(R128_PM4_FIFO_DATA_EVEN, *commands++);
+ R128_WRITE(R128_PM4_FIFO_DATA_ODD, *commands++);
+ *count -= 2;
+ }
+
+ if (*count) {
+ if ((ret = r128_do_cce_wait_for_fifo(dev, 2)) < 0) return ret;
+ R128_WRITE(R128_PM4_FIFO_DATA_EVEN, *commands++);
+ R128_WRITE(R128_PM4_FIFO_DATA_ODD, R128_CCE_PACKET2);
+ *count = 0;
+ }
+
+ return 0;
+}
+
+static int r128_do_submit_packets(drm_device_t *dev, u32 *buffer, int count)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ int c = count;
+ int ret;
+
+ if (dev_priv->cce_is_bm_mode) {
+ int left = 0;
+
+ if (c >= dev_priv->ring_entries) {
+ c = dev_priv->ring_entries-1;
+ left = count - c;
+ }
+
+ /* Since this is only used by the kernel we can use the
+ insecure ring buffer submit packet routine */
+ ret = r128_submit_packets_ring(dev, buffer, &c);
+
+ c += left;
+ } else {
+ /* Since this is only used by the kernel we can use the
+ insecure PIO submit packet routine */
+ ret = r128_submit_packets_pio(dev, buffer, &c);
+ }
+
+ if (ret < 0) return ret;
+ else return c;
+}
+
+int r128_submit_pkt(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_r128_private_t *dev_priv = dev->dev_private;
+ drm_r128_packet_t packet;
+ u32 *buffer;
+ int c;
+ int size;
+ int ret = 0;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+ dev->lock.pid != current->pid) {
+ DRM_ERROR("r128_submit_pkt called without holding the lock\n");
+ return -EINVAL;
+ }
+
+ copy_from_user_ret(&packet, (drm_r128_packet_t *)arg, sizeof(packet),
+ -EFAULT);
+
+ c = packet.count;
+ size = c * sizeof(*buffer);
+
+ if (dev_priv->cce_is_bm_mode) {
+ int left = 0;
+
+ if (c >= dev_priv->ring_entries) {
+ c = dev_priv->ring_entries-1;
+ size = c * sizeof(*buffer);
+ left = packet.count - c;
+ }
+
+ if ((buffer = kmalloc(size, 0)) == NULL) return -ENOMEM;
+ copy_from_user_ret(buffer, packet.buffer, size, -EFAULT);
+
+ if (dev_priv->cce_secure)
+ ret = r128_submit_packets_ring_secure(dev, buffer, &c);
+ else
+ ret = r128_submit_packets_ring(dev, buffer, &c);
+
+ c += left;
+ } else {
+ if ((buffer = kmalloc(size, 0)) == NULL) return -ENOMEM;
+ copy_from_user_ret(buffer, packet.buffer, size, -EFAULT);
+
+ if (dev_priv->cce_secure)
+ ret = r128_submit_packets_pio_secure(dev, buffer, &c);
+ else
+ ret = r128_submit_packets_pio(dev, buffer, &c);
+ }
+
+ kfree(buffer);
+
+ packet.count = c;
+ copy_to_user_ret((drm_r128_packet_t *)arg, &packet, sizeof(packet),
+ -EFAULT);
+
+ if (ret) return ret;
+ else if (c > 0) return -EAGAIN;
+
+ return 0;
+}
+
+static int r128_send_vertbufs(drm_device_t *dev, drm_r128_vertex_t *v)
+{
+ drm_device_dma_t *dma = dev->dma;
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ drm_r128_buf_priv_t *buf_priv;
+ drm_buf_t *buf;
+ int i, ret;
+ u32 cce[2];
+
+ /* Make sure we have valid data */
+ for (i = 0; i < v->send_count; i++) {
+ int idx = v->send_indices[i];
+
+ if (idx < 0 || idx >= dma->buf_count) {
+ DRM_ERROR("Index %d (of %d max)\n",
+ idx, dma->buf_count - 1);
+ return -EINVAL;
+ }
+ buf = dma->buflist[idx];
+ if (buf->pid != current->pid) {
+ DRM_ERROR("Process %d using buffer owned by %d\n",
+ current->pid, buf->pid);
+ return -EINVAL;
+ }
+ if (buf->pending) {
+ DRM_ERROR("Sending pending buffer:"
+ " buffer %d, offset %d\n",
+ v->send_indices[i], i);
+ return -EINVAL;
+ }
+ }
+
+ /* Wait for idle, if we've wrapped to make sure that all pending
+ buffers have been processed */
+ if (dev_priv->submit_age == R128_MAX_VBUF_AGE) {
+ if ((ret = r128_do_cce_wait_for_idle(dev)) < 0) return ret;
+ dev_priv->submit_age = 0;
+ r128_mark_vertbufs_done(dev);
+ }
+
+ /* Make sure WC cache has been flushed (if in PIO mode) */
+ if (!dev_priv->cce_is_bm_mode) r128_flush_write_combine();
+
+ /* FIXME: Add support for sending vertex buffer to the CCE here
+ instead of in client code. The v->prim holds the primitive
+ type that should be drawn. Loop over the list buffers in
+ send_indices[] and submit a packet for each VB.
+
+ This will require us to loop over the clip rects here as
+ well, which implies that we extend the kernel driver to allow
+ cliprects to be stored here. Note that the cliprects could
+ possibly come from the X server instead of the client, but
+ this will require additional changes to the DRI to allow for
+ this optimization. */
+
+ /* Submit a CCE packet that writes submit_age to R128_VB_AGE_REG */
+ cce[0] = R128CCE0(R128_CCE_PACKET0, R128_VB_AGE_REG, 0);
+ cce[1] = dev_priv->submit_age;
+ if ((ret = r128_do_submit_packets(dev, cce, 2)) < 0) {
+ /* Until we add support for sending VBs to the CCE in
+ this routine, we can recover from this error. After
+ we add that support, we won't be able to easily
+ recover, so we will probably have to implement
+ another mechanism for handling timeouts from packets
+ submitted directly by the kernel. */
+ return ret;
+ }
+
+ /* Now that the submit packet request has succeeded, we can mark
+ the buffers as pending */
+ for (i = 0; i < v->send_count; i++) {
+ buf = dma->buflist[v->send_indices[i]];
+ buf->pending = 1;
+
+ buf_priv = buf->dev_private;
+ buf_priv->age = dev_priv->submit_age;
+ }
+
+ dev_priv->submit_age++;
+
+ return 0;
+}
+
+static drm_buf_t *r128_freelist_get(drm_device_t *dev)
+{
+ drm_device_dma_t *dma = dev->dma;
+ drm_r128_private_t *dev_priv = dev->dev_private;
+ drm_r128_buf_priv_t *buf_priv;
+ drm_buf_t *buf;
+ int i, t;
+
+ /* FIXME: Optimize -- use freelist code */
+
+ for (i = 0; i < dma->buf_count; i++) {
+ buf = dma->buflist[i];
+ buf_priv = buf->dev_private;
+ if (buf->pid == 0) return buf;
+ }
+
+ for (t = 0; t < dev_priv->usec_timeout; t++) {
+ u32 done_age = R128_READ(R128_VB_AGE_REG);
+
+ for (i = 0; i < dma->buf_count; i++) {
+ buf = dma->buflist[i];
+ buf_priv = buf->dev_private;
+ if (buf->pending && buf_priv->age <= done_age) {
+ /* The buffer has been processed, so it
+ can now be used */
+ buf->pending = 0;
+ return buf;
+ }
+ }
+ udelay(1);
+ }
+
+ r128_status(dev);
+ return NULL;
+}
+
+
+static int r128_get_vertbufs(drm_device_t *dev, drm_r128_vertex_t *v)
+{
+ drm_buf_t *buf;
+ int i;
+
+ for (i = v->granted_count; i < v->request_count; i++) {
+ buf = r128_freelist_get(dev);
+ if (!buf) break;
+ buf->pid = current->pid;
+ copy_to_user_ret(&v->request_indices[i],
+ &buf->idx,
+ sizeof(buf->idx),
+ -EFAULT);
+ copy_to_user_ret(&v->request_sizes[i],
+ &buf->total,
+ sizeof(buf->total),
+ -EFAULT);
+ ++v->granted_count;
+ }
+ return 0;
+}
+
+int r128_vertex_buf(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_r128_private_t *dev_priv = dev->dev_private;
+ drm_device_dma_t *dma = dev->dma;
+ int retcode = 0;
+ drm_r128_vertex_t v;
+
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ||
+ dev->lock.pid != current->pid) {
+ DRM_ERROR("r128_vertex_buf called without holding the lock\n");
+ return -EINVAL;
+ }
+
+ if (!dev_priv || dev_priv->is_pci) {
+ DRM_ERROR("r128_vertex_buf called with a PCI card\n");
+ return -EINVAL;
+ }
+
+ copy_from_user_ret(&v, (drm_r128_vertex_t *)arg, sizeof(v), -EFAULT);
+ DRM_DEBUG("%d: %d send, %d req\n",
+ current->pid, v.send_count, v.request_count);
+
+ if (v.send_count < 0 || v.send_count > dma->buf_count) {
+ DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n",
+ current->pid, v.send_count, dma->buf_count);
+ return -EINVAL;
+ }
+ if (v.request_count < 0 || v.request_count > dma->buf_count) {
+ DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
+ current->pid, v.request_count, dma->buf_count);
+ return -EINVAL;
+ }
+
+ if (v.send_count) {
+ retcode = r128_send_vertbufs(dev, &v);
+ }
+
+ v.granted_count = 0;
+
+ if (!retcode && v.request_count) {
+ retcode = r128_get_vertbufs(dev, &v);
+ }
+
+ DRM_DEBUG("%d returning, granted = %d\n",
+ current->pid, v.granted_count);
+ copy_to_user_ret((drm_r128_vertex_t *)arg, &v, sizeof(v), -EFAULT);
+
+ return retcode;
+}
diff --git a/drivers/char/drm/r128_drm.h b/drivers/char/drm/r128_drm.h
new file mode 100644
index 000000000..ac6f73bcd
--- /dev/null
+++ b/drivers/char/drm/r128_drm.h
@@ -0,0 +1,111 @@
+/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*-
+ * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#ifndef _R128_DRM_H_
+#define _R128_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmR128.h)
+ */
+typedef struct drm_r128_init {
+ enum {
+ R128_INIT_CCE = 0x01,
+ R128_CLEANUP_CCE = 0x02
+ } func;
+ int sarea_priv_offset;
+ int is_pci;
+ int cce_mode;
+ int cce_fifo_size;
+ int cce_secure;
+ int ring_size;
+ int usec_timeout;
+
+ int fb_offset;
+ int agp_ring_offset;
+ int agp_read_ptr_offset;
+ int agp_vertbufs_offset;
+ int agp_indbufs_offset;
+ int agp_textures_offset;
+ int mmio_offset;
+} drm_r128_init_t;
+
+typedef struct drm_r128_packet {
+ unsigned long *buffer;
+ int count;
+ int flags;
+} drm_r128_packet_t;
+
+typedef enum drm_r128_prim {
+ _DRM_R128_PRIM_NONE = 0x0001,
+ _DRM_R128_PRIM_POINT = 0x0002,
+ _DRM_R128_PRIM_LINE = 0x0004,
+ _DRM_R128_PRIM_POLY_LINE = 0x0008,
+ _DRM_R128_PRIM_TRI_LIST = 0x0010,
+ _DRM_R128_PRIM_TRI_FAN = 0x0020,
+ _DRM_R128_PRIM_TRI_STRIP = 0x0040,
+ _DRM_R128_PRIM_TRI_TYPE2 = 0x0080
+} drm_r128_prim_t;
+
+typedef struct drm_r128_vertex {
+ /* Indices here refer to the offset into
+ buflist in drm_buf_get_t. */
+ int send_count; /* Number of buffers to send */
+ int *send_indices; /* List of handles to buffers */
+ int *send_sizes; /* Lengths of data to send */
+ drm_r128_prim_t prim; /* Primitive type */
+ int request_count; /* Number of buffers requested */
+ int *request_indices; /* Buffer information */
+ int *request_sizes;
+ int granted_count; /* Number of buffers granted */
+} drm_r128_vertex_t;
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (r128_sarea.h)
+ */
+#define R128_LOCAL_TEX_HEAP 0
+#define R128_AGP_TEX_HEAP 1
+#define R128_NR_TEX_HEAPS 2
+#define R128_NR_TEX_REGIONS 64
+#define R128_LOG_TEX_GRANULARITY 16
+
+typedef struct drm_tex_region {
+ unsigned char next, prev;
+ unsigned char in_use;
+ int age;
+} drm_tex_region_t;
+
+typedef struct drm_r128_sarea {
+ drm_tex_region_t tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS+1];
+ int tex_age[R128_NR_TEX_HEAPS];
+ int ctx_owner;
+ int ring_write;
+} drm_r128_sarea_t;
+
+#endif
diff --git a/drivers/char/drm/r128_drv.c b/drivers/char/drm/r128_drv.c
new file mode 100644
index 000000000..e78d0231f
--- /dev/null
+++ b/drivers/char/drm/r128_drv.c
@@ -0,0 +1,717 @@
+/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:47:27 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
+#include "drmP.h"
+#include "r128_drv.h"
+EXPORT_SYMBOL(r128_init);
+EXPORT_SYMBOL(r128_cleanup);
+
+#define R128_NAME "r128"
+#define R128_DESC "ATI Rage 128"
+#define R128_DATE "20000719"
+#define R128_MAJOR 1
+#define R128_MINOR 0
+#define R128_PATCHLEVEL 0
+
+static drm_device_t r128_device;
+drm_ctx_t r128_res_ctx;
+
+static struct file_operations r128_fops = {
+#if LINUX_VERSION_CODE >= 0x020322
+ /* This started being used approx. 2.3.34 */
+ owner: THIS_MODULE,
+#endif
+ open: r128_open,
+ flush: drm_flush,
+ release: r128_release,
+ ioctl: r128_ioctl,
+ mmap: drm_mmap,
+ read: drm_read,
+ fasync: drm_fasync,
+ poll: drm_poll,
+};
+
+static struct miscdevice r128_misc = {
+ minor: MISC_DYNAMIC_MINOR,
+ name: R128_NAME,
+ fops: &r128_fops,
+};
+
+static drm_ioctl_desc_t r128_ioctls[] = {
+ [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { r128_version, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { r128_addbufs, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { drm_markbufs, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { drm_infobufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { r128_mapbufs, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { drm_freebufs, 1, 0 },
+
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { r128_addctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { r128_rmctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { r128_modctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { r128_getctx, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { r128_switchctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { r128_newctx, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { r128_resctx, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { r128_lock, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { r128_unlock, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 },
+
+#ifdef DRM_AGP
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 },
+#endif
+
+ [DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)] = { r128_init_cce, 1, 1 },
+ [DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)] = { r128_eng_reset, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_R128_FLUSH)] = { r128_eng_flush, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_R128_PACKET)] = { r128_submit_pkt, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_R128_IDLE)] = { r128_cce_idle, 1, 0 },
+ [DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)] = { r128_vertex_buf, 1, 0 },
+};
+#define R128_IOCTL_COUNT DRM_ARRAY_SIZE(r128_ioctls)
+
+#ifdef MODULE
+static char *r128 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("r128");
+MODULE_PARM(r128, "s");
+
+module_init(r128_init);
+module_exit(r128_cleanup);
+
+#ifndef MODULE
+/* r128_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO). It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init r128_options(char *str)
+{
+ drm_parse_options(str);
+ return 1;
+}
+
+__setup("r128=", r128_options);
+#endif
+
+static int r128_setup(drm_device_t *dev)
+{
+ int i;
+
+ atomic_set(&dev->ioctl_count, 0);
+ atomic_set(&dev->vma_count, 0);
+ dev->buf_use = 0;
+ atomic_set(&dev->buf_alloc, 0);
+
+ drm_dma_setup(dev);
+
+ atomic_set(&dev->total_open, 0);
+ atomic_set(&dev->total_close, 0);
+ atomic_set(&dev->total_ioctl, 0);
+ atomic_set(&dev->total_irq, 0);
+ atomic_set(&dev->total_ctx, 0);
+ atomic_set(&dev->total_locks, 0);
+ atomic_set(&dev->total_unlocks, 0);
+ atomic_set(&dev->total_contends, 0);
+ atomic_set(&dev->total_sleeps, 0);
+
+ for (i = 0; i < DRM_HASH_SIZE; i++) {
+ dev->magiclist[i].head = NULL;
+ dev->magiclist[i].tail = NULL;
+ }
+ dev->maplist = NULL;
+ dev->map_count = 0;
+ dev->vmalist = NULL;
+ dev->lock.hw_lock = NULL;
+ init_waitqueue_head(&dev->lock.lock_queue);
+ dev->queue_count = 0;
+ dev->queue_reserved = 0;
+ dev->queue_slots = 0;
+ dev->queuelist = NULL;
+ dev->irq = 0;
+ dev->context_flag = 0;
+ dev->interrupt_flag = 0;
+ dev->dma_flag = 0;
+ dev->last_context = 0;
+ dev->last_switch = 0;
+ dev->last_checked = 0;
+ init_timer(&dev->timer);
+ init_waitqueue_head(&dev->context_wait);
+
+ dev->ctx_start = 0;
+ dev->lck_start = 0;
+
+ dev->buf_rp = dev->buf;
+ dev->buf_wp = dev->buf;
+ dev->buf_end = dev->buf + DRM_BSZ;
+ dev->buf_async = NULL;
+ init_waitqueue_head(&dev->buf_readers);
+ init_waitqueue_head(&dev->buf_writers);
+
+ r128_res_ctx.handle=-1;
+
+ DRM_DEBUG("\n");
+
+ /* The kernel's context could be created here, but is now created
+ in drm_dma_enqueue. This is more resource-efficient for
+ hardware that does not do DMA, but may mean that
+ drm_select_queue fails between the time the interrupt is
+ initialized and the time the queues are initialized. */
+
+ return 0;
+}
+
+
+static int r128_takedown(drm_device_t *dev)
+{
+ int i;
+ drm_magic_entry_t *pt, *next;
+ drm_map_t *map;
+ drm_vma_entry_t *vma, *vma_next;
+
+ DRM_DEBUG("\n");
+
+ down(&dev->struct_sem);
+ del_timer(&dev->timer);
+
+ if (dev->devname) {
+ drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+ dev->devname = NULL;
+ }
+
+ if (dev->unique) {
+ drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+ dev->unique = NULL;
+ dev->unique_len = 0;
+ }
+ /* Clear pid list */
+ for (i = 0; i < DRM_HASH_SIZE; i++) {
+ for (pt = dev->magiclist[i].head; pt; pt = next) {
+ next = pt->next;
+ drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+ }
+ dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+ }
+
+#ifdef DRM_AGP
+ /* Clear AGP information */
+ if (dev->agp) {
+ drm_agp_mem_t *entry;
+ drm_agp_mem_t *nexte;
+
+ /* Remove AGP resources, but leave dev->agp
+ intact until r128_cleanup is called. */
+ for (entry = dev->agp->memory; entry; entry = nexte) {
+ nexte = entry->next;
+ if (entry->bound) drm_unbind_agp(entry->memory);
+ drm_free_agp(entry->memory, entry->pages);
+ drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+ }
+ dev->agp->memory = NULL;
+
+ if (dev->agp->acquired && drm_agp.release)
+ (*drm_agp.release)();
+
+ dev->agp->acquired = 0;
+ dev->agp->enabled = 0;
+ }
+#endif
+
+ /* Clear vma list (only built for debugging) */
+ if (dev->vmalist) {
+ for (vma = dev->vmalist; vma; vma = vma_next) {
+ vma_next = vma->next;
+ drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+ }
+ dev->vmalist = NULL;
+ }
+
+ /* Clear map area and mtrr information */
+ if (dev->maplist) {
+ for (i = 0; i < dev->map_count; i++) {
+ map = dev->maplist[i];
+ switch (map->type) {
+ case _DRM_REGISTERS:
+ case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+ if (map->mtrr >= 0) {
+ int retcode;
+ retcode = mtrr_del(map->mtrr,
+ map->offset,
+ map->size);
+ DRM_DEBUG("mtrr_del = %d\n", retcode);
+ }
+#endif
+ drm_ioremapfree(map->handle, map->size);
+ break;
+ case _DRM_SHM:
+ drm_free_pages((unsigned long)map->handle,
+ drm_order(map->size)
+ - PAGE_SHIFT,
+ DRM_MEM_SAREA);
+ break;
+ case _DRM_AGP:
+ /* Do nothing here, because this is all
+ handled in the AGP/GART driver. */
+ break;
+ }
+ drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+ }
+ drm_free(dev->maplist,
+ dev->map_count * sizeof(*dev->maplist),
+ DRM_MEM_MAPS);
+ dev->maplist = NULL;
+ dev->map_count = 0;
+ }
+
+ drm_dma_takedown(dev);
+
+ dev->queue_count = 0;
+ if (dev->lock.hw_lock) {
+ dev->lock.hw_lock = NULL; /* SHM removed */
+ dev->lock.pid = 0;
+ wake_up_interruptible(&dev->lock.lock_queue);
+ }
+ up(&dev->struct_sem);
+
+ return 0;
+}
+
+/* r128_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+int r128_init(void)
+{
+ int retcode;
+ drm_device_t *dev = &r128_device;
+
+ DRM_DEBUG("\n");
+
+ memset((void *)dev, 0, sizeof(*dev));
+ dev->count_lock = SPIN_LOCK_UNLOCKED;
+ sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+ drm_parse_options(r128);
+#endif
+
+ if ((retcode = misc_register(&r128_misc))) {
+ DRM_ERROR("Cannot register \"%s\"\n", R128_NAME);
+ return retcode;
+ }
+ dev->device = MKDEV(MISC_MAJOR, r128_misc.minor);
+ dev->name = R128_NAME;
+
+ drm_mem_init();
+ drm_proc_init(dev);
+
+#ifdef DRM_AGP
+ dev->agp = drm_agp_init();
+ if (dev->agp == NULL) {
+ DRM_ERROR("Cannot initialize agpgart module.\n");
+ drm_proc_cleanup();
+ misc_deregister(&r128_misc);
+ r128_takedown(dev);
+ return -ENOMEM;
+ }
+
+#ifdef CONFIG_MTRR
+ dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+ dev->agp->agp_info.aper_size*1024*1024,
+ MTRR_TYPE_WRCOMB,
+ 1);
+#endif
+#endif
+
+ if((retcode = drm_ctxbitmap_init(dev))) {
+ DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+ drm_proc_cleanup();
+ misc_deregister(&r128_misc);
+ r128_takedown(dev);
+ return retcode;
+ }
+
+ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+ R128_NAME,
+ R128_MAJOR,
+ R128_MINOR,
+ R128_PATCHLEVEL,
+ R128_DATE,
+ r128_misc.minor);
+
+ return 0;
+}
+
+/* r128_cleanup is called via cleanup_module at module unload time. */
+
+void r128_cleanup(void)
+{
+ drm_device_t *dev = &r128_device;
+
+ DRM_DEBUG("\n");
+
+ drm_proc_cleanup();
+ if (misc_deregister(&r128_misc)) {
+ DRM_ERROR("Cannot unload module\n");
+ } else {
+ DRM_INFO("Module unloaded\n");
+ }
+ drm_ctxbitmap_cleanup(dev);
+ r128_takedown(dev);
+#ifdef DRM_AGP
+ if (dev->agp) {
+ drm_agp_uninit();
+ drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+ dev->agp = NULL;
+ }
+#endif
+}
+
+int r128_version(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ drm_version_t version;
+ int len;
+
+ copy_from_user_ret(&version,
+ (drm_version_t *)arg,
+ sizeof(version),
+ -EFAULT);
+
+#define DRM_COPY(name,value) \
+ len = strlen(value); \
+ if (len > name##_len) len = name##_len; \
+ name##_len = strlen(value); \
+ if (len && name) { \
+ copy_to_user_ret(name, value, len, -EFAULT); \
+ }
+
+ version.version_major = R128_MAJOR;
+ version.version_minor = R128_MINOR;
+ version.version_patchlevel = R128_PATCHLEVEL;
+
+ DRM_COPY(version.name, R128_NAME);
+ DRM_COPY(version.date, R128_DATE);
+ DRM_COPY(version.desc, R128_DESC);
+
+ copy_to_user_ret((drm_version_t *)arg,
+ &version,
+ sizeof(version),
+ -EFAULT);
+ return 0;
+}
+
+int r128_open(struct inode *inode, struct file *filp)
+{
+ drm_device_t *dev = &r128_device;
+ int retcode = 0;
+
+ DRM_DEBUG("open_count = %d\n", dev->open_count);
+ if (!(retcode = drm_open_helper(inode, filp, dev))) {
+ MOD_INC_USE_COUNT;
+ atomic_inc(&dev->total_open);
+ spin_lock(&dev->count_lock);
+ if (!dev->open_count++) {
+ spin_unlock(&dev->count_lock);
+ return r128_setup(dev);
+ }
+ spin_unlock(&dev->count_lock);
+ }
+ return retcode;
+}
+
+int r128_release(struct inode *inode, struct file *filp)
+{
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ int retcode = 0;
+
+ DRM_DEBUG("open_count = %d\n", dev->open_count);
+ if (!(retcode = drm_release(inode, filp))) {
+ MOD_DEC_USE_COUNT;
+ atomic_inc(&dev->total_close);
+ spin_lock(&dev->count_lock);
+ if (!--dev->open_count) {
+ if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+ DRM_ERROR("Device busy: %d %d\n",
+ atomic_read(&dev->ioctl_count),
+ dev->blocked);
+ spin_unlock(&dev->count_lock);
+ return -EBUSY;
+ }
+ spin_unlock(&dev->count_lock);
+ return r128_takedown(dev);
+ }
+ spin_unlock(&dev->count_lock);
+ }
+ return retcode;
+}
+
+/* r128_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int r128_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int nr = DRM_IOCTL_NR(cmd);
+ drm_file_t *priv = filp->private_data;
+ drm_device_t *dev = priv->dev;
+ int retcode = 0;
+ drm_ioctl_desc_t *ioctl;
+ drm_ioctl_t *func;
+
+ atomic_inc(&dev->ioctl_count);
+ atomic_inc(&dev->total_ioctl);
+ ++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);
+
+ if (nr >= R128_IOCTL_COUNT) {
+ retcode = -EINVAL;
+ } else {
+ ioctl = &r128_ioctls[nr];
+ func = ioctl->func;
+
+ if (!func) {
+ DRM_DEBUG("no function\n");
+ retcode = -EINVAL;
+ } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+ || (ioctl->auth_needed && !priv->authenticated)) {
+ retcode = -EACCES;
+ } else {
+ retcode = (func)(inode, filp, cmd, arg);
+ }
+ }
+
+ atomic_dec(&dev->ioctl_count);
+ return retcode;
+}
+
+int r128_lock(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;
+ DECLARE_WAITQUEUE(entry, current);
+ int ret = 0;
+ drm_lock_t lock;
+#if DRM_DMA_HISTOGRAM
+ cycles_t start;
+
+ dev->lck_start = start = get_cycles();
+#endif
+
+ copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+ if (lock.context == DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("Process %d using kernel context %d\n",
+ current->pid, lock.context);
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+ lock.context, current->pid, dev->lock.hw_lock->lock,
+ lock.flags);
+
+#if 0
+ /* dev->queue_count == 0 right now for
+ r128. FIXME? */
+ if (lock.context < 0 || lock.context >= dev->queue_count)
+ return -EINVAL;
+#endif
+
+ if (!ret) {
+#if 0
+ if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+ != lock.context) {
+ long j = jiffies - dev->lock.lock_time;
+
+ if (lock.context == r128_res_ctx.handle &&
+ j >= 0 && j < DRM_LOCK_SLICE) {
+ /* Can't take lock if we just had it and
+ there is contention. */
+ DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n",
+ lock.context, current->pid, j,
+ dev->lock.lock_time, jiffies);
+ current->state = TASK_INTERRUPTIBLE;
+ current->policy |= SCHED_YIELD;
+ schedule_timeout(DRM_LOCK_SLICE-j);
+ DRM_DEBUG("jiffies=%d\n", jiffies);
+ }
+ }
+#endif
+ add_wait_queue(&dev->lock.lock_queue, &entry);
+ for (;;) {
+ if (!dev->lock.hw_lock) {
+ /* Device has been unregistered */
+ ret = -EINTR;
+ break;
+ }
+ if (drm_lock_take(&dev->lock.hw_lock->lock,
+ lock.context)) {
+ dev->lock.pid = current->pid;
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->total_locks);
+ break; /* Got lock */
+ }
+
+ /* Contention */
+ atomic_inc(&dev->total_sleeps);
+ current->state = TASK_INTERRUPTIBLE;
+#if 1
+ current->policy |= SCHED_YIELD;
+#endif
+ schedule();
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->lock.lock_queue, &entry);
+ }
+
+#if 0
+ if (!ret && dev->last_context != lock.context &&
+ lock.context != r128_res_ctx.handle &&
+ dev->last_context != r128_res_ctx.handle) {
+ add_wait_queue(&dev->context_wait, &entry);
+ current->state = TASK_INTERRUPTIBLE;
+ /* PRE: dev->last_context != lock.context */
+ r128_context_switch(dev, dev->last_context, lock.context);
+ /* POST: we will wait for the context
+ switch and will dispatch on a later call
+ when dev->last_context == lock.context
+ NOTE WE HOLD THE LOCK THROUGHOUT THIS
+ TIME! */
+ current->policy |= SCHED_YIELD;
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->context_wait, &entry);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ } else if (dev->last_context != lock.context) {
+ DRM_ERROR("Context mismatch: %d %d\n",
+ dev->last_context, lock.context);
+ }
+ }
+#endif
+
+ if (!ret) {
+ if (lock.flags & _DRM_LOCK_READY) {
+ /* Wait for space in DMA/FIFO */
+ }
+ if (lock.flags & _DRM_LOCK_QUIESCENT) {
+ /* Make hardware quiescent */
+#if 0
+ r128_quiescent(dev);
+#endif
+ }
+ }
+
+#if LINUX_VERSION_CODE < 0x020400
+ if (lock.context != r128_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY/4;
+ }
+#endif
+ DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+ atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+ return ret;
+}
+
+
+int r128_unlock(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_lock_t lock;
+
+ copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
+
+ if (lock.context == DRM_KERNEL_CONTEXT) {
+ DRM_ERROR("Process %d using kernel context %d\n",
+ current->pid, lock.context);
+ return -EINVAL;
+ }
+
+ DRM_DEBUG("%d frees lock (%d holds)\n",
+ lock.context,
+ _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+ atomic_inc(&dev->total_unlocks);
+ if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+ atomic_inc(&dev->total_contends);
+ drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+ /* FIXME: Try to send data to card here */
+ if (!dev->context_flag) {
+ if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ DRM_ERROR("\n");
+ }
+ }
+
+#if LINUX_VERSION_CODE < 0x020400
+ if (lock.context != r128_res_ctx.handle) {
+ current->counter = 5;
+ current->priority = DEF_PRIORITY;
+ }
+#endif
+
+ return 0;
+}
diff --git a/drivers/char/drm/r128_drv.h b/drivers/char/drm/r128_drv.h
new file mode 100644
index 000000000..81390bb8b
--- /dev/null
+++ b/drivers/char/drm/r128_drv.h
@@ -0,0 +1,226 @@
+/* r128_drv.h -- Private header for r128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * 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
+ * PRECISION INSIGHT 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: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Kevin E. Martin <martin@valinux.com>
+ *
+ */
+
+#ifndef _R128_DRV_H_
+#define _R128_DRV_H_
+
+typedef struct drm_r128_private {
+ int is_pci;
+
+ int cce_mode;
+ int cce_fifo_size;
+ int cce_is_bm_mode;
+ int cce_secure;
+
+ drm_r128_sarea_t *sarea_priv;
+
+ __volatile__ u32 *ring_read_ptr;
+
+ u32 *ring_start;
+ u32 *ring_end;
+ int ring_size;
+ int ring_sizel2qw;
+ int ring_entries;
+
+ int submit_age;
+
+ int usec_timeout;
+
+ drm_map_t *sarea;
+ drm_map_t *fb;
+ drm_map_t *agp_ring;
+ drm_map_t *agp_read_ptr;
+ drm_map_t *agp_vertbufs;
+ drm_map_t *agp_indbufs;
+ drm_map_t *agp_textures;
+ drm_map_t *mmio;
+} drm_r128_private_t;
+
+typedef struct drm_r128_buf_priv {
+ u32 age;
+} drm_r128_buf_priv_t;
+
+ /* r128_drv.c */
+extern int r128_init(void);
+extern void r128_cleanup(void);
+extern int r128_version(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_open(struct inode *inode, struct file *filp);
+extern int r128_release(struct inode *inode, struct file *filp);
+extern int r128_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_lock(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_unlock(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+ /* r128_dma.c */
+extern int r128_init_cce(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_eng_reset(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_eng_flush(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_submit_pkt(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_cce_idle(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_vertex_buf(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+ /* r128_bufs.c */
+extern int r128_addbufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_mapbufs(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+ /* r128_context.c */
+extern int r128_resctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_addctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_modctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_getctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_switchctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_newctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern int r128_rmctx(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+extern int r128_context_switch(drm_device_t *dev, int old, int new);
+extern int r128_context_switch_complete(drm_device_t *dev, int new);
+
+
+/* Register definitions, register access macros and drmAddMap constants
+ * for Rage 128 kernel driver.
+ */
+
+#define R128_PC_NGUI_CTLSTAT 0x0184
+# define R128_PC_FLUSH_ALL 0x00ff
+# define R128_PC_BUSY (1 << 31)
+
+#define R128_CLOCK_CNTL_INDEX 0x0008
+#define R128_CLOCK_CNTL_DATA 0x000c
+# define R128_PLL_WR_EN (1 << 7)
+
+#define R128_MCLK_CNTL 0x000f
+# define R128_FORCE_GCP (1 << 16)
+# define R128_FORCE_PIPE3D_CP (1 << 17)
+# define R128_FORCE_RCP (1 << 18)
+
+#define R128_GEN_RESET_CNTL 0x00f0
+# define R128_SOFT_RESET_GUI (1 << 0)
+
+#define R128_PM4_BUFFER_CNTL 0x0704
+# define R128_PM4_NONPM4 (0 << 28)
+# define R128_PM4_192PIO (1 << 28)
+# define R128_PM4_192BM (2 << 28)
+# define R128_PM4_128PIO_64INDBM (3 << 28)
+# define R128_PM4_128BM_64INDBM (4 << 28)
+# define R128_PM4_64PIO_128INDBM (5 << 28)
+# define R128_PM4_64BM_128INDBM (6 << 28)
+# define R128_PM4_64PIO_64VCBM_64INDBM (7 << 28)
+# define R128_PM4_64BM_64VCBM_64INDBM (8 << 28)
+# define R128_PM4_64PIO_64VCPIO_64INDPIO (15 << 28)
+
+
+#define R128_PM4_BUFFER_DL_RPTR 0x0710
+#define R128_PM4_BUFFER_DL_WPTR 0x0714
+# define R128_PM4_BUFFER_DL_DONE (1 << 31)
+
+#define R128_PM4_VC_FPU_SETUP 0x071c
+
+#define R128_PM4_STAT 0x07b8
+# define R128_PM4_FIFOCNT_MASK 0x0fff
+# define R128_PM4_BUSY (1 << 16)
+# define R128_PM4_GUI_ACTIVE (1 << 31)
+
+#define R128_PM4_BUFFER_ADDR 0x07f0
+#define R128_PM4_MICRO_CNTL 0x07fc
+# define R128_PM4_MICRO_FREERUN (1 << 30)
+
+#define R128_PM4_FIFO_DATA_EVEN 0x1000
+#define R128_PM4_FIFO_DATA_ODD 0x1004
+
+#define R128_GUI_SCRATCH_REG0 0x15e0
+#define R128_GUI_SCRATCH_REG1 0x15e4
+#define R128_GUI_SCRATCH_REG2 0x15e8
+#define R128_GUI_SCRATCH_REG3 0x15ec
+#define R128_GUI_SCRATCH_REG4 0x15f0
+#define R128_GUI_SCRATCH_REG5 0x15f4
+
+#define R128_GUI_STAT 0x1740
+# define R128_GUI_FIFOCNT_MASK 0x0fff
+# define R128_GUI_ACTIVE (1 << 31)
+
+
+/* CCE command packets */
+#define R128_CCE_PACKET0 0x00000000
+#define R128_CCE_PACKET1 0x40000000
+#define R128_CCE_PACKET2 0x80000000
+# define R128_CCE_PACKET_MASK 0xC0000000
+# define R128_CCE_PACKET_COUNT_MASK 0x3fff0000
+# define R128_CCE_PACKET0_REG_MASK 0x000007ff
+# define R128_CCE_PACKET1_REG0_MASK 0x000007ff
+# define R128_CCE_PACKET1_REG1_MASK 0x003ff800
+
+
+#define R128_MAX_USEC_TIMEOUT 100000 /* 100 ms */
+
+
+#define R128_BASE(reg) ((u32)(dev_priv->mmio->handle))
+#define R128_ADDR(reg) (R128_BASE(reg) + reg)
+
+#define R128_DEREF(reg) *(__volatile__ int *)R128_ADDR(reg)
+#define R128_READ(reg) R128_DEREF(reg)
+#define R128_WRITE(reg,val) do { R128_DEREF(reg) = val; } while (0)
+
+#define R128_DEREF8(reg) *(__volatile__ char *)R128_ADDR(reg)
+#define R128_READ8(reg) R128_DEREF8(reg)
+#define R128_WRITE8(reg,val) do { R128_DEREF8(reg) = val; } while (0)
+
+#define R128_WRITE_PLL(addr,val) \
+do { \
+ R128_WRITE8(R128_CLOCK_CNTL_INDEX, ((addr) & 0x1f) | R128_PLL_WR_EN); \
+ R128_WRITE(R128_CLOCK_CNTL_DATA, (val)); \
+} while (0)
+
+extern int R128_READ_PLL(drm_device_t *dev, int addr);
+
+#define R128CCE0(p,r,n) ((p) | ((n) << 16) | ((r) >> 2))
+#define R128CCE1(p,r1,r2) ((p) | (((r2) >> 2) << 11) | ((r1) >> 2))
+#define R128CCE2(p) ((p))
+#define R128CCE3(p,n) ((p) | ((n) << 16))
+
+#endif
diff --git a/drivers/char/drm/tdfx_context.c b/drivers/char/drm/tdfx_context.c
index 22bf59ff3..c8d6e50ea 100644
--- a/drivers/char/drm/tdfx_context.c
+++ b/drivers/char/drm/tdfx_context.c
@@ -2,6 +2,7 @@
* Created: Thu Oct 7 10:50:22 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,8 +25,9 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
- *
+ * Rickard E. (Rik) Faith <faith@valinux.com>
+ * Daryll Strauss <daryll@valinux.com>
+ *
*/
#include <linux/sched.h>
@@ -38,9 +40,7 @@ extern drm_ctx_t tdfx_res_ctx;
static int tdfx_alloc_queue(drm_device_t *dev)
{
- static int context = 0;
-
- return ++context; /* Should this reuse contexts in the future? */
+ return drm_ctxbitmap_next(dev);
}
int tdfx_context_switch(drm_device_t *dev, int old, int new)
@@ -137,6 +137,12 @@ int tdfx_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
ctx.handle = tdfx_alloc_queue(dev);
}
DRM_DEBUG("%d\n", ctx.handle);
+ if (ctx.handle == -1) {
+ DRM_DEBUG("Not enough free contexts.\n");
+ /* Should this return -EBUSY instead? */
+ return -ENOMEM;
+ }
+
copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT);
return 0;
}
@@ -193,13 +199,13 @@ int tdfx_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
int tdfx_rmctx(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_ctx_t ctx;
copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT);
DRM_DEBUG("%d\n", ctx.handle);
- /* This is currently a noop because we
- don't reuse context values. Perhaps we
- should? */
-
+ drm_ctxbitmap_free(dev, ctx.handle);
+
return 0;
}
diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c
index b0c41c93c..97bd4180b 100644
--- a/drivers/char/drm/tdfx_drv.c
+++ b/drivers/char/drm/tdfx_drv.c
@@ -1,7 +1,8 @@
/* tdfx.c -- tdfx driver -*- linux-c -*-
* Created: Thu Oct 7 10:38:32 1999 by faith@precisioninsight.com
*
- * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -22,31 +23,37 @@
* 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:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
- * Daryll Strauss <daryll@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
+ * Daryll Strauss <daryll@valinux.com>
*
*/
#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
#include "drmP.h"
#include "tdfx_drv.h"
+EXPORT_SYMBOL(tdfx_init);
+EXPORT_SYMBOL(tdfx_cleanup);
#define TDFX_NAME "tdfx"
-#define TDFX_DESC "tdfx"
-#define TDFX_DATE "19991009"
-#define TDFX_MAJOR 0
+#define TDFX_DESC "3dfx Banshee/Voodoo3+"
+#define TDFX_DATE "20000719"
+#define TDFX_MAJOR 1
#define TDFX_MINOR 0
-#define TDFX_PATCHLEVEL 1
+#define TDFX_PATCHLEVEL 0
static drm_device_t tdfx_device;
drm_ctx_t tdfx_res_ctx;
static struct file_operations tdfx_fops = {
- owner: THIS_MODULE,
+#if LINUX_VERSION_CODE >= 0x020322
+ /* This started being used approx. 2.3.34 */
+ owner: THIS_MODULE,
+#endif
open: tdfx_open,
flush: drm_flush,
release: tdfx_release,
@@ -87,6 +94,16 @@ static drm_ioctl_desc_t tdfx_ioctls[] = {
[DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { tdfx_lock, 1, 0 },
[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { tdfx_unlock, 1, 0 },
[DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 },
+#ifdef DRM_AGP
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = {drm_agp_acquire, 1, 1},
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = {drm_agp_release, 1, 1},
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = {drm_agp_enable, 1, 1},
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = {drm_agp_info, 1, 1},
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = {drm_agp_alloc, 1, 1},
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = {drm_agp_free, 1, 1},
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = {drm_agp_unbind, 1, 1},
+ [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = {drm_agp_bind, 1, 1},
+#endif
};
#define TDFX_IOCTL_COUNT DRM_ARRAY_SIZE(tdfx_ioctls)
@@ -94,10 +111,28 @@ static drm_ioctl_desc_t tdfx_ioctls[] = {
static char *tdfx = NULL;
#endif
-MODULE_AUTHOR("Precision Insight, Inc., Cedar Park, Texas.");
+MODULE_AUTHOR("VA Linux Systems, Inc.");
MODULE_DESCRIPTION("tdfx");
MODULE_PARM(tdfx, "s");
+module_init(tdfx_init);
+module_exit(tdfx_cleanup);
+
+#ifndef MODULE
+/* tdfx_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO). It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init tdfx_options(char *str)
+{
+ drm_parse_options(str);
+ return 1;
+}
+
+__setup("tdfx=", tdfx_options);
+#endif
+
static int tdfx_setup(drm_device_t *dev)
{
int i;
@@ -195,7 +230,22 @@ static int tdfx_takedown(drm_device_t *dev)
}
dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
}
-
+#ifdef DRM_AGP
+ /* Clear AGP information */
+ if (dev->agp) {
+ drm_agp_mem_t *temp;
+ drm_agp_mem_t *temp_next;
+
+ temp = dev->agp->memory;
+ while(temp != NULL) {
+ temp_next = temp->next;
+ drm_free_agp(temp->memory, temp->pages);
+ drm_free(temp, sizeof(*temp), DRM_MEM_AGPLISTS);
+ temp = temp_next;
+ }
+ if (dev->agp->acquired) (*drm_agp.release)();
+ }
+#endif
/* Clear vma list (only built for debugging) */
if (dev->vmalist) {
for (vma = dev->vmalist; vma; vma = vma_next) {
@@ -229,6 +279,10 @@ static int tdfx_takedown(drm_device_t *dev)
- PAGE_SHIFT,
DRM_MEM_SAREA);
break;
+ case _DRM_AGP:
+ /* Do nothing here, because this is all
+ handled in the AGP/GART driver. */
+ break;
}
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
}
@@ -276,6 +330,16 @@ int tdfx_init(void)
drm_mem_init();
drm_proc_init(dev);
+#ifdef DRM_AGP
+ dev->agp = drm_agp_init();
+#endif
+ if((retcode = drm_ctxbitmap_init(dev))) {
+ DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+ drm_proc_cleanup();
+ misc_deregister(&tdfx_misc);
+ tdfx_takedown(dev);
+ return retcode;
+ }
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
TDFX_NAME,
@@ -302,7 +366,15 @@ void tdfx_cleanup(void)
} else {
DRM_INFO("Module unloaded\n");
}
+ drm_ctxbitmap_cleanup(dev);
tdfx_takedown(dev);
+#ifdef DRM_AGP
+ if (dev->agp) {
+ drm_agp_uninit();
+ drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+ dev->agp = NULL;
+ }
+#endif
}
int tdfx_version(struct inode *inode, struct file *filp, unsigned int cmd,
@@ -346,6 +418,7 @@ int tdfx_open(struct inode *inode, struct file *filp)
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_open_helper(inode, filp, dev))) {
+ MOD_INC_USE_COUNT;
atomic_inc(&dev->total_open);
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
@@ -360,13 +433,12 @@ int tdfx_open(struct inode *inode, struct file *filp)
int tdfx_release(struct inode *inode, struct file *filp)
{
drm_file_t *priv = filp->private_data;
- drm_device_t *dev;
+ drm_device_t *dev = priv->dev;
int retcode = 0;
- lock_kernel();
- dev = priv->dev;
DRM_DEBUG("open_count = %d\n", dev->open_count);
if (!(retcode = drm_release(inode, filp))) {
+ MOD_DEC_USE_COUNT;
atomic_inc(&dev->total_close);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
@@ -375,17 +447,13 @@ int tdfx_release(struct inode *inode, struct file *filp)
atomic_read(&dev->ioctl_count),
dev->blocked);
spin_unlock(&dev->count_lock);
- unlock_kernel();
return -EBUSY;
}
spin_unlock(&dev->count_lock);
- retcode = tdfx_takedown(dev);
- unlock_kernel();
- return retcode;
+ return tdfx_takedown(dev);
}
spin_unlock(&dev->count_lock);
}
- unlock_kernel();
return retcode;
}
@@ -500,7 +568,9 @@ int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
/* Contention */
atomic_inc(&dev->total_sleeps);
current->state = TASK_INTERRUPTIBLE;
+#if 1
current->policy |= SCHED_YIELD;
+#endif
schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
@@ -549,11 +619,12 @@ int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
}
}
+#if LINUX_VERSION_CODE < 0x020400
if (lock.context != tdfx_res_ctx.handle) {
current->counter = 5;
- current->nice = 0;
+ current->priority = DEF_PRIORITY/4;
}
-
+#endif
DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
#if DRM_DMA_HISTOGRAM
@@ -593,29 +664,13 @@ int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
DRM_ERROR("\n");
}
}
-
+
+#if LINUX_VERSION_CODE < 0x020400
if (lock.context != tdfx_res_ctx.handle) {
current->counter = 5;
- current->nice = 0;
+ current->priority = DEF_PRIORITY;
}
-
+#endif
+
return 0;
}
-
-module_init(tdfx_init);
-module_exit(tdfx_cleanup);
-
-#ifndef MODULE
-/*
- * tdfx_setup is called by the kernel to parse command-line options passed
- * via the boot-loader (e.g., LILO). It calls the insmod option routine,
- * drm_parse_options.
- */
-static int __init tdfx_options(char *str)
-{
- drm_parse_options(str);
- return 1;
-}
-
-__setup("tdfx=", tdfx_options);
-#endif
diff --git a/drivers/char/drm/tdfx_drv.h b/drivers/char/drm/tdfx_drv.h
index 4c0c3282b..6bfcef1d0 100644
--- a/drivers/char/drm/tdfx_drv.h
+++ b/drivers/char/drm/tdfx_drv.h
@@ -2,6 +2,7 @@
* Created: Thu Oct 7 10:40:04 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,8 +25,8 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
- * Daryll Strauss <daryll@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
+ * Daryll Strauss <daryll@valinux.com>
*
*/
diff --git a/drivers/char/drm/vm.c b/drivers/char/drm/vm.c
index b6595c88c..9dfd0d2b0 100644
--- a/drivers/char/drm/vm.c
+++ b/drivers/char/drm/vm.c
@@ -2,6 +2,7 @@
* Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
*
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,7 +25,7 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ * Rickard E. (Rik) Faith <faith@valinux.com>
*
*/
@@ -87,7 +88,7 @@ struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
offset = address - vma->vm_start;
page = offset >> PAGE_SHIFT;
- physical = (unsigned long)dev->lock.hw_lock + (offset & (~PAGE_MASK));
+ physical = (unsigned long)dev->lock.hw_lock + offset;
atomic_inc(&mem_map[MAP_NR(physical)].count); /* Dec. by kernel */
DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical);
@@ -144,6 +145,7 @@ void drm_vm_open(struct vm_area_struct *vma)
DRM_DEBUG("0x%08lx,0x%08lx\n",
vma->vm_start, vma->vm_end - vma->vm_start);
atomic_inc(&dev->vma_count);
+ MOD_INC_USE_COUNT;
#if DRM_DEBUG_CODE
vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS);
@@ -168,6 +170,7 @@ void drm_vm_close(struct vm_area_struct *vma)
DRM_DEBUG("0x%08lx,0x%08lx\n",
vma->vm_start, vma->vm_end - vma->vm_start);
+ MOD_DEC_USE_COUNT;
atomic_dec(&dev->vma_count);
#if DRM_DEBUG_CODE
@@ -244,13 +247,26 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
/* Check for valid size. */
if (map->size != vma->vm_end - vma->vm_start) return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
+ vma->vm_flags &= VM_MAYWRITE;
+#if defined(__i386__)
+ pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
+#else
+ /* Ye gads this is ugly. With more thought
+ we could move this up higher and use
+ `protection_map' instead. */
+ vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+ __pte(pgprot_val(vma->vm_page_prot)))));
+#endif
+ }
switch (map->type) {
case _DRM_FRAME_BUFFER:
case _DRM_REGISTERS:
+ case _DRM_AGP:
if (VM_OFFSET(vma) >= __pa(high_memory)) {
#if defined(__i386__)
- if (boot_cpu_data.x86 > 3) {
+ if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) {
pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
}
@@ -262,6 +278,10 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
return -EAGAIN;
+ DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx,"
+ " offset = 0x%lx\n",
+ map->type,
+ vma->vm_start, vma->vm_end, VM_OFFSET(vma));
vma->vm_ops = &drm_vm_ops;
break;
case _DRM_SHM:
@@ -274,19 +294,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
return -EINVAL; /* This should never happen. */
}
vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
- if (map->flags & _DRM_READ_ONLY) {
-#if defined(__i386__)
- pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
-#else
- /* Ye gads this is ugly. With more thought
- we could move this up higher and use
- `protection_map' instead. */
- vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
- __pte(pgprot_val(vma->vm_page_prot)))));
-#endif
- }
-
#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
/* In Linux 2.2.3 and above, this is
handled in do_mmap() in mm/mmap.c. */
diff --git a/drivers/char/hp600_keyb.c b/drivers/char/hp600_keyb.c
index 52f66ba5f..7e48ad821 100644
--- a/drivers/char/hp600_keyb.c
+++ b/drivers/char/hp600_keyb.c
@@ -1,5 +1,5 @@
/*
- * $Id: hp600_keyb.c,v 1.1 2000/06/10 21:45:30 yaegashi Exp $
+ * $Id: hp600_keyb.c,v 1.1 2000/06/10 21:45:30 yaegashi Exp $
* Copyright (C) 2000 YAEGASHI Takeshi
* HP600 keyboard scan routine and translate table
*/
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index a49e1c48e..42be56d7a 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -53,6 +53,7 @@
* this driver.)
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
diff --git a/drivers/char/scan_keyb.c b/drivers/char/scan_keyb.c
index 5bc1bdff9..7ec7034e0 100644
--- a/drivers/char/scan_keyb.c
+++ b/drivers/char/scan_keyb.c
@@ -1,5 +1,5 @@
/*
- * $Id: scan_keyb.c,v 1.1 2000/06/10 21:45:30 yaegashi Exp $
+ * $Id: scan_keyb.c,v 1.2 2000/07/04 06:24:42 yaegashi Exp $
* Copyright (C) 2000 YAEGASHI Takeshi
* Generic scan keyboard driver
*/
diff --git a/drivers/char/scan_keyb.h b/drivers/char/scan_keyb.h
index 849b50870..257024086 100644
--- a/drivers/char/scan_keyb.h
+++ b/drivers/char/scan_keyb.h
@@ -1,7 +1,7 @@
#ifndef __DRIVER_CHAR_SCAN_KEYB_H
#define __DRIVER_CHAR_SCAN_KEYB_H
/*
- * $Id: scan_keyb.h,v 1.1 2000/06/10 21:45:30 yaegashi Exp $
+ * $Id: scan_keyb.h,v 1.1 2000/06/10 21:45:30 yaegashi Exp $
* Copyright (C) 2000 YAEGASHI Takeshi
* Generic scan keyboard driver
*/
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index 05180e3df..8ec857e06 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -49,6 +49,8 @@
* 6/00: Remove old-style timer, use timer_list
* Andrew Morton <andrewm@uow.edu.au>
*
+ * 7/00: Support Timedia/Sunix/Exsys PCI cards
+ *
* This module exports the following rs232 io functions:
*
* int rs_init(void);
@@ -3795,6 +3797,7 @@ static _INLINE_ int get_pci_port(struct pci_dev *dev,
unsigned long port;
int base_idx;
int max_port;
+ int offset;
base_idx = SPCI_FL_GET_BASE(board->flags);
if (board->flags & SPCI_FL_BASE_TABLE)
@@ -3805,8 +3808,27 @@ static _INLINE_ int get_pci_port(struct pci_dev *dev,
if (idx >= max_port)
return 1;
}
-
- port = pci_resource_start(dev, base_idx) + board->first_uart_offset;
+
+ offset = board->first_uart_offset;
+
+ /* Timedia/SUNIX uses a mixture of BARs and offsets */
+ if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */
+ switch(idx) {
+ case 0: base_idx=0;
+ break;
+ case 1: base_idx=0; offset=8;
+ break;
+ case 2: base_idx=1;
+ break;
+ case 3: base_idx=1; offset=8;
+ break;
+ case 4: /* BAR 2*/
+ case 5: /* BAR 3 */
+ case 6: /* BAR 4*/
+ case 7: base_idx=idx-2; /* BAR 5*/
+ }
+
+ port = pci_resource_start(dev, base_idx) + offset;
if ((board->flags & SPCI_FL_BASE_TABLE) == 0)
port += idx * (board->uart_offset ? board->uart_offset : 8);
@@ -4247,9 +4269,75 @@ static struct pci_board pci_boards[] __initdata = {
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 },
- { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
- PCI_ANY_ID, PCI_ANY_ID,
- SPCI_FL_BASE0 , 2, 921600 },
+ /* PCI_VENDOR_ID_TIMEDIA/Sunix, PCI_DEVICE_ID_TIMEDIA_1889, */
+ { 0x1409, 0x7168, 0x1409, 0x0002, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4036A*/
+ { 0x1409, 0x7168, 0x1409, 0x4025, SPCI_FL_BASE_TABLE, 1, 921600 }, /*4025A*/
+ { 0x1409, 0x7168, 0x1409, 0x4027, SPCI_FL_BASE_TABLE, 1, 921600 }, /*4027A*/
+ { 0x1409, 0x7168, 0x1409, 0x4028, SPCI_FL_BASE_TABLE, 1, 921600 }, /*4028D*/
+ { 0x1409, 0x7168, 0x1409, 0x4036, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4036D*/
+ { 0x1409, 0x7168, 0x1409, 0x4037, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4037A*/
+ { 0x1409, 0x7168, 0x1409, 0x4038, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4038D*/
+ { 0x1409, 0x7168, 0x1409, 0x4055, SPCI_FL_BASE_TABLE, 4, 921600 }, /*4055A*/
+ { 0x1409, 0x7168, 0x1409, 0x4056, SPCI_FL_BASE_TABLE, 4, 921600 }, /*4056A*/
+ { 0x1409, 0x7168, 0x1409, 0x4065, SPCI_FL_BASE_TABLE, 8, 921600 }, /*4065A*/
+ { 0x1409, 0x7168, 0x1409, 0x4066, SPCI_FL_BASE_TABLE, 8, 921600 }, /*4066A*/
+ { 0x1409, 0x7168, 0x1409, 0x4078, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4078A*/
+ { 0x1409, 0x7168, 0x1409, 0x4079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4079H*/
+ { 0x1409, 0x7168, 0x1409, 0x4085, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4085H*/
+ { 0x1409, 0x7168, 0x1409, 0x4088, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4088A*/
+ { 0x1409, 0x7168, 0x1409, 0x4089, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4089A*/
+ { 0x1409, 0x7168, 0x1409, 0x4095, SPCI_FL_BASE_TABLE, 4, 921600 }, /*4095A*/
+ { 0x1409, 0x7168, 0x1409, 0x4096, SPCI_FL_BASE_TABLE, 4, 921600 }, /*4096A*/
+ { 0x1409, 0x7168, 0x1409, 0x5025, SPCI_FL_BASE_TABLE, 1, 921600 }, /*4025D*/
+ { 0x1409, 0x7168, 0x1409, 0x5027, SPCI_FL_BASE_TABLE, 1, 921600 }, /*4027D*/
+ { 0x1409, 0x7168, 0x1409, 0x5037, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4037D*/
+ { 0x1409, 0x7168, 0x1409, 0x5056, SPCI_FL_BASE_TABLE, 4, 921600 }, /*4056R*/
+ { 0x1409, 0x7168, 0x1409, 0x5065, SPCI_FL_BASE_TABLE, 8, 921600 }, /*4065R*/
+ { 0x1409, 0x7168, 0x1409, 0x5066, SPCI_FL_BASE_TABLE, 8, 921600 }, /*4066R*/
+ { 0x1409, 0x7168, 0x1409, 0x5078, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4078U*/
+ { 0x1409, 0x7168, 0x1409, 0x5079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4079A*/
+ { 0x1409, 0x7168, 0x1409, 0x5085, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4085U*/
+ { 0x1409, 0x7168, 0x1409, 0x6079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4079R*/
+ { 0x1409, 0x7168, 0x1409, 0x7079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4079S*/
+ { 0x1409, 0x7168, 0x1409, 0x8079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4079D*/
+ { 0x1409, 0x7168, 0x1409, 0x8137, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8137*/
+ { 0x1409, 0x7168, 0x1409, 0x8138, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8138*/
+ { 0x1409, 0x7168, 0x1409, 0x8156, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8156*/
+ { 0x1409, 0x7168, 0x1409, 0x8157, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8157*/
+ { 0x1409, 0x7168, 0x1409, 0x8166, SPCI_FL_BASE_TABLE, 8, 921600 }, /*8166*/
+ { 0x1409, 0x7168, 0x1409, 0x8237, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8237*/
+ { 0x1409, 0x7168, 0x1409, 0x8238, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8238*/
+ { 0x1409, 0x7168, 0x1409, 0x8256, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8256*/
+ { 0x1409, 0x7168, 0x1409, 0x8257, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8257*/
+ { 0x1409, 0x7168, 0x1409, 0x9056, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9056A*/
+ { 0x1409, 0x7168, 0x1409, 0x9066, SPCI_FL_BASE_TABLE, 8, 921600 }, /*9066A*/
+ { 0x1409, 0x7168, 0x1409, 0x9079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4079E*/
+ { 0x1409, 0x7168, 0x1409, 0x9137, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8137S*/
+ { 0x1409, 0x7168, 0x1409, 0x9138, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8138S*/
+ { 0x1409, 0x7168, 0x1409, 0x9156, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8156S*/
+ { 0x1409, 0x7168, 0x1409, 0x9157, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8157S*/
+ { 0x1409, 0x7168, 0x1409, 0x9158, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9158*/
+ { 0x1409, 0x7168, 0x1409, 0x9159, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9159*/
+ { 0x1409, 0x7168, 0x1409, 0x9166, SPCI_FL_BASE_TABLE, 8, 921600 }, /*8166S*/
+ { 0x1409, 0x7168, 0x1409, 0x9167, SPCI_FL_BASE_TABLE, 8, 921600 }, /*9167*/
+ { 0x1409, 0x7168, 0x1409, 0x9168, SPCI_FL_BASE_TABLE, 8, 921600 }, /*9168*/
+ { 0x1409, 0x7168, 0x1409, 0x9237, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8237S*/
+ { 0x1409, 0x7168, 0x1409, 0x9238, SPCI_FL_BASE_TABLE, 2, 921600 }, /*8238S*/
+ { 0x1409, 0x7168, 0x1409, 0x9256, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8256S*/
+ { 0x1409, 0x7168, 0x1409, 0x9257, SPCI_FL_BASE_TABLE, 4, 921600 }, /*8257S*/
+ { 0x1409, 0x7168, 0x1409, 0xA056, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9056B*/
+ { 0x1409, 0x7168, 0x1409, 0xA066, SPCI_FL_BASE_TABLE, 8, 921600 }, /*9066B*/
+ { 0x1409, 0x7168, 0x1409, 0xA079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*4079F*/
+ { 0x1409, 0x7168, 0x1409, 0xA157, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9157*/
+ { 0x1409, 0x7168, 0x1409, 0xA158, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9158S*/
+ { 0x1409, 0x7168, 0x1409, 0xA159, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9159S*/
+ { 0x1409, 0x7168, 0x1409, 0xA167, SPCI_FL_BASE_TABLE, 8, 921600 }, /*9167S*/
+ { 0x1409, 0x7168, 0x1409, 0xA168, SPCI_FL_BASE_TABLE, 8, 921600 }, /*9168S*/
+ { 0x1409, 0x7168, 0x1409, 0xB056, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9056C*/
+ { 0x1409, 0x7168, 0x1409, 0xB079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*9079A*/
+ { 0x1409, 0x7168, 0x1409, 0xB157, SPCI_FL_BASE_TABLE, 4, 921600 }, /*9157S*/
+ { 0x1409, 0x7168, 0x1409, 0xC079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*9079B*/
+ { 0x1409, 0x7168, 0x1409, 0xD079, SPCI_FL_BASE_TABLE, 2, 921600 }, /*9079C*/
{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 },
diff --git a/drivers/char/sh-sci.c b/drivers/char/sh-sci.c
index b51e91557..12ab34d72 100644
--- a/drivers/char/sh-sci.c
+++ b/drivers/char/sh-sci.c
@@ -45,21 +45,23 @@
#include <linux/generic_serial.h>
#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
-static void gdb_detach(void);
-static int in_gdb = 1;
-#define IN_GDB in_gdb
+#include <asm/sh_bios.h>
#endif
+
#include "sh-sci.h"
#ifdef CONFIG_SERIAL_CONSOLE
static struct console sercons;
-static struct sci_port* sercons_port;
+static struct sci_port* sercons_port=0;
static int sercons_baud;
#endif
/* Function prototypes */
static void sci_init_pins_sci(struct sci_port* port, unsigned int cflag);
+#ifndef SCI_ONLY
static void sci_init_pins_scif(struct sci_port* port, unsigned int cflag);
+#endif
+static void sci_init_pins_irda(struct sci_port* port, unsigned int cflag);
static void sci_disable_tx_interrupts(void *ptr);
static void sci_enable_tx_interrupts(void *ptr);
static void sci_disable_rx_interrupts(void *ptr);
@@ -86,6 +88,117 @@ int sci_debug = 0;
MODULE_PARM(sci_debug, "i");
#endif
+static void put_char(struct sci_port *port, char c)
+{
+ unsigned long flags;
+ unsigned short status;
+
+ save_and_cli(flags);
+
+ do
+ status = sci_in(port, SCxSR);
+ while (!(status & SCxSR_TDxE(port)));
+
+ sci_out(port, SCxTDR, c);
+ sci_in(port, SCxSR); /* Dummy read */
+ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+
+ restore_flags(flags);
+}
+
+static void handle_error(struct sci_port *port)
+{ /* Clear error flags */
+ sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
+}
+
+static int get_char(struct sci_port *port)
+{
+ unsigned long flags;
+ unsigned short status;
+ int c;
+
+ save_and_cli(flags);
+ do {
+ status = sci_in(port, SCxSR);
+ if (status & SCxSR_ERRORS(port)) {
+ handle_error(port);
+ continue;
+ }
+ } while (!(status & SCxSR_RDxF(port)));
+ c = sci_in(port, SCxRDR);
+ sci_in(port, SCxSR); /* Dummy read */
+ sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+ restore_flags(flags);
+
+ return c;
+}
+
+#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
+
+/* Taken from sh-stub.c of GDB 4.18 */
+static const char hexchars[] = "0123456789abcdef";
+
+static __inline__ char highhex(int x)
+{
+ return hexchars[(x >> 4) & 0xf];
+}
+
+static __inline__ char lowhex(int x)
+{
+ return hexchars[x & 0xf];
+}
+
+#endif
+
+/*
+ * Send the packet in buffer. The host gets one chance to read it.
+ * This routine does not wait for a positive acknowledge.
+ */
+
+static void put_string(struct sci_port *port,
+ const char *buffer, int count)
+{
+ int i;
+ const unsigned char *p = buffer;
+#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
+ int checksum;
+
+ /* This call only does a trap the first time it is
+ * called, and so is safe to do here unconditionally
+ */
+ if (sh_bios_in_gdb_mode()) {
+ /* $<packet info>#<checksum>. */
+ do {
+ unsigned char c;
+ put_char(port, '$');
+ put_char(port, 'O'); /* 'O'utput to console */
+ checksum = 'O';
+
+ for (i=0; i<count; i++) { /* Don't use run length encoding */
+ int h, l;
+
+ c = *p++;
+ h = highhex(c);
+ l = lowhex(c);
+ put_char(port, h);
+ put_char(port, l);
+ checksum += h + l;
+ }
+ put_char(port, '#');
+ put_char(port, highhex(checksum));
+ put_char(port, lowhex(checksum));
+ } while (get_char(port) != '+');
+ } else
+#endif
+ for (i=0; i<count; i++) {
+ if (*p == 10)
+ put_char(port, '\r');
+ put_char(port, *p++);
+ }
+}
+
+
+
static struct real_driver sci_real_driver = {
sci_disable_tx_interrupts,
sci_enable_tx_interrupts,
@@ -139,6 +252,16 @@ static void sci_init_pins_scif(struct sci_port* port, unsigned int cflag)
sci_out(port, SCFCR, fcr_val);
}
+static void sci_init_pins_irda(struct sci_port* port, unsigned int cflag)
+{
+ unsigned int fcr_val = 0;
+
+ if (cflag & CRTSCTS)
+ fcr_val |= SCFCR_MCE;
+
+ sci_out(port, SCFCR, fcr_val);
+}
+
#else
/* For SH7750 */
@@ -359,7 +482,7 @@ static void sci_transmit_chars(struct sci_port *port)
} else {
if (port->type == PORT_SCIF) {
sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCIF_TDFE);
+ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
}
ctrl |= SCI_CTRL_FLAGS_TIE;
}
@@ -715,7 +838,7 @@ static int sci_read_proc(char *page, char **start, off_t off, int count,
struct sci_port *port;
int len = 0;
- len += sprintf(page, "serinfo:1.0\n");
+ len += sprintf(page, "sciinfo:0.1\n");
for (i = 0; i < SCI_NPORTS && len < 4000; i++) {
port = &sci_ports[i];
len += sprintf(page+len, "%d: uart:%s address: %08x\n", i,
@@ -737,8 +860,8 @@ static int sci_init_drivers(void)
memset(&sci_driver, 0, sizeof(sci_driver));
sci_driver.magic = TTY_DRIVER_MAGIC;
- sci_driver.driver_name = "serial";
- sci_driver.name = "ttyS";
+ sci_driver.driver_name = "sci";
+ sci_driver.name = "ttySC";
sci_driver.major = SCI_MAJOR;
sci_driver.minor_start = SCI_MINOR_START;
sci_driver.num = SCI_NPORTS;
@@ -774,7 +897,7 @@ static int sci_init_drivers(void)
sci_callout_driver = sci_driver;
sci_callout_driver.name = "cusc";
- sci_callout_driver.major = SCI_MAJOR + 1;
+ sci_callout_driver.major = SCI_MAJOR+1;
sci_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
sci_callout_driver.read_proc = NULL;
@@ -808,17 +931,22 @@ static int sci_init_drivers(void)
int __init sci_init(void)
{
struct sci_port *port;
- int i;
+ int i, j;
void (*handlers[3])(int irq, void *ptr, struct pt_regs *regs) = {
sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt
};
- for (port = &sci_ports[0]; port < &sci_ports[SCI_NPORTS]; port++) {
+ printk("SuperH SCI(F) driver initialized\n");
+
+ for (j=0; j<SCI_NPORTS; j++) {
+ port = &sci_ports[j];
+ printk("ttySC%d at 0x%08x is a %s\n", j, port->base,
+ (port->type == PORT_SCI) ? "SCI" : "SCIF");
for (i=0; i<3; i++) {
set_ipr_data(port->irqs[i], port->intc_addr, port->intc_pos, SCI_PRIORITY);
if (request_irq(port->irqs[i], handlers[i], SA_INTERRUPT,
- "serial", port)) {
+ "sci", port)) {
printk(KERN_ERR "sci: Cannot allocate irq.\n");
return -ENODEV;
}
@@ -829,7 +957,7 @@ int __init sci_init(void)
sci_init_drivers();
#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
- gdb_detach();
+ sh_bios_gdb_detach();
#endif
return 0; /* Return -EIO when not detected */
}
@@ -856,27 +984,6 @@ void cleanup_module(void)
#ifdef CONFIG_SERIAL_CONSOLE
/*
- * ------------------------------------------------------------
- * Serial console driver for SH-3/SH-4 SCI (with no FIFO)
- * ------------------------------------------------------------
- */
-
-#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
-
-static void __init gdb_detach(void)
-{
- asm volatile("trapa #0xff");
-
- if (in_gdb == 1) {
- in_gdb = 0;
- get_char(sercons_port);
- put_char(sercons_port, '\r');
- put_char(sercons_port, '\n');
- }
-}
-#endif
-
-/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*/
@@ -992,8 +1099,18 @@ static struct console sercons = {
* Register console.
*/
-void __init serial_console_init(void)
+#ifdef CONFIG_SH_EARLY_PRINTK
+extern void sh_console_unregister (void);
+#endif
+
+void __init sci_console_init(void)
{
register_console(&sercons);
+#ifdef CONFIG_SH_EARLY_PRINTK
+ /* Now that the real console is available, unregister the one we
+ * used while first booting.
+ */
+ sh_console_unregister();
+#endif
}
#endif /* CONFIG_SERIAL_CONSOLE */
diff --git a/drivers/char/sh-sci.h b/drivers/char/sh-sci.h
index ddf5712a5..b9ecad78b 100644
--- a/drivers/char/sh-sci.h
+++ b/drivers/char/sh-sci.h
@@ -13,6 +13,7 @@
/* Values for sci_port->type */
#define PORT_SCI 0
#define PORT_SCIF 1
+#define PORT_IRDA 1 /* XXX: temporary assignment */
/* Offsets into the sci_port->irqs array */
#define SCIx_ERI_IRQ 0
@@ -22,6 +23,7 @@
/* ERI, RXI, TXI, INTC reg, INTC pos */
#define SCI_IRQS { 23, 24, 25 }, INTC_IPRB, 1
#define SH3_SCIF_IRQS { 56, 57, 59 }, INTC_IPRE, 1
+#define SH3_IRDA_IRQS { 52, 53, 55 }, INTC_IPRE, 2
#define SH4_SCIF_IRQS { 40, 41, 43 }, INTC_IPRC, 1
#if defined(CONFIG_CPU_SUBTYPE_SH7708)
@@ -33,10 +35,11 @@
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
# define SCI_ONLY
#elif defined(CONFIG_CPU_SUBTYPE_SH7709)
-# define SCI_NPORTS 2
+# define SCI_NPORTS 3
# define SCI_INIT { \
- { {}, PORT_SCI, 0xfffffe80, SCI_IRQS, sci_init_pins_sci }, \
- { {}, PORT_SCIF, 0xA4000150, SH3_SCIF_IRQS, sci_init_pins_scif } \
+ { {}, PORT_SCI, 0xfffffe80, SCI_IRQS, sci_init_pins_sci }, \
+ { {}, PORT_SCIF, 0xA4000150, SH3_SCIF_IRQS, sci_init_pins_scif }, \
+ { {}, PORT_SCIF, 0xA4000140, SH3_IRDA_IRQS, sci_init_pins_irda } \
}
# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */
# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */
@@ -127,7 +130,7 @@
#define SCI_PRIORITY 3
-#define SCI_MAJOR 204
+#define SCI_MAJOR 204
#define SCI_MINOR_START 8
/* Generic serial flags */
@@ -261,11 +264,20 @@ SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
* scaling the value which is needed in SCBRR.
*
* -- Stuart Menefy - 23 May 2000
+ *
+ * I meant, why would anyone bother with bitrates below 2400.
+ *
+ * -- Greg Banks - 7Jul2000
+ *
+ * You "speedist"! How will I use my 110bps ASR-33 teletype with paper
+ * tape reader as a console!
+ *
+ * -- Mitch Davis - 15 Jul 2000
*/
#define PCLK (current_cpu_data.module_clock)
-#define SCBRR_VALUE(bps) (PCLK/(32*bps)-1)
+#define SCBRR_VALUE(bps) ((PCLK+16*bps)/(32*bps)-1)
#define BPS_2400 SCBRR_VALUE(2400)
#define BPS_4800 SCBRR_VALUE(4800)
#define BPS_9600 SCBRR_VALUE(9600)
@@ -273,106 +285,3 @@ SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
#define BPS_38400 SCBRR_VALUE(38400)
#define BPS_115200 SCBRR_VALUE(115200)
-#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
-/* Taken from sh-stub.c of GDB 4.18 */
-static const char hexchars[] = "0123456789abcdef";
-
-static __inline__ char highhex(int x)
-{
- return hexchars[(x >> 4) & 0xf];
-}
-
-static __inline__ char lowhex(int x)
-{
- return hexchars[x & 0xf];
-}
-#endif
-
-static __inline__ void put_char(struct sci_port *port, char c)
-{
- unsigned long flags;
- unsigned short status;
-
- save_and_cli(flags);
-
- do
- status = sci_in(port, SCxSR);
- while (!(status & SCxSR_TDxE(port)));
-
- sci_out(port, SCxTDR, c);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
-
- restore_flags(flags);
-}
-
-static __inline__ void handle_error(struct sci_port *port)
-{ /* Clear error flags */
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
-}
-
-static __inline__ int get_char(struct sci_port *port)
-{
- unsigned long flags;
- unsigned short status;
- int c;
-
- save_and_cli(flags);
- do {
- status = sci_in(port, SCxSR);
- if (status & SCxSR_ERRORS(port)) {
- handle_error(port);
- continue;
- }
- } while (!(status & SCxSR_RDxF(port)));
- c = sci_in(port, SCxRDR);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- restore_flags(flags);
-
- return c;
-}
-
-/*
- * Send the packet in buffer. The host get's one chance to read it.
- * This routine does not wait for a positive acknowledge.
- */
-
-static __inline__ void put_string(struct sci_port *port,
- const char *buffer, int count)
-{
- int i;
- const unsigned char *p = buffer;
-#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
- int checksum;
-
-if (IN_GDB) {
- /* $<packet info>#<checksum>. */
- do {
- unsigned char c;
- put_char(port, '$');
- put_char(port, 'O'); /* 'O'utput to console */
- checksum = 'O';
-
- for (i=0; i<count; i++) { /* Don't use run length encoding */
- int h, l;
-
- c = *p++;
- h = highhex(c);
- l = lowhex(c);
- put_char(port, h);
- put_char(port, l);
- checksum += h + l;
- }
- put_char(port, '#');
- put_char(port, highhex(checksum));
- put_char(port, lowhex(checksum));
- } while (get_char(port) != '+');
-} else
-#endif
- for (i=0; i<count; i++) {
- if (*p == 10)
- put_char(port, '\r');
- put_char(port, *p++);
- }
-}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 7b42d645f..4e8d4db73 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -151,6 +151,7 @@ extern void con3215_init(void);
extern void rs285_console_init(void);
extern void sa1100_rs_console_init(void);
extern void sgi_serial_console_init(void);
+extern void sci_console_init(void);
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -2181,6 +2182,9 @@ void __init console_init(void)
#if defined(CONFIG_SERIAL167)
serial167_console_init();
#endif
+#if defined(CONFIG_SH_SCI)
+ sci_console_init();
+#endif
#endif
#ifdef CONFIG_3215
con3215_init();
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index bd89fcc61..298603394 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -1539,7 +1539,7 @@ static idetape_pc_t *idetape_next_pc_storage (ide_drive_t *drive)
/**************************************************************
* *
- * This should get fixed to use kmalloc(GFP_ATOMIC, ..) *
+ * This should get fixed to use kmalloc(.., GFP_ATOMIC) *
* followed later on by kfree(). -ml *
* *
**************************************************************/
diff --git a/drivers/parport/daisy.c b/drivers/parport/daisy.c
index 065bcf8df..573c5ef20 100644
--- a/drivers/parport/daisy.c
+++ b/drivers/parport/daisy.c
@@ -14,10 +14,9 @@
* 13-03-1999: Get DeviceID from non-IEEE 1284.3 devices too.
* 22-02-2000: Count devices that are actually detected.
*
- * The block comments above the functions in this file are
- * licensed as part of the generated file
- * Documentation/DocBook/parportbook.sgml under the GNU Free
- * Documentation License.
+ * Any part of this program may be used in documents licensed under
+ * the GNU Free Documentation License, Version 1.1 or any later version
+ * published by the Free Software Foundation.
*/
#include <linux/parport.h>
@@ -51,7 +50,7 @@ static int assign_addrs (struct parport *port);
static void add_dev (int devnum, struct parport *port, int daisy)
{
struct daisydev *newdev;
- newdev = kmalloc (GFP_KERNEL, sizeof (struct daisydev));
+ newdev = kmalloc (sizeof (struct daisydev), GFP_KERNEL);
if (newdev) {
newdev->port = port;
newdev->daisy = daisy;
diff --git a/drivers/parport/ieee1284.c b/drivers/parport/ieee1284.c
index c148a5b0c..0d84fd1a1 100644
--- a/drivers/parport/ieee1284.c
+++ b/drivers/parport/ieee1284.c
@@ -9,10 +9,9 @@
* This file is responsible for IEEE 1284 negotiation, and for handing
* read/write requests to low-level drivers.
*
- * The block comments above the functions in this file are
- * licensed as part of the generated file
- * Documentation/DocBook/parportbook.sgml under the GNU Free
- * Documentation License.
+ * Any part of this program may be used in documents licensed under
+ * the GNU Free Documentation License, Version 1.1 or any later version
+ * published by the Free Software Foundation.
*/
#include <linux/config.h>
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 92618e7b4..c4c6a44b3 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -10,10 +10,9 @@
* based on work by Grant Guenther <grant@torque.net>
* and Philip Blundell
*
- * The block comments above the functions in this file are
- * licensed as part of the generated file
- * Documentation/DocBook/parportbook.sgml under the GNU Free
- * Documentation License.
+ * Any part of this program may be used in documents licensed under
+ * the GNU Free Documentation License, Version 1.1 or any later version
+ * published by the Free Software Foundation.
*/
#undef PARPORT_DEBUG_SHARING /* undef for production */
diff --git a/drivers/s390/misc/chandev.c b/drivers/s390/misc/chandev.c
index 4345f1211..f2b842e16 100644
--- a/drivers/s390/misc/chandev.c
+++ b/drivers/s390/misc/chandev.c
@@ -31,7 +31,7 @@ static int chandev_conf_read=FALSE;
static void *chandev_alloc_listmember(list **listhead,size_t size)
{
- void *newmember=kmalloc(GFP_KERNEL,size);
+ void *newmember=kmalloc(size, GFP_KERNEL);
if(newmember)
add_to_list(listhead,newmember);
return(newmember);
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index b3b8d17fb..ebe4e33ba 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -2,6 +2,8 @@
3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
Written By: Adam Radford <linux@3ware.com>
+ Modifications By: Joel Jacobson <linux@3ware.com>
+
Copyright (C) 1999-2000 3ware Inc.
Kernel compatablity By: Andre Hedrick <andre@suse.com>
@@ -47,6 +49,19 @@
For more information, goto:
http://www.3ware.com
+
+ History
+ -------
+ 0.1.000 - Initial release.
+ 0.4.000 - Added support for Asynchronous Event Notification through
+ ioctls for 3DM.
+ 1.0.000 - Added DPO & FUA bit support for WRITE_10 & WRITE_6 cdb
+ to disable drive write-cache before writes.
+ 1.1.000 - Fixed performance bug with DPO & FUA not existing for WRITE_6.
+ 1.2.000 - Added support for clean shutdown notification/feature table.
+ 1.02.00.001 - Added support for full command packet posts through ioctls
+ for 3DM.
+ Bug fix so hot spare drives don't show up.
*/
#include <linux/module.h>
@@ -65,6 +80,7 @@ MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/smp.h>
+#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <asm/errno.h>
@@ -83,9 +99,15 @@ MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");
static int tw_copy_info(TW_Info *info, char *fmt, ...);
static void tw_copy_mem_info(TW_Info *info, char *data, int len);
static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int tw_halt(struct notifier_block *nb, ulong event, void *buf);
+
+/* Notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block tw_notifier = {
+ tw_halt, NULL, 0
+};
/* Globals */
-char *tw_driver_version="1.1.000";
+char *tw_driver_version="1.02.00.001";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
@@ -224,7 +246,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
if (command_packet->status != 0) {
if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
/* Bad response */
- printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
return 1;
} else {
/* We know this is a 3w-1x00, and doesn't support aen's */
@@ -562,6 +584,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
TW_Device_Extension *tw_dev2;
struct pci_dev *tw_pci_dev = NULL;
u32 status_reg_value;
+ unsigned char c = 1;
dprintk(KERN_NOTICE "3w-xxxx: tw_findcards()\n");
while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, TW_DEVICE_ID, tw_pci_dev))) {
@@ -662,7 +685,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
continue;
}
- error = tw_initconnection(tw_dev);
+ error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
@@ -722,10 +745,15 @@ int tw_findcards(Scsi_Host_Template *tw_host)
/* Free the temporary device extension */
if (tw_dev)
kfree(tw_dev);
+
+ /* Tell the firmware we support shutdown notification*/
+ tw_setfeature(tw_dev, 2, 1, &c);
}
if (numcards == 0)
printk(KERN_WARNING "3w-xxxx: tw_findcards(): No cards found.\n");
+ else
+ register_reboot_notifier(&tw_notifier);
return numcards;
} /* End tw_findcards() */
@@ -747,8 +775,22 @@ void tw_free_device_extension(TW_Device_Extension *tw_dev)
}
} /* End tw_free_device_extension() */
+/* Clean shutdown routine */
+static int tw_halt(struct notifier_block *nb, ulong event, void *buf)
+{
+ int i;
+
+ for (i=0;i<tw_device_extension_count;i++) {
+ printk(KERN_NOTICE "3w-xxxx: Notifying card #%d\n", i);
+ tw_shutdown_device(tw_device_extension_list[i]);
+ }
+ unregister_reboot_notifier(&tw_notifier);
+
+ return NOTIFY_OK;
+} /* End tw_halt() */
+
/* This function will send an initconnection command to controller */
-int tw_initconnection(TW_Device_Extension *tw_dev)
+int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
{
u32 command_que_addr, command_que_value;
u32 status_reg_addr, status_reg_value;
@@ -780,7 +822,7 @@ int tw_initconnection(TW_Device_Extension *tw_dev)
command_packet->byte3.host_id = 0x0;
command_packet->status = 0x0;
command_packet->flags = 0x0;
- command_packet->byte6.message_credits = TW_INIT_MESSAGE_CREDITS;
+ command_packet->byte6.message_credits = message_credits;
command_packet->byte8.init_connection.response_queue_pointer = 0x0;
command_que_value = tw_dev->command_packet_physical_address[request_id];
@@ -811,7 +853,7 @@ int tw_initconnection(TW_Device_Extension *tw_dev)
}
if (command_packet->status != 0) {
/* bad response */
- printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
return 1;
}
break; /* Response was okay, so we exit */
@@ -959,7 +1001,7 @@ int tw_initialize_units(TW_Device_Extension *tw_dev)
}
if (command_packet->status != 0) {
/* bad response */
- printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
return 1;
}
found = 1;
@@ -981,9 +1023,11 @@ int tw_initialize_units(TW_Device_Extension *tw_dev)
if (is_unit_present[i] == 0) {
tw_dev->is_unit_present[i] = FALSE;
} else {
+ if (is_unit_present[i] & TW_UNIT_ONLINE) {
dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units(): Unit %d found.\n", i);
tw_dev->is_unit_present[i] = TRUE;
num_units++;
+ }
}
}
tw_dev->num_units = num_units;
@@ -1089,7 +1133,7 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
request_id = response_que.u.response_id;
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
if (command_packet->status != 0) {
- printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
}
if (tw_dev->state[request_id] != TW_S_POSTED) {
printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", request_id, command_packet->byte0.opcode);
@@ -1186,7 +1230,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
/* Initialize command packet */
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
if (command_packet == NULL) {
- printk(KERN_WARNING "3w-xxxx: twioctl(): Bad command packet virtual address.\n");
+ printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad command packet virtual address.\n");
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->srb[request_id]->result = (DID_OK << 16);
@@ -1256,6 +1300,12 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
tw_dev->srb[request_id]->result = (DID_OK << 16);
tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
return 0;
+ case TW_CMD_PACKET:
+ memcpy(command_packet, ioctl->data, sizeof(TW_Command));
+ command_packet->request_id = request_id;
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
default:
printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -1480,7 +1530,7 @@ int tw_reset_sequence(TW_Device_Extension *tw_dev)
return 1;
}
- error = tw_initconnection(tw_dev);
+ error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Couldn't initconnection for card %d.\n", tw_dev->host->host_no);
return 1;
@@ -1598,7 +1648,6 @@ int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt)
int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt)
{
TW_Device_Extension *tw_dev=NULL;
- int flags = 0;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_eh_reset()\n");
@@ -1613,17 +1662,17 @@ int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt)
return (FAILED);
}
- spin_lock_irqsave(&tw_dev->tw_lock, flags);
+ spin_lock(&tw_dev->tw_lock);
tw_dev->num_resets++;
/* Now reset the card and some of the device extension data */
if (tw_reset_device_extension(tw_dev)) {
printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset failed for card %d.\n", tw_dev->host->host_no);
- spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ spin_unlock(&tw_dev->tw_lock);
return (FAILED);
}
printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset succeeded for card %d.\n", tw_dev->host->host_no);
- spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+ spin_unlock(&tw_dev->tw_lock);
return (SUCCESS);
} /* End tw_scsi_eh_reset() */
@@ -1803,6 +1852,11 @@ int tw_scsi_release(struct Scsi_Host *tw_host)
/* Tell kernel scsi-layer we are gone */
scsi_unregister(tw_host);
+
+ /* Fake like we just shut down, so notify the card that
+ * we "shut down cleanly".
+ */
+ tw_halt(0, 0, 0); // parameters aren't actually used
return 0;
} /* End tw_scsi_release() */
@@ -1901,8 +1955,10 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
if (is_unit_present[i] == 0) {
tw_dev->is_unit_present[i] = FALSE;
} else {
+ if (is_unit_present[i] & TW_UNIT_ONLINE) {
tw_dev->is_unit_present[i] = TRUE;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete: Unit %d found.\n", i);
+ }
}
}
@@ -2124,6 +2180,92 @@ int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
return 0;
} /* End tw_scsiop_test_unit_ready() */
+/* Set a value in the features table */
+int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
+ unsigned char *val)
+{
+ TW_Param *param;
+ TW_Command *command_packet;
+ TW_Response_Queue response_queue;
+ int request_id = 0;
+ u32 command_que_value, command_que_addr;
+ u32 status_reg_addr, status_reg_value;
+ u32 response_que_addr;
+ u32 param_value;
+ int imax, i;
+
+ /* Initialize SetParam command packet */
+ if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ memset(command_packet, 0, sizeof(TW_Sector));
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+
+ command_packet->byte0.opcode = TW_OP_SET_PARAM;
+ command_packet->byte0.sgl_offset = 2;
+ param->table_id = 0x404; /* Features table */
+ param->parameter_id = parm;
+ param->parameter_size_bytes = param_size;
+ memcpy(param->data, val, param_size);
+
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad alignment physical address.\n");
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ tw_dev->srb[request_id]->result = (DID_OK << 16);
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ }
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->byte6.parameter_count = 1;
+
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet physical address.\n");
+ return 1;
+ }
+ command_que_addr = tw_dev->registers.command_que_addr;
+ status_reg_addr = tw_dev->registers.status_reg_addr;
+ response_que_addr = tw_dev->registers.response_que_addr;
+
+ /* Send command packet to the board */
+ outl(command_que_value, command_que_addr);
+
+ /* Poll for completion */
+ imax = TW_POLL_MAX_RETRIES;
+ for (i=0;i<imax;i++) {
+ mdelay(10);
+ status_reg_value = inl(status_reg_addr);
+ if (tw_check_bits(status_reg_value)) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected bits.\n");
+ return 1;
+ }
+ if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+ response_queue.value = inl(response_que_addr);
+ request_id = (unsigned char)response_queue.u.response_id;
+ if (request_id != 0) {
+ /* unexpected request id */
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected request id.\n");
+ return 1;
+ }
+ if (command_packet->status != 0) {
+ /* bad response */
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
+ return 1;
+ }
+ break; /* Response was okay, so we exit */
+ }
+ }
+
+ return 0;
+} /* End tw_setfeature() */
+
/* This function will setup the interrupt handler */
int tw_setup_irq(TW_Device_Extension *tw_dev)
{
@@ -2140,6 +2282,29 @@ int tw_setup_irq(TW_Device_Extension *tw_dev)
return 0;
} /* End tw_setup_irq() */
+/* This function will tell the controller we're shutting down by sending
+ initconnection with a 1 */
+int tw_shutdown_device(TW_Device_Extension *tw_dev)
+{
+ int error;
+
+ /* Disable interrupts */
+ tw_disable_interrupts(tw_dev);
+
+ /* poke the board */
+ error = tw_initconnection(tw_dev, 1);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: tw_shutdown_device(): Couldn't initconnection for card %d.\n", tw_dev->host->host_no);
+ } else {
+ printk(KERN_NOTICE "3w-xxxx shutdown succeeded\n");
+ }
+
+ /* Re-enable interrupts */
+ tw_enable_interrupts(tw_dev);
+
+ return 0;
+} /* End tw_shutdown_device() */
+
/* This function will soft reset the controller */
void tw_soft_reset(TW_Device_Extension *tw_dev)
{
diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h
index f07ad658d..68d29fae7 100644
--- a/drivers/scsi/3w-xxxx.h
+++ b/drivers/scsi/3w-xxxx.h
@@ -2,6 +2,8 @@
3w-xxxx.h -- 3ware Storage Controller device driver for Linux.
Written By: Adam Radford <linux@3ware.com>
+ Modifications By: Joel Jacobson <linux@3ware.com>
+
Copyright (C) 1999 3ware Inc.
Kernel compatablity By: Andre Hedrick <andre@suse.com>
@@ -106,6 +108,7 @@
#define TW_OP_SET_PARAM 0x13
#define TW_OP_SECTOR_INFO 0x1a
#define TW_OP_AEN_LISTEN 0x1c
+#define TW_CMD_PACKET 0x1d
/* Asynchronous Event Notification (AEN) Codes */
#define TW_AEN_QUEUE_EMPTY 0x0000
@@ -135,7 +138,7 @@
#define TW_BLOCK_SIZE 0x200 /* 512-byte blocks */
#define TW_IOCTL 0x80
#define TW_MAX_AEN_TRIES 100
-
+#define TW_UNIT_ONLINE 1
#define TW_IN_INTR 1
/* Macros */
@@ -303,7 +306,7 @@ int tw_empty_response_que(TW_Device_Extension *tw_dev);
void tw_enable_interrupts(TW_Device_Extension *tw_dev);
int tw_findcards(Scsi_Host_Template *tw_host);
void tw_free_device_extension(TW_Device_Extension *tw_dev);
-int tw_initconnection(TW_Device_Extension *tw_dev);
+int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits);
int tw_initialize_device_extension(TW_Device_Extension *tw_dev);
int tw_initialize_units(TW_Device_Extension *tw_dev);
int tw_ioctl(TW_Device_Extension *tw_dev, int request_id);
@@ -326,7 +329,10 @@ int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id);
+int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
+ unsigned char *val);
int tw_setup_irq(TW_Device_Extension *tw_dev);
+int tw_shutdown_device(TW_Device_Extension *tw_dev);
void tw_soft_reset(TW_Device_Extension *tw_dev);
int tw_state_request_finish(TW_Device_Extension *tw_dev,int request_id);
int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id);
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a82cbd4dd..3494e793e 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -1361,7 +1361,7 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt)
SCpnt->done(SCpnt);
}
-#ifdef CONFIG_MODULES
+#if defined(CONFIG_MODULES) || defined(CONFIG_BLK_DEV_IDESCSI) || defined(CONFIG_USB_STORAGE)
static int scsi_register_host(Scsi_Host_Template *);
static void scsi_unregister_host(Scsi_Host_Template *);
#endif
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
index 51229c0c0..1edbfcb44 100644
--- a/drivers/usb/Config.in
+++ b/drivers/usb/Config.in
@@ -36,12 +36,12 @@ comment 'USB Devices'
dep_tristate ' USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB
if [ "$CONFIG_USB_SERIAL" != "n" ]; then
bool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC
- bool ' USB Handspring Visor Driver' CONFIG_USB_SERIAL_VISOR
+ dep_tristate ' USB Handspring Visor Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL
if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
- bool ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT
- bool ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO
- bool ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA
- bool ' USB Keyspan USA-xxx Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN
+ dep_tristate ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT $CONFIG_USB_SERIAL
+ dep_tristate ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL
+ dep_tristate ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA $CONFIG_USB_SERIAL
+ dep_tristate ' USB Keyspan USA-xxx Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN $CONFIG_USB_SERIAL
if [ "$CONFIG_USB_SERIAL_KEYSPAN" != "n" ]; then
bool ' USB Keyspan USA-28 Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28
bool ' USB Keyspan USA-28X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA28X
@@ -49,8 +49,8 @@ comment 'USB Devices'
bool ' USB Keyspan USA-18X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA18X
bool ' USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W
fi
- bool ' USB Digi International AccelePort USB Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_DIGI_ACCELEPORT
- bool ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET
+ dep_tristate ' USB Digi International AccelePort USB Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_DIGI_ACCELEPORT $CONFIG_USB_SERIAL
+ dep_tristate ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL
fi
bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG
fi
diff --git a/drivers/usb/devices.c b/drivers/usb/devices.c
index df02bcf2a..86b208d84 100644
--- a/drivers/usb/devices.c
+++ b/drivers/usb/devices.c
@@ -136,9 +136,11 @@ static const struct class_info clas_info[] =
{USB_CLASS_COMM, "comm."},
{USB_CLASS_HID, "HID"},
{USB_CLASS_HUB, "hub"},
+ {USB_CLASS_PHYSICAL, "phys."},
{USB_CLASS_PRINTER, "print"},
{USB_CLASS_MASS_STORAGE, "stor."},
{USB_CLASS_DATA, "data"},
+ {USB_CLASS_APP_SPEC, "app."},
{USB_CLASS_VENDOR_SPEC, "vend."},
{-1, "unk."} /* leave as last */
};
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 28001ce2f..146090ab2 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -3,30 +3,41 @@
#
O_TARGET := usb-serial.o
-M_OBJS := usb-serial.o
-O_OBJS := usbserial.o
+M_OBJS :=
+O_OBJS :=
MOD_LIST_NAME := USB_SERIAL_MODULES
-ifeq ($(CONFIG_USB_SERIAL_VISOR),y)
- O_OBJS += visor.o
-endif
-ifeq ($(CONFIG_USB_SERIAL_WHITEHEAT),y)
- O_OBJS += whiteheat.o
-endif
-ifeq ($(CONFIG_USB_SERIAL_FTDI_SIO),y)
- O_OBJS += ftdi_sio.o
-endif
-ifeq ($(CONFIG_USB_SERIAL_KEYSPAN_PDA),y)
- O_OBJS += keyspan_pda.o
-endif
-ifeq ($(CONFIG_USB_SERIAL_KEYSPAN),y)
- O_OBJS += keyspan.o
-endif
-ifeq ($(CONFIG_USB_SERIAL_OMNINET),y)
- O_OBJS += omninet.o
+# Object file lists.
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+ifeq ($(CONFIG_USB_SERIAL),y)
+ obj-y += usbserial.o
endif
-ifeq ($(CONFIG_USB_SERIAL_DIGI_ACCELEPORT),y)
- O_OBJS += digi_acceleport.o
+ifeq ($(CONFIG_USB_SERIAL),m)
+ obj-m += usbserial.o
endif
+
+obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o
+obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o
+obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
+obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o
+obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
+obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT) += digi_acceleport.o
+# Objects that export symbols.
+export-objs := usbserial.o
+
+# Translate to Rules.make lists.
+
+O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y)))
+OX_OBJS := $(sort $(filter $(export-objs), $(obj-y)))
+M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+
include $(TOPDIR)/Rules.make
+
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 4355f3773..9ecd1370b 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -14,6 +14,22 @@
* Peter Berger (pberger@brimson.com)
* Al Borchers (borchers@steinerpoint.com)
*
+* (7/15/2000) borchers
+* -- Fixed race in open when a close is in progress.
+* -- Keep count of opens and dec the module use count for each
+* outstanding open when shutdown is called (on disconnect).
+* -- Fixed sanity checks in read_bulk_callback and write_bulk_callback
+* so pointers are checked before use.
+* -- Split read bulk callback into in band and out of band
+* callbacks, and no longer restart read chains if there is
+* a status error or a sanity error. This fixed the seg
+* faults and other errors we used to get on disconnect.
+* -- Port->active is once again a flag, not a count, as it was
+* intended by usb-serial. Since it was only a char it would
+* have been limited to 256 simultaneous opens. Now the open
+* count is kept in the port private structure in dp_open_count.
+* -- Added code for modularization of the digi_acceleport driver.
+*
* (6/27/2000) pberger and borchers
* -- Zeroed out sync field in the wakeup_task before first use;
* otherwise the uninitialized value might prevent the task from
@@ -141,7 +157,7 @@
* - Following Documentation/DocBook/kernel-locking.pdf no spin locks
* are held when calling copy_to/from_user or printk.
*
-* $Id: digi_acceleport.c,v 1.63 2000/06/28 18:28:31 root Exp root $
+* $Id: digi_acceleport.c,v 1.5 2000/07/18 04:52:43 root Exp $
*/
#include <linux/config.h>
@@ -325,8 +341,9 @@ typedef struct digi_private {
int dp_buf_len;
unsigned char dp_buf[DIGI_PORT_BUF_LEN];
unsigned int dp_modem_signals;
+ int dp_open_count; /* inc on open, dec on close */
int dp_transmit_idle;
- int dp_in_close;
+ int dp_in_close; /* close in progress */
wait_queue_head_t dp_close_wait; /* wait queue for close */
struct tq_struct dp_wakeup_task;
} digi_private_t;
@@ -361,7 +378,8 @@ static int digi_startup_device( struct usb_serial *serial );
static int digi_startup( struct usb_serial *serial );
static void digi_shutdown( struct usb_serial *serial );
static void digi_read_bulk_callback( struct urb *urb );
-static void digi_read_oob_callback( struct urb *urb );
+static int digi_read_inb_callback( struct urb *urb );
+static int digi_read_oob_callback( struct urb *urb );
/* Statics */
@@ -1135,14 +1153,21 @@ static void digi_write_bulk_callback( struct urb *urb )
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial = port->serial;
- digi_private_t *priv = (digi_private_t *)(port->private);
+ struct usb_serial *serial;
+ digi_private_t *priv;
int ret = 0;
-dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
+dbg( "digi_write_bulk_callback: TOP" );
+
+ /* port sanity check */
+ if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) {
+ err( __FUNCTION__ ": port or port->private is NULL, status=%d",
+ urb->status );
+ return;
+ }
- /* handle callback on out-of-band port */
+ /* handle oob callback */
if( priv->dp_port_num == oob_port_num ) {
dbg( "digi_write_bulk_callback: oob callback" );
spin_lock( &priv->dp_port_lock );
@@ -1152,10 +1177,11 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
}
/* sanity checks */
- if( port_paranoia_check( port, "digi_write_bulk_callback" )
- || serial_paranoia_check( serial, "digi_write_bulk_callback" ) ) {
+ if( port_paranoia_check( port, "digi_write_bulk_callback" ) )
+ return;
+ serial = port->serial;
+ if( serial_paranoia_check( serial, "digi_write_bulk_callback" ) )
return;
- }
/* try to send any buffered data on this port */
spin_lock( &priv->dp_port_lock );
@@ -1187,8 +1213,8 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num );
spin_unlock( &priv->dp_port_lock );
if( ret ) {
- dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d",
- ret );
+ err( __FUNCTION__ ": usb_submit_urb failed, ret=%d, port=%d",
+ ret, priv->dp_port_num );
}
}
@@ -1245,21 +1271,13 @@ static int digi_open( struct usb_serial_port *port, struct file *filp )
unsigned long flags = 0;
-dbg( "digi_open: TOP: port %d, active:%d", priv->dp_port_num, port->active );
+dbg( "digi_open: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, port->active, priv->dp_open_count );
/* be sure the device is started up */
if( digi_startup_device( port->serial ) != 0 )
return( -ENXIO );
- /* if port is already open, just return */
- /* be sure exactly one open proceeds */
spin_lock_irqsave( &priv->dp_port_lock, flags );
- if( port->active >= 1 && !priv->dp_in_close ) {
- ++port->active;
- spin_unlock_irqrestore( &priv->dp_port_lock, flags );
- MOD_INC_USE_COUNT;
- return( 0 );
- }
/* don't wait on a close in progress for non-blocking opens */
if( priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) {
@@ -1267,23 +1285,31 @@ dbg( "digi_open: TOP: port %d, active:%d", priv->dp_port_num, port->active );
return( -EAGAIN );
}
- /* prevent other opens from proceeding, before giving up lock */
- ++port->active;
-
- /* wait for close to finish */
+ /* wait for a close in progress to finish */
while( priv->dp_in_close ) {
cond_wait_interruptible_timeout_irqrestore(
&priv->dp_close_wait, DIGI_RETRY_TIMEOUT,
&priv->dp_port_lock, flags );
if( signal_pending(current) ) {
- --port->active;
return( -EINTR );
}
spin_lock_irqsave( &priv->dp_port_lock, flags );
}
- spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ /* if port is already open, just return */
+ /* be sure exactly one open proceeds */
+ if( port->active ) {
+ ++priv->dp_open_count;
+ MOD_INC_USE_COUNT;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ return( 0 );
+ }
+
+ /* open is certain */
+ port->active = 1;
+ ++priv->dp_open_count;
MOD_INC_USE_COUNT;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
/* read modem signals automatically whenever they change */
buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
@@ -1323,17 +1349,17 @@ static void digi_close( struct usb_serial_port *port, struct file *filp )
unsigned long flags = 0;
-dbg( "digi_close: TOP: port %d, active:%d", priv->dp_port_num, port->active );
+dbg( "digi_close: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, port->active, priv->dp_open_count );
/* do cleanup only after final close on this port */
spin_lock_irqsave( &priv->dp_port_lock, flags );
- if( port->active > 1 ) {
- --port->active;
- spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ if( priv->dp_open_count > 1 ) {
+ --priv->dp_open_count;
MOD_DEC_USE_COUNT;
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return;
- } else if( port->active <= 0 ) {
+ } else if( priv->dp_open_count <= 0 ) {
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
return;
}
@@ -1410,13 +1436,13 @@ dbg( "digi_close: TOP: port %d, active:%d", priv->dp_port_num, port->active );
tty->closing = 0;
spin_lock_irqsave( &priv->dp_port_lock, flags );
- --port->active;
+ port->active = 0;
priv->dp_in_close = 0;
+ --priv->dp_open_count;
+ MOD_DEC_USE_COUNT;
wake_up_interruptible( &priv->dp_close_wait );
spin_unlock_irqrestore( &priv->dp_port_lock, flags );
- MOD_DEC_USE_COUNT;
-
dbg( "digi_close: done" );
}
@@ -1489,6 +1515,7 @@ dbg( "digi_startup: TOP" );
priv->dp_port_num = i;
priv->dp_buf_len = 0;
priv->dp_modem_signals = 0;
+ priv->dp_open_count = 0;
priv->dp_transmit_idle = 0;
priv->dp_in_close = 0;
init_waitqueue_head( &priv->dp_close_wait );
@@ -1517,18 +1544,31 @@ static void digi_shutdown( struct usb_serial *serial )
{
int i;
+ digi_private_t *priv;
+ unsigned long flags;
-dbg( "digi_shutdown: TOP" );
+dbg( "digi_shutdown: TOP, in_interrupt()=%d", in_interrupt() );
/* stop reads and writes on all ports */
for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) {
- usb_unlink_urb (serial->port[i].read_urb);
- usb_unlink_urb (serial->port[i].write_urb);
+ usb_unlink_urb( serial->port[i].read_urb );
+ usb_unlink_urb( serial->port[i].write_urb );
}
device_startup = 0;
+ /* dec module use count */
+ for( i=0; i<digi_acceleport_device.num_ports; i++ ) {
+ priv = serial->port[i].private;
+ spin_lock_irqsave( &priv->dp_port_lock, flags );
+ while( priv->dp_open_count > 0 ) {
+ MOD_DEC_USE_COUNT;
+ --priv->dp_open_count;
+ }
+ spin_unlock_irqrestore( &priv->dp_port_lock, flags );
+ }
+
/* free the private data structures for all ports */
/* number of regular ports + 1 for the out-of-band port */
for( i=0; i<digi_acceleport_device.num_ports+1; i++ )
@@ -1541,40 +1581,76 @@ static void digi_read_bulk_callback( struct urb *urb )
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial = port->serial;
- struct tty_struct *tty = port->tty;
- digi_private_t *priv = (digi_private_t *)(port->private);
- int opcode = ((unsigned char *)urb->transfer_buffer)[0];
- int len = ((unsigned char *)urb->transfer_buffer)[1];
- int status = ((unsigned char *)urb->transfer_buffer)[2];
- unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3;
- int ret,i;
+ digi_private_t *priv;
+ int ret;
-dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num );
+dbg( "digi_read_bulk_callback: TOP" );
- /* handle oob callback */
- if( priv->dp_port_num == oob_port_num ) {
- digi_read_oob_callback( urb );
+ /* port sanity check, do not resubmit if port is not valid */
+ if( port == NULL || (priv=(digi_private_t *)(port->private)) == NULL ) {
+ err( __FUNCTION__ ": port or port->private is NULL, status=%d",
+ urb->status );
return;
}
- /* sanity checks */
- if( port_paranoia_check( port, "digi_read_bulk_callback" )
- || serial_paranoia_check( serial, "digi_read_bulk_callback" ) ) {
- goto resubmit;
+ /* do not resubmit urb if it has any status error */
+ if( urb->status ) {
+ err( __FUNCTION__ ": nonzero read bulk status: status=%d, port=%d", urb->status, priv->dp_port_num );
+ return;
}
- if( urb->status ) {
- dbg( "digi_read_bulk_callback: nonzero read bulk status: %d",
- urb->status );
- if( urb->status == -ENOENT )
+ /* handle oob or inb callback, do not resubmit if error */
+ if( priv->dp_port_num == oob_port_num ) {
+ if( digi_read_oob_callback( urb ) != 0 )
return;
- goto resubmit;
+ } else {
+ if( digi_read_inb_callback( urb ) != 0 )
+ return;
+ }
+
+ /* continue read */
+ if( (ret=usb_submit_urb(urb)) != 0 ) {
+ err( __FUNCTION__ ": failed resubmitting urb, ret=%d, port=%d",
+ ret, priv->dp_port_num );
}
- if( urb->actual_length != len + 2 )
- err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", priv->dp_port_num, opcode, len, urb->actual_length, status );
+}
+
+
+/*
+* Digi Read INB Callback
+*
+* Digi Read INB Callback handles reads on the in band ports, sending
+* the data on to the tty subsystem. When called we know port and
+* port->private are not NULL. It returns 0 if successful, and -1 if
+* the sanity checks failed.
+*/
+
+static int digi_read_inb_callback( struct urb *urb )
+{
+
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial = port->serial;
+ struct tty_struct *tty = port->tty;
+ digi_private_t *priv = (digi_private_t *)(port->private);
+ int opcode = ((unsigned char *)urb->transfer_buffer)[0];
+ int len = ((unsigned char *)urb->transfer_buffer)[1];
+ int status = ((unsigned char *)urb->transfer_buffer)[2];
+ unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3;
+ int i;
+
+
+ /* sanity checks */
+ if( port_paranoia_check( port, __FUNCTION__ )
+ || serial_paranoia_check( serial, __FUNCTION__ ) )
+ return( -1 );
+
+ /* short packet check */
+ if( urb->actual_length != len + 2 ) {
+ err( __FUNCTION__ ": INCOMPLETE PACKET, urb->status=%d, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", urb->status, priv->dp_port_num, opcode, len, urb->actual_length, status );
+ return( -1 );
+ }
/* receive data */
if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) {
@@ -1585,36 +1661,39 @@ dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num );
tty_flip_buffer_push(tty);
}
- /* continue read */
-resubmit:
- if( (ret=usb_submit_urb(urb)) != 0 ) {
- dbg( "digi_read_bulk_callback: failed resubmitting urb, ret=%d",
- ret );
- }
+ return( 0 );
}
-static void digi_read_oob_callback( struct urb *urb )
+/*
+* Digi Read OOB Callback
+*
+* Digi Read OOB Callback handles reads on the out of band port.
+* When called we know port and port->private are not NULL. It
+* returns 0 if successful, and -1 if the sanity checks failed.
+*/
+
+static int digi_read_oob_callback( struct urb *urb )
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
- digi_private_t *priv;
+ digi_private_t *priv = (digi_private_t *)(port->private);
int opcode, line, status, val;
- int i,ret;
+ int i;
dbg( "digi_read_oob_callback: len=%d", urb->actual_length );
- if( urb->status ) {
- dbg( "digi_read_oob_callback: nonzero read bulk status on oob: %d",
- urb->status );
- if( urb->status == -ENOENT )
- return;
- goto resubmit;
+ /* sanity check */
+ if( serial == NULL ) {
+ err( __FUNCTION__ ": port->serial is NULL, status=%d, port=%d",
+ urb->status, priv->dp_port_num );
+ return( -1 );
}
+ /* handle each oob command */
for( i=0; i<urb->actual_length-3; ) {
opcode = ((unsigned char *)urb->transfer_buffer)[i++];
@@ -1627,7 +1706,10 @@ dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, li
if( status != 0 )
continue;
- priv = serial->port[line].private;
+ if( (priv=serial->port[line].private) == NULL ) {
+ dbg( __FUNCTION__ ": port[%d].private is NULL!", line );
+ continue;
+ }
if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) {
@@ -1665,11 +1747,25 @@ dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, li
}
+ return( 0 );
+
+}
+
+
+int digi_init (void)
+{
+ usb_serial_register (&digi_acceleport_device);
+ return 0;
+}
-resubmit:
- if( (ret=usb_submit_urb(urb)) != 0 ) {
- dbg( "digi_read_oob_callback: failed resubmitting oob urb, ret=%d",
- ret );
- }
+void digi_exit (void)
+{
+ usb_serial_deregister (&digi_acceleport_device);
}
+
+
+module_init(digi_init);
+module_exit(digi_exit);
+
+
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8aec73569..f78ff752b 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -12,6 +12,10 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (07/19/2000) gkh
+ * Added module_init and module_exit functions to handle the fact that this
+ * driver is a loadable module now.
+ *
* (04/04/2000) Bill Ryder
* Fixed bugs in TCGET/TCSET ioctls (by removing them - they are
* handled elsewhere in the serial driver chain).
@@ -720,3 +724,20 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns
return 0;
} /* ftdi_sio_ioctl */
+
+int ftdi_sio_init (void)
+{
+ usb_serial_register (&ftdi_sio_device);
+ return 0;
+}
+
+
+void ftdi_sio_exit (void)
+{
+ usb_serial_deregister (&ftdi_sio_device);
+}
+
+
+module_init(ftdi_sio_init);
+module_exit(ftdi_sio_exit);
+
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 397e2bf48..2fecdca12 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -22,6 +22,10 @@
Tip 'o the hat to Linuxcare for supporting staff in their work on
open source projects.
+ Wed Jul 19 14:00:42 EST 2000 gkh
+ Added module_init and module_exit functions to handle the fact that this
+ driver is a loadable module now.
+
Tue Jul 18 16:14:52 EST 2000 Hugh
Basic character input/output for USA-19 now mostly works,
fixed at 9600 baud for the moment.
@@ -30,9 +34,6 @@
#include <linux/config.h>
-
-#ifdef CONFIG_USB_SERIAL_KEYSPAN
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
@@ -685,5 +686,38 @@ static void keyspan_shutdown (struct usb_serial *serial)
}
-#endif /* CONFIG_USB_SERIAL_KEYSPAN */
+
+int keyspan_init (void)
+{
+ usb_serial_register (&keyspan_usa18x_pre_device);
+ usb_serial_register (&keyspan_usa19_pre_device);
+ usb_serial_register (&keyspan_usa19w_pre_device);
+ usb_serial_register (&keyspan_usa28_pre_device);
+ usb_serial_register (&keyspan_usa28x_pre_device);
+ usb_serial_register (&keyspan_usa18x_device);
+ usb_serial_register (&keyspan_usa19_device);
+ usb_serial_register (&keyspan_usa19w_device);
+ usb_serial_register (&keyspan_usa28_device);
+ usb_serial_register (&keyspan_usa28x_device);
+ return 0;
+}
+
+
+void keyspan_exit (void)
+{
+ usb_serial_deregister (&keyspan_usa18x_pre_device);
+ usb_serial_deregister (&keyspan_usa19_pre_device);
+ usb_serial_deregister (&keyspan_usa19w_pre_device);
+ usb_serial_deregister (&keyspan_usa28_pre_device);
+ usb_serial_deregister (&keyspan_usa28x_pre_device);
+ usb_serial_deregister (&keyspan_usa18x_device);
+ usb_serial_deregister (&keyspan_usa19_device);
+ usb_serial_deregister (&keyspan_usa19w_device);
+ usb_serial_deregister (&keyspan_usa28_device);
+ usb_serial_deregister (&keyspan_usa28x_device);
+}
+
+
+module_init(keyspan_init);
+module_exit(keyspan_exit);
diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h
index ce966dc4f..666946181 100644
--- a/drivers/usb/serial/keyspan.h
+++ b/drivers/usb/serial/keyspan.h
@@ -29,6 +29,7 @@
#ifndef __LINUX_USB_SERIAL_KEYSPAN_H
#define __LINUX_USB_SERIAL_KEYSPAN_H
+#include <linux/config.h>
/* Function prototypes for Keyspan serial converter */
static int keyspan_open (struct usb_serial_port *port,
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index c18137ffa..aca2a38c5 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -11,6 +11,10 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (07/19/2000) gkh
+ * Added module_init and module_exit functions to handle the fact that this
+ * driver is a loadable module now.
+ *
* (03/26/2000) gkh
* Split driver up into device specific pieces.
*
@@ -697,3 +701,22 @@ struct usb_serial_device_type keyspan_pda_device = {
shutdown: keyspan_pda_shutdown,
};
+
+int keyspan_pda_init (void)
+{
+ usb_serial_register (&keyspan_pda_fake_device);
+ usb_serial_register (&keyspan_pda_device);
+ return 0;
+}
+
+
+void keyspan_pda_exit (void)
+{
+ usb_serial_deregister (&keyspan_pda_fake_device);
+ usb_serial_deregister (&keyspan_pda_device);
+}
+
+
+module_init(keyspan_pda_init);
+module_exit(keyspan_pda_exit);
+
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 2e5e50849..7a68a0072 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -10,6 +10,10 @@
*
* Please report both successes and troubles to the author at omninet@kroah.com
*
+ * (07/19/2000) gkh
+ * Added module_init and module_exit functions to handle the fact that this
+ * driver is a loadable module now.
+ *
*/
#include <linux/config.h>
@@ -333,3 +337,20 @@ static void omninet_write_bulk_callback (struct urb *urb)
return;
}
+
+int omninet_init (void)
+{
+ usb_serial_register (&zyxel_omninet_device);
+ return 0;
+}
+
+
+void omninet_exit (void)
+{
+ usb_serial_deregister (&zyxel_omninet_device);
+}
+
+
+module_init(omninet_init);
+module_exit(omninet_exit);
+
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index d51fe02a2..b0a0f2459 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -10,6 +10,10 @@
* (at your option) any later version.
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (07/19/2000) gkh, pberger, and borchers
+ * Modifications to allow usb-serial drivers to be modules.
+ *
*
*/
@@ -35,7 +39,6 @@ struct usb_serial_port {
int magic;
struct usb_serial *serial; /* pointer back to the owner of this port */
struct tty_struct * tty; /* the coresponding tty for this port */
- unsigned char minor;
unsigned char number;
char active; /* someone has this device open */
@@ -60,6 +63,7 @@ struct usb_serial {
int magic;
struct usb_device * dev;
struct usb_serial_device_type * type; /* the type of usb serial device this is */
+ struct usb_interface * interface; /* the interface for this device */
struct tty_driver * tty_driver; /* the tty_driver for this device */
unsigned char minor; /* the starting minor number for this device */
unsigned char num_ports; /* the number of ports this device has */
@@ -95,6 +99,8 @@ struct usb_serial_device_type {
char num_bulk_out;
char num_ports; /* number of serial ports this device has */
+ struct list_head driver_list;
+
/* function call to make before accepting driver */
int (*startup) (struct usb_serial *serial); /* return 0 to continue initialization, anything else to abort */
void (*shutdown) (struct usb_serial *serial);
@@ -116,30 +122,11 @@ struct usb_serial_device_type {
void (*write_bulk_callback)(struct urb *urb);
};
-
-
-extern struct usb_serial_device_type handspring_device;
-extern struct usb_serial_device_type whiteheat_fake_device;
-extern struct usb_serial_device_type whiteheat_device;
-extern struct usb_serial_device_type ftdi_sio_device;
-extern struct usb_serial_device_type keyspan_pda_fake_device;
-extern struct usb_serial_device_type keyspan_pda_device;
-extern struct usb_serial_device_type keyspan_usa18x_pre_device;
-extern struct usb_serial_device_type keyspan_usa19_pre_device;
-extern struct usb_serial_device_type keyspan_usa19w_pre_device;
-extern struct usb_serial_device_type keyspan_usa28_pre_device;
-extern struct usb_serial_device_type keyspan_usa28x_pre_device;
-extern struct usb_serial_device_type keyspan_usa18x_device;
-extern struct usb_serial_device_type keyspan_usa19_device;
-extern struct usb_serial_device_type keyspan_usa19w_device;
-extern struct usb_serial_device_type keyspan_usa28_device;
-extern struct usb_serial_device_type keyspan_usa28x_device;
-extern struct usb_serial_device_type zyxel_omninet_device;
-extern struct usb_serial_device_type digi_acceleport_device;
-
+extern int usb_serial_register(struct usb_serial_device_type *new_device);
+extern void usb_serial_deregister(struct usb_serial_device_type *device);
/* determine if we should include the EzUSB loader functions */
-#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_WHITEHEAT) || defined(CONFIG_USB_SERIAL_KEYSPAN)
+#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_WHITEHEAT) || defined(CONFIG_USB_SERIAL_KEYSPAN) || defined(CONFIG_USB_SERIAL_KEYSPAN_PDA_MODULE) || defined(CONFIG_USB_SERIAL_WHITEHEAT_MODULE) || defined(CONFIG_USB_SERIAL_KEYSPAN_MODULE)
#define USES_EZUSB_FUNCTIONS
extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest);
extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c
index a02c34456..50b85c4f2 100644
--- a/drivers/usb/serial/usbserial.c
+++ b/drivers/usb/serial/usbserial.c
@@ -14,6 +14,9 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (07/19/2000) gkh, pberger, and borchers
+ * Modifications to allow usb-serial drivers to be modules.
+ *
* (07/03/2000) gkh
* Added more debugging to serial_ioctl call
*
@@ -284,49 +287,6 @@ static struct usb_serial_device_type generic_device = {
#endif
-/* To add support for another serial converter, create a usb_serial_device_type
- structure for that device, and add it to this list, making sure that the
- last entry is NULL. */
-static struct usb_serial_device_type *usb_serial_devices[] = {
-#ifdef CONFIG_USB_SERIAL_GENERIC
- &generic_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_WHITEHEAT
- &whiteheat_fake_device,
- &whiteheat_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_VISOR
- &handspring_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_FTDI_SIO
- &ftdi_sio_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
- &keyspan_pda_fake_device,
- &keyspan_pda_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_OMNINET
- &zyxel_omninet_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_DIGI_ACCELEPORT
- &digi_acceleport_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_KEYSPAN
- &keyspan_usa18x_pre_device,
- &keyspan_usa19_pre_device,
- &keyspan_usa19w_pre_device,
- &keyspan_usa28_pre_device,
- &keyspan_usa28x_pre_device,
- &keyspan_usa18x_device,
- &keyspan_usa19_device,
- &keyspan_usa19w_device,
- &keyspan_usa28_device,
- &keyspan_usa28x_device,
-#endif
- NULL
-};
-
-
/* local function prototypes */
static int serial_open (struct tty_struct *tty, struct file * filp);
static void serial_close (struct tty_struct *tty, struct file * filp);
@@ -354,6 +314,8 @@ static struct termios * serial_termios[SERIAL_TTY_MINORS];
static struct termios * serial_termios_locked[SERIAL_TTY_MINORS];
static struct usb_serial *serial_table[SERIAL_TTY_MINORS] = {NULL, };
+LIST_HEAD(usb_serial_driver_list);
+
static inline struct usb_serial* get_usb_serial (struct usb_serial_port *port, const char *function)
{
@@ -950,13 +912,15 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
{
struct usb_serial *serial = NULL;
struct usb_serial_port *port;
- struct usb_interface_descriptor *interface;
+ struct usb_interface *interface;
+ struct usb_interface_descriptor *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
- struct usb_serial_device_type *type;
- int device_num;
+ struct usb_serial_device_type *type = NULL;
+ struct list_head *tmp;
+ int found;
int minor;
int buffer_size;
int i;
@@ -968,34 +932,38 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
int num_bulk_out = 0;
int num_ports;
int max_endpoints;
+
/* loop through our list of known serial converters, and see if this
- device matches. */
- for (device_num = 0; usb_serial_devices[device_num]; device_num++) {
- type = usb_serial_devices[device_num];
+ device matches. */
+ found = 0;
+ list_for_each (tmp, &usb_serial_driver_list) {
+ type = list_entry(tmp, struct usb_serial_device_type, driver_list);
dbg ("Looking at %s Vendor id=%.4x Product id=%.4x",
- type->name, *(type->idVendor), *(type->idProduct));
+ type->name, *(type->idVendor), *(type->idProduct));
/* look at the device descriptor */
if ((dev->descriptor.idVendor == *(type->idVendor)) &&
(dev->descriptor.idProduct == *(type->idProduct))) {
dbg("descriptor matches");
+ found = 1;
break;
}
}
- if (!usb_serial_devices[device_num]) {
+ if (!found) {
/* no match */
dbg("none matched");
return(NULL);
}
-
+
/* descriptor matches, let's find the endpoints needed */
interrupt_pipe = bulk_in_pipe = bulk_out_pipe = HAS_NOT;
/* check out the endpoints */
- interface = &dev->actconfig->interface[ifnum].altsetting[0];
- for (i = 0; i < interface->bNumEndpoints; ++i) {
- endpoint = &interface->endpoint[i];
+ interface = &dev->actconfig->interface[ifnum];
+ iface_desc = &interface->altsetting[0];
+ for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i];
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x02)) {
@@ -1059,6 +1027,7 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
serial->dev = dev;
serial->type = type;
+ serial->interface = interface;
serial->minor = minor;
serial->num_ports = num_ports;
serial->num_bulk_in = num_bulk_in;
@@ -1205,6 +1174,12 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
int i;
if (serial) {
+ /* fail all future close/read/write/ioctl/etc calls */
+ for (i = 0; i < serial->num_ports; ++i) {
+ if (serial->port[i].tty != NULL)
+ serial->port[i].tty->driver_data = NULL;
+ }
+
if (serial->type->shutdown)
serial->type->shutdown(serial);
@@ -1290,7 +1265,6 @@ static struct tty_driver serial_tty_driver = {
int usb_serial_init(void)
{
int i;
- int something;
int result;
/* Initalize our global data */
@@ -1298,17 +1272,6 @@ int usb_serial_init(void)
serial_table[i] = NULL;
}
- /* tell the world what devices this driver currently supports */
- something = 0;
- for (i = 0; usb_serial_devices[i]; ++i) {
- if (!strstr (usb_serial_devices[i]->name, "prerenumeration")) {
- info ("USB Serial support registered for %s", usb_serial_devices[i]->name);
- something = 1;
- }
- }
- if (!something)
- info ("USB Serial driver is not configured for any devices!");
-
/* register the tty driver */
serial_tty_driver.init_termios = tty_std_termios;
serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
@@ -1317,6 +1280,11 @@ int usb_serial_init(void)
return -1;
}
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ /* register our generic driver with ourselves */
+ usb_serial_register (&generic_device);
+#endif
+
/* register the USB driver */
result = usb_register(&usb_serial_driver);
if (result < 0) {
@@ -1325,12 +1293,19 @@ int usb_serial_init(void)
return -1;
}
+
return 0;
}
void usb_serial_exit(void)
{
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ /* remove our generic driver */
+ usb_serial_deregister (&generic_device);
+#endif
+
usb_deregister(&usb_serial_driver);
tty_unregister_driver(&serial_tty_driver);
}
@@ -1340,3 +1315,46 @@ module_init(usb_serial_init);
module_exit(usb_serial_exit);
+int usb_serial_register(struct usb_serial_device_type *new_device)
+{
+ /* Add this device to our list of devices */
+ list_add(&new_device->driver_list, &usb_serial_driver_list);
+
+ info ("USB Serial support registered for %s", new_device->name);
+
+ usb_scan_devices();
+
+ return 0;
+}
+
+
+void usb_serial_deregister(struct usb_serial_device_type *device)
+{
+ struct usb_serial *serial;
+ int i;
+
+ info("USB Serial deregistering driver %s", device->name);
+
+ /* clear out the serial_table if the device is attached to a port */
+ for(i = 0; i < SERIAL_TTY_MINORS; ++i) {
+ serial = serial_table[i];
+ if ((serial != NULL) && (serial->type == device)) {
+ usb_driver_release_interface (&usb_serial_driver, serial->interface);
+ usb_serial_disconnect (NULL, serial);
+ }
+ }
+
+ list_del(&device->driver_list);
+}
+
+
+
+/* If the usb-serial core is build into the core, the usb-serial drivers
+ need these symbols to load properly as modules. */
+EXPORT_SYMBOL(usb_serial_register);
+EXPORT_SYMBOL(usb_serial_deregister);
+#ifdef USES_EZUSB_FUNCTIONS
+ EXPORT_SYMBOL(ezusb_writememory);
+ EXPORT_SYMBOL(ezusb_set_reset);
+#endif
+
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index a455e8dc8..f2502fbf6 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -11,6 +11,10 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (07/19/2000) gkh
+ * Added module_init and module_exit functions to handle the fact that this
+ * driver is a loadable module now.
+ *
* (07/03/2000) gkh
* Added visor_set_ioctl and visor_set_termios functions (they don't do much
* of anything, but are good for debugging.)
@@ -293,4 +297,19 @@ static void visor_set_termios (struct usb_serial_port *port, struct termios *old
}
+int visor_init (void)
+{
+ usb_serial_register (&handspring_device);
+ return 0;
+}
+
+
+void visor_exit (void)
+{
+ usb_serial_deregister (&handspring_device);
+}
+
+
+module_init(visor_init);
+module_exit(visor_exit);
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 91afddd56..a6ca6ebcb 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -11,6 +11,11 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (07/19/2000) gkh
+ * Added module_init and module_exit functions to handle the fact that this
+ * driver is a loadable module now.
+ * Fixed bug with port->minor that was found by Al Borchers
+ *
* (07/04/2000) gkh
* Added support for port settings. Baud rate can now be changed. Line signals
* are not transferred to and from the tty layer yet, but things seem to be
@@ -258,7 +263,7 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
/* send an open port command */
- open_command.port = port->number - port->minor;
+ open_command.port = port->number - port->serial->minor;
whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
/* Need to do device specific setup here (control lines, baud rate, etc.) */
@@ -277,7 +282,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
dbg(__FUNCTION__ " - port %d", port->number);
/* send a close command to the port */
- close_command.port = port->number - port->minor;
+ close_command.port = port->number - port->serial->minor;
whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command));
/* Need to change the control lines here */
@@ -501,7 +506,7 @@ static void set_command (struct usb_serial_port *port, unsigned char state, unsi
struct whiteheat_rdb_set rdb_command;
/* send a set rts command to the port */
- rdb_command.port = port->number - port->minor;
+ rdb_command.port = port->number - port->serial->minor;
rdb_command.state = state;
whiteheat_send_cmd (port->serial, command, (__u8 *)&rdb_command, sizeof(rdb_command));
@@ -525,3 +530,22 @@ static inline void set_break (struct usb_serial_port *port, unsigned char brk)
set_command (port, brk, WHITEHEAT_SET_BREAK);
}
+
+int whiteheat_init (void)
+{
+ usb_serial_register (&whiteheat_fake_device);
+ usb_serial_register (&whiteheat_device);
+ return 0;
+}
+
+
+void whiteheat_exit (void)
+{
+ usb_serial_deregister (&whiteheat_fake_device);
+ usb_serial_deregister (&whiteheat_device);
+}
+
+
+module_init(whiteheat_init);
+module_exit(whiteheat_exit);
+
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index c3dd30dee..7ede92c88 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -4,7 +4,7 @@
O_TARGET := usb-storage.o
M_OBJS := usb-storage.o
-O_OBJS := scsiglue.o protocol.o transport.o debug.o usb.o
+O_OBJS := scsiglue.o protocol.o transport.o debug.o usb.o
MOD_LIST_NAME := USB_STORAGE_MODULES
CFLAGS_scsiglue.o:= -I../../scsi/
@@ -12,11 +12,16 @@ CFLAGS_protocol.o:= -I../../scsi/
CFLAGS_transport.o:= -I../../scsi/
CFLAGS_debug.o:= -I../../scsi/
CFLAGS_usb.o:= -I../../scsi/
+CFLAGS_scm.o:= -I../../scsi/
ifeq ($(CONFIG_USB_STORAGE_DEBUG),y)
O_OBJS += debug.o
endif
+ifeq ($(CONFIG_USB_STORAGE_HP8200e),y)
+ O_OBJS += scm.o
+endif
+
ifeq ($(CONFIG_USB_STORAGE_FREECOM),y)
O_OBJS += freecom.o
endif
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
index d8845d8c2..2a5127bb6 100644
--- a/drivers/usb/storage/debug.c
+++ b/drivers/usb/storage/debug.c
@@ -1,7 +1,7 @@
/* Driver for USB Mass Storage compliant devices
* Debugging Functions Source Code File
*
- * $Id: debug.c,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ * $Id: debug.c,v 1.2 2000/07/19 17:21:39 groovyjava Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -97,22 +97,47 @@ void usb_stor_show_command(Scsi_Cmnd *srb)
case WRITE_LONG: what = "WRITE_LONG"; break;
case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
case WRITE_SAME: what = "WRITE_SAME"; break;
+ case GPCMD_READ_SUBCHANNEL: what = "READ SUBCHANNEL"; break;
case READ_TOC: what = "READ_TOC"; break;
+ case GPCMD_READ_HEADER: what = "READ HEADER"; break;
+ case GPCMD_PLAY_AUDIO_10: what = "PLAY AUDIO (10)"; break;
+ case GPCMD_PLAY_AUDIO_MSF: what = "PLAY AUDIO MSF"; break;
+ case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
+ what = "GET EVENT/STATUS NOTIFICATION"; break;
+ case GPCMD_PAUSE_RESUME: what = "PAUSE/RESUME"; break;
case LOG_SELECT: what = "LOG_SELECT"; break;
case LOG_SENSE: what = "LOG_SENSE"; break;
+ case GPCMD_STOP_PLAY_SCAN: what = "STOP PLAY/SCAN"; break;
+ case GPCMD_READ_DISC_INFO: what = "READ DISC INFORMATION"; break;
+ case GPCMD_READ_TRACK_RZONE_INFO:
+ what = "READ TRACK INFORMATION"; break;
+ case GPCMD_RESERVE_RZONE_TRACK: what = "RESERVE TRACK"; break;
+ case GPCMD_SEND_OPC: what = "SEND OPC"; break;
case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+ case GPCMD_REPAIR_RZONE_TRACK: what = "REPAIR TRACK"; break;
+ case 0x59: what = "READ MASTER CUE"; break;
case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
- case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+ case GPCMD_CLOSE_TRACK: what = "CLOSE TRACK/SESSION"; break;
+ case 0x5C: what = "READ BUFFER CAPACITY"; break;
+ case 0x5D: what = "SEND CUE SHEET"; break;
+ case GPCMD_BLANK: what = "BLANK"; break;
+ case MOVE_MEDIUM: what = "MOVE_MEDIUM or PLAY AUDIO (12)"; break;
case READ_12: what = "READ_12"; break;
case WRITE_12: what = "WRITE_12"; break;
case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
- case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+ case GPCMD_READ_CD_MSF: what = "READ CD MSF"; break;
+ case GPCMD_SCAN: what = "SCAN"; break;
+ case GPCMD_SET_SPEED: what = "SET CD SPEED"; break;
+ case GPCMD_MECHANISM_STATUS: what = "MECHANISM STATUS"; break;
+ case GPCMD_READ_CD: what = "READ CD"; break;
+ case 0xE1: what = "WRITE CONTINUE"; break;
case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
- default: break;
+ default: what = "(unknown command)"; break;
}
US_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len);
US_DEBUGP("%02x %02x %02x %02x "
diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h
index ab5bef0ac..04a08be2e 100644
--- a/drivers/usb/storage/debug.h
+++ b/drivers/usb/storage/debug.h
@@ -1,7 +1,7 @@
/* Driver for USB Mass Storage compliant devices
* Debugging Functions Header File
*
- * $Id: debug.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ * $Id: debug.h,v 1.3 2000/07/19 19:34:48 groovyjava Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -47,6 +47,7 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/blk.h>
+#include <linux/cdrom.h>
#include "scsi.h"
#define USB_STORAGE "usb-storage: "
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index d3624dc76..5e7a5a23b 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -1,6 +1,6 @@
/* Driver for USB Mass Storage compliant devices
*
- * $Id: protocol.c,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ * $Id: protocol.c,v 1.2 2000/07/19 17:21:39 groovyjava Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -64,6 +64,11 @@ void usb_stor_ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
* a unsigned char cmnd[12], so we know we have storage available
*/
+ /* Pad the ATAPI command with zeros */
+
+ for (; srb->cmd_len<12; srb->cmd_len++)
+ srb->cmnd[srb->cmd_len] = 0;
+
/* set command length to 12 bytes */
srb->cmd_len = 12;
diff --git a/drivers/usb/storage/scm.c b/drivers/usb/storage/scm.c
new file mode 100644
index 000000000..2d3ca7051
--- /dev/null
+++ b/drivers/usb/storage/scm.c
@@ -0,0 +1,979 @@
+/* Driver for SCM Microsystems USB-ATAPI cable
+ *
+ * SCM driver v0.1
+ *
+ * Current development and maintainance by:
+ * (c) 2000 Robert Baruch (autophile@dol.net)
+ *
+ * Many originally ATAPI devices were slightly modified to meet the USB
+ * market by using some kind of translation from ATAPI to USB on the host,
+ * and the peripheral would translate from USB back to ATAPI.
+ *
+ * SCM Microsystems (www.scmmicro.com) makes a device, sold to OEM's only,
+ * which does the USB-to-ATAPI conversion. By obtaining the data sheet on
+ * their device under nondisclosure agreement, I have been able to write
+ * this driver for Linux.
+ *
+ * The chip used in the device can also be used for EPP and ISA translation
+ * as well. This driver is only guaranteed to work with the ATAPI
+ * translation.
+ *
+ * The only peripherals that I know of (as of 14 Jul 2000) that uses this
+ * device is the Hewlett-Packard 8200e CD-Writer Plus.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "transport.h"
+#include "protocol.h"
+#include "usb.h"
+#include "debug.h"
+#include "scm.h"
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+
+extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
+ u8 request, u8 requesttype, u16 value, u16 index,
+ void *data, u16 size);
+extern int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
+ unsigned int len, unsigned int *act_len);
+
+/*
+ * Send a control message and wait for the response.
+ *
+ * us - the pointer to the us_data structure for the device to use
+ *
+ * request - the URB Setup Packet's first 6 bytes. The first byte always
+ * corresponds to the request type, and the second byte always corresponds
+ * to the request. The other 4 bytes do not correspond to value and index,
+ * since they are used in a custom way by the SCM protocol.
+ *
+ * xfer_data - a buffer from which to get, or to which to store, any data
+ * that gets send or received, respectively, with the URB. Even though
+ * it looks like we allocate a buffer in this code for the data, xfer_data
+ * must contain enough allocated space.
+ *
+ * xfer_len - the number of bytes to send or receive with the URB.
+ *
+ */
+
+static int scm_send_control(struct us_data *us,
+ unsigned char command[8],
+ unsigned char *xfer_data,
+ unsigned int xfer_len) {
+
+ int result;
+ int pipe;
+ void *buffer = NULL;
+
+ command[6] = xfer_len&0xFF;
+ command[7] = (xfer_len>>8)&0xFF;
+
+ // Get the receive or send control pipe number, based on
+ // the direction indicated by the request type.
+
+ if (command[0] & USB_DIR_IN)
+ pipe = usb_rcvctrlpipe(us->pusb_dev,0);
+ else
+ pipe = usb_sndctrlpipe(us->pusb_dev,0);
+
+
+ // If data is going to be sent or received with the URB,
+ // then allocate a buffer for it. If data is to be sent,
+ // copy the data into the buffer.
+
+ if (xfer_len > 0) {
+ buffer = kmalloc(xfer_len, GFP_KERNEL);
+ if (!(command[0] & USB_DIR_IN))
+ memcpy(buffer, xfer_data, xfer_len);
+ }
+
+ // Send the URB to the device and wait for a response.
+
+ /* Why are request and request type reversed in this call? */
+
+ result = usb_stor_control_msg(us, pipe,
+ command[1], command[0],
+ (((unsigned short)command[3])<<8) | command[2],
+ (((unsigned short)command[5])<<8) | command[3],
+ buffer,
+ xfer_len);
+
+
+ // If data was sent or received with the URB, free the buffer we
+ // allocated earlier, but not before reading the data out of the
+ // buffer if we wanted to receive data.
+
+ if (xfer_len > 0) {
+ if (command[0] & USB_DIR_IN)
+ memcpy(xfer_data, buffer, xfer_len);
+ kfree(buffer);
+ }
+
+ // Check the return code for the command.
+
+ if (result < 0) {
+ /* a stall is a fatal condition from the device */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ result = usb_clear_halt(us->pusb_dev, pipe);
+ US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ /* Uh oh... serious problem here */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+static int scm_raw_bulk(struct us_data *us,
+ int pipe, int maxlen,
+ unsigned char *data,
+ unsigned short len) {
+
+ int transferred = 0;
+ int result;
+ int act_len;
+ int partial_len;
+ unsigned char status;
+
+ maxlen = len;
+
+ // while (transferred < len) {
+
+ // We want to transfer up to maxlen bytes
+
+ partial_len = len-transferred > maxlen ?
+ maxlen :
+ len-transferred;
+
+ result = usb_stor_bulk_msg(us,
+ data+transferred,
+ pipe,
+ partial_len,
+ &act_len);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("EPIPE: clearing endpoint halt for"
+ " pipe 0x%x, stalled at %d bytes\n",
+ pipe, act_len);
+ usb_clear_halt(us->pusb_dev, pipe);
+
+ // What the hey is goin' on?
+
+ /* US_DEBUGP("EPIPE: Trying to retransmit bulk data...\n");
+
+ wait_ms(10);
+
+ result = usb_stor_bulk_msg(us, data+act_len,
+ pipe, len-act_len, &act_len);
+
+ if (result==0)
+ US_DEBUGP("EPIPE: Retransmit successful\n"); */
+
+ wait_ms(1000);
+ US_DEBUGP("Trying to get status\n");
+ result = scm_read(us, SCM_ATA, 0x17, &status);
+ US_DEBUGP("SCM: Write ATA data status is %02X\n",
+ status);
+
+ result = -EPIPE;
+ }
+
+ if (result) {
+
+ /* NAK - that means we've retried a few times already */
+ if (result == -ETIMEDOUT) {
+ US_DEBUGP("scm_raw_bulk():"
+ " device NAKed\n");
+ return US_BULK_TRANSFER_FAILED;
+ }
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("scm_raw_bulk():"
+ " transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ if (result == -EPIPE) {
+ US_DEBUGP("scm_raw_bulk():"
+ " output pipe stalled\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ /* the catch-all case */
+ US_DEBUGP("us_transfer_partial(): unknown error\n");
+ return US_BULK_TRANSFER_FAILED;
+ }
+
+ if (act_len != partial_len) {
+ US_DEBUGP("Warning: Transferred only %d bytes\n",
+ act_len);
+ return US_BULK_TRANSFER_SHORT;
+ }
+
+ US_DEBUGP("Transfered %d of %d bytes\n", act_len, partial_len);
+
+ transferred += act_len;
+ // } // while transferred < len
+
+ return US_BULK_TRANSFER_GOOD;
+}
+
+static int scm_bulk_transport(struct us_data *us,
+ unsigned char *command,
+ unsigned short command_len,
+ unsigned char *data,
+ unsigned short len,
+ int use_sg) {
+
+ int result;
+ int transferred = 0;
+ int maxlen;
+ unsigned char execute[8] = {
+ 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ int i;
+ int pipe;
+ struct scatterlist *sg;
+ char string[64];
+
+ maxlen = us->pusb_dev->actconfig->
+ interface[0].altsetting[0].endpoint[
+ ( (command[0]&0x80) ? us->ep_in : us->ep_out)-1].
+ wMaxPacketSize;
+
+ /* Fix up the command's data length */
+
+ command[6] = len&0xFF;
+ command[7] = (len>>8)&0xFF;
+
+ result = scm_send_control(us,
+ execute,
+ command,
+ command_len);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if (len==0)
+ return result;
+
+ /* transfer the data payload for the command, if there is any */
+
+ if (command[0]&0x80)
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+ else {
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* Debug-print the first 48 bytes of the transfer */
+
+ if (!use_sg) {
+ string[0] = 0;
+ for (i=0; i<len && i<48; i++) {
+ sprintf(string+strlen(string), "%02X ",
+ data[i]);
+ if ((i%16)==15) {
+ US_DEBUGP("%s\n", string);
+ string[0] = 0;
+ }
+ }
+ if (string[0]!=0)
+ US_DEBUGP("%s\n", string);
+ }
+ }
+
+
+ US_DEBUGP("HP 8200e data %s maxlen %d transfer %d sg buffers %d\n",
+ ( (command[0]&0x80) ? "in" : "out"),
+ maxlen, len, use_sg);
+
+ if (!use_sg)
+ result = scm_raw_bulk(us, pipe, maxlen,
+ data, len);
+ else {
+ sg = (struct scatterlist *)data;
+ for (i=0; i<use_sg && transferred<len; i++) {
+ result = scm_raw_bulk(us, pipe, maxlen,
+ sg[i].address,
+ len-transferred > sg[i].length ?
+ sg[i].length : len-transferred);
+ if (result!=US_BULK_TRANSFER_GOOD)
+ break;
+ transferred += sg[i].length;
+ }
+ }
+
+ return result;
+}
+
+int scm_read(struct us_data *us,
+ unsigned char access,
+ unsigned char reg,
+ unsigned char *content) {
+
+ int result;
+ unsigned char command[8] = {
+ 0xC0, access, reg, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ result = scm_send_control(us, command, content, 1);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // US_DEBUGP("SCM: Reg %d -> %02X\n", reg, *content);
+
+ return result;
+}
+
+int scm_write(struct us_data *us,
+ unsigned char access,
+ unsigned char reg,
+ unsigned char content) {
+
+ int result;
+ unsigned char command[8] = {
+ 0x40, access|0x01, reg, content, 0x00, 0x00, 0x00, 0x00
+ };
+
+ result = scm_send_control(us, command, NULL, 0);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // US_DEBUGP("SCM: Reg %d <- %02X\n", reg, content);
+
+ return result;
+}
+
+int scm_set_shuttle_features(struct us_data *us,
+ unsigned char external_trigger,
+ unsigned char epp_control,
+ unsigned char mask_byte,
+ unsigned char test_pattern,
+ unsigned char subcountH,
+ unsigned char subcountL) {
+
+ int result;
+ unsigned char command[8] = {
+ 0x40, 0x81, epp_control, external_trigger,
+ test_pattern, mask_byte, subcountL, subcountH
+ };
+
+ result = scm_bulk_transport(us, command, 8, NULL, 0, 0);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ return result;
+}
+
+int scm_read_block(struct us_data *us,
+ unsigned char access,
+ unsigned char reg,
+ unsigned char *content,
+ unsigned short len,
+ int use_sg) {
+
+ int result;
+ unsigned char command[8] = {
+ 0xC0, access|0x02, reg, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ result = scm_bulk_transport(us,
+ command, 8, content, len, use_sg);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // US_DEBUGP("SCM: Read data, result %d\n", result);
+
+ return result;
+}
+
+/*
+ * Block, waiting for an ATA device to become not busy or to report
+ * an error condition.
+ */
+
+int scm_wait_not_busy(struct us_data *us) {
+
+ int i;
+ int result;
+ unsigned char status;
+
+ /* Synchronizing cache on a CDR could take a heck of a long time,
+ but probably not more than 15 minutes or so */
+
+ for (i=0; i<500; i++) {
+ result = scm_read(us, SCM_ATA, 0x17, &status);
+ US_DEBUGP("SCM: Write ATA data status is %02X\n", status);
+ if (result!=USB_STOR_TRANSPORT_GOOD)
+ return result;
+ if (status&0x01) // check condition
+ return USB_STOR_TRANSPORT_FAILED;
+ if ((status&0x80)!=0x80) // not busy
+ break;
+ if (i<5)
+ wait_ms(100);
+ else if (i<20)
+ wait_ms(500);
+ else if (i<49)
+ wait_ms(1000);
+ else if (i<499)
+ wait_ms(2000);
+ }
+
+ if (i==500)
+ return USB_STOR_TRANSPORT_FAILED;
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+int scm_write_block(struct us_data *us,
+ unsigned char access,
+ unsigned char reg,
+ unsigned char *content,
+ unsigned short len,
+ int use_sg) {
+
+ int result;
+ unsigned char command[8] = {
+ 0x40, access|0x03, reg, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ int i;
+ unsigned char status;
+
+ result = scm_bulk_transport(us,
+ command, 8, content, len, use_sg);
+
+ if (result!=USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ return scm_wait_not_busy(us);
+}
+
+int scm_write_block_test(struct us_data *us,
+ unsigned char access,
+ unsigned char *registers,
+ unsigned char *data_out,
+ unsigned short num_registers,
+ unsigned char data_reg,
+ unsigned char status_reg,
+ unsigned char qualifier,
+ unsigned char timeout,
+ unsigned char *content,
+ unsigned short len,
+ int use_sg) {
+
+ int result;
+ unsigned char command[16] = {
+ 0x40, access|0x07, 0x07, 0x17, 0xfc, 0xe7, 0x00, 0x00,
+ 0x40, access|0x05, data_reg, status_reg,
+ qualifier, timeout, 0x00, 0x00
+ };
+ int i;
+ unsigned char status;
+ unsigned char data[num_registers*2];
+ int maxlen;
+ int transferred;
+ int pipe;
+ struct scatterlist *sg;
+ char string[64];
+
+ command[14] = len&0xFF;
+ command[15] = (len>>8)&0xFF;
+
+ for (i=0; i<num_registers; i++) {
+ data[i<<1] = registers[i];
+ data[1+(i<<1)] = data_out[i];
+ }
+
+ result = scm_bulk_transport(us,
+ command, 16, data, num_registers*2, 0);
+
+ if (result!=USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ maxlen = us->pusb_dev->actconfig->
+ interface[0].altsetting[0].endpoint[us->ep_out-1].
+ wMaxPacketSize;
+
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+ transferred = 0;
+
+ US_DEBUGP("Transfer out %d bytes, sg buffers %d\n",
+ len, use_sg);
+
+ if (!use_sg) {
+
+ /* Debug-print the first 48 bytes of the transfer */
+
+ if (!use_sg) {
+ string[0] = 0;
+ for (i=0; i<len && i<48; i++) {
+ sprintf(string+strlen(string), "%02X ",
+ content[i]);
+ if ((i%16)==15) {
+ US_DEBUGP("%s\n", string);
+ string[0] = 0;
+ }
+ }
+ if (string[0]!=0)
+ US_DEBUGP("%s\n", string);
+ }
+
+ result = scm_raw_bulk(us, pipe, maxlen,
+ content, len);
+
+ } else {
+
+ sg = (struct scatterlist *)content;
+ for (i=0; i<use_sg && transferred<len; i++) {
+ result = scm_raw_bulk(us, pipe, maxlen,
+ sg[i].address,
+ len-transferred > sg[i].length ?
+ sg[i].length : len-transferred);
+ if (result!=US_BULK_TRANSFER_GOOD)
+ break;
+ transferred += sg[i].length;
+ }
+ }
+
+ if (result!=USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ return scm_wait_not_busy(us);
+}
+
+int scm_multiple_write(struct us_data *us,
+ unsigned char access,
+ unsigned char *registers,
+ unsigned char *data_out,
+ unsigned short num_registers) {
+
+ int result;
+ unsigned char data[num_registers*2];
+ int i;
+ unsigned char cmd[8] = {
+ 0x40, access|0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ unsigned char status;
+
+ for (i=0; i<num_registers; i++) {
+ data[i<<1] = registers[i];
+ data[1+(i<<1)] = data_out[i];
+ }
+
+ result = scm_bulk_transport(us, cmd, 8, data, num_registers*2, 0);
+
+ if (result!=USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ return scm_wait_not_busy(us);
+}
+
+static int hp_8200e_select_and_test_registers(struct us_data *us) {
+
+ int result;
+ int selector;
+ unsigned char status;
+
+ // try device = master, then device = slave.
+
+ for (selector = 0xA0; selector <= 0xB0; selector += 0x10) {
+
+ if ( (result = scm_write(us, SCM_ATA, 0x16, selector)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read(us, SCM_ATA, 0x17, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read(us, SCM_ATA, 0x16, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read(us, SCM_ATA, 0x14, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read(us, SCM_ATA, 0x15, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_write(us, SCM_ATA, 0x14, 0x55)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_write(us, SCM_ATA, 0x15, 0xAA)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read(us, SCM_ATA, 0x14, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read(us, SCM_ATA, 0x15, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+ }
+
+ return result;
+}
+
+int scm_read_user_io(struct us_data *us,
+ unsigned char *data_flags) {
+
+ unsigned char command[8] = {
+ 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ int result;
+
+ result = scm_send_control(us, command, data_flags, 1);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // US_DEBUGP("SCM: User I/O flags -> %02X\n", *data_flags);
+
+ return result;
+}
+
+int scm_write_user_io(struct us_data *us,
+ unsigned char enable_flags,
+ unsigned char data_flags) {
+
+ unsigned char command[8] = {
+ 0x40, 0x82, enable_flags, data_flags, 0x00, 0x00, 0x00, 0x00
+ };
+ int result;
+
+ result = scm_send_control(us, command, NULL, 0);
+
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // US_DEBUGP("SCM: User I/O flags <- %02X\n", data_flags);
+
+ return result;
+}
+
+static int init_8200e(struct us_data *us) {
+
+ int result;
+ unsigned char status;
+
+ // Enable peripheral control signals
+
+ if ( (result = scm_write_user_io(us,
+ SCM_UIO_OE1 | SCM_UIO_OE0,
+ SCM_UIO_EPAD | SCM_UIO_1)) != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ wait_ms(2000);
+
+ if ( (result = scm_read_user_io(us, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read_user_io(us, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // Reset peripheral, enable periph control signals
+ // (bring reset signal up)
+
+ if ( (result = scm_write_user_io(us,
+ SCM_UIO_DRVRST | SCM_UIO_OE1 | SCM_UIO_OE0,
+ SCM_UIO_EPAD | SCM_UIO_1)) != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // Enable periph control signals
+ // (bring reset signal down)
+
+ if ( (result = scm_write_user_io(us,
+ SCM_UIO_OE1 | SCM_UIO_OE0,
+ SCM_UIO_EPAD | SCM_UIO_1)) != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ wait_ms(250);
+
+ // Write 0x80 to ISA port 0x3F
+
+ if ( (result = scm_write(us, SCM_ISA, 0x3F, 0x80)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // Read ISA port 0x27
+
+ if ( (result = scm_read(us, SCM_ISA, 0x27, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read_user_io(us, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = hp_8200e_select_and_test_registers(us)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read_user_io(us, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ // Enable periph control signals and card detect
+
+ if ( (result = scm_write_user_io(us,
+ SCM_UIO_ACKD |SCM_UIO_OE1 | SCM_UIO_OE0,
+ SCM_UIO_EPAD | SCM_UIO_1)) != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_read_user_io(us, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ wait_ms(1400);
+
+ if ( (result = scm_read_user_io(us, &status)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = hp_8200e_select_and_test_registers(us)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ if ( (result = scm_set_shuttle_features(us,
+ 0x83, 0x00, 0x88, 0x08, 0x15, 0x14)) !=
+ USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ return result;
+}
+
+/*
+ * Transport for the HP 8200e
+ */
+int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int result;
+ unsigned char status;
+ unsigned char registers[32];
+ unsigned char data[32];
+ unsigned int len;
+ int i;
+ char string[64];
+
+ /* This table tells us:
+ X = command not supported
+ L = return length in cmnd[4] (8 bits).
+ H = return length in cmnd[7] and cmnd[8] (16 bits).
+ D = return length in cmnd[6] to cmnd[9] (32 bits).
+ B = return length/blocksize in cmnd[6] to cmnd[8].
+ T = return length in cmnd[6] to cmnd[8] (24 bits).
+ 0-9 = fixed return length
+ W = 24 bytes
+ h = return length/2048 in cmnd[7-8].
+ */
+
+ static char *lengths =
+
+ /* 0123456789ABCDEF 0123456789ABCDEF */
+
+ "0XXL0XXXXXXXXXXX" "XXLXXXXXXXX0XX0X" /* 00-1F */
+ "XXXXX8XXhXH0XXX0" "XXXXX0XXXXXXXXXX" /* 20-3F */
+ "XXHHL0X0XXH0XX0X" "XHH00HXX0TH0H0XX" /* 40-5F */
+ "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* 60-7F */
+ "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* 80-9F */
+ "X0XXX0XXDXDXXXXX" "XXXXXXXXX000XHBX" /* A0-BF */
+ "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* C0-DF */
+ "XDXXXXXXXXXXXXXX" "XXW00HXXXXXXXXXX"; /* E0-FF */
+
+ /* FIXME: B9 (READ CD MSF) has unknown length! */
+
+ /* US_DEBUGP("XXXXXXXX 8200e transport called, tid %08X\n",
+ current->pid); */
+
+ if (us->flags & US_FL_NEED_INIT) {
+ US_DEBUGP("8200e: initializing\n");
+ init_8200e(us);
+ us->flags &= ~US_FL_NEED_INIT;
+ }
+
+ if (srb->sc_data_direction == SCSI_DATA_WRITE)
+ len = srb->request_bufflen;
+ else {
+
+ switch (lengths[srb->cmnd[0]]) {
+
+ case 'L':
+ len = srb->cmnd[4];
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ len = lengths[srb->cmnd[0]]-'0';
+ break;
+ case 'H':
+ len = (((unsigned int)srb->cmnd[7])<<8) | srb->cmnd[8];
+ break;
+ case 'h':
+ len = (((unsigned int)srb->cmnd[7])<<8) | srb->cmnd[8];
+ len <<= 11; // *2048
+ break;
+ case 'T':
+ len = (((unsigned int)srb->cmnd[6])<<16) |
+ (((unsigned int)srb->cmnd[7])<<8) |
+ srb->cmnd[8];
+ break;
+ case 'D':
+ len = (((unsigned int)srb->cmnd[6])<<24) |
+ (((unsigned int)srb->cmnd[7])<<16) |
+ (((unsigned int)srb->cmnd[8])<<8) |
+ srb->cmnd[9];
+ break;
+ case 'W':
+ len = 24;
+ break;
+ default:
+ US_DEBUGP("Error: UNSUPPORTED COMMAND %02X\n",
+ srb->cmnd[0]);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ }
+
+ if (len > 0xFFFF) {
+ US_DEBUGP("Error: len = %08X... what do I do now?\n",
+ len);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ // US_DEBUGP("XXXXXXXXXXXXXXXX req_bufflen %d, len %d, bufflen %d\n",
+ // srb->request_bufflen, len, srb->bufflen);
+
+ /* Send A0 (ATA PACKET COMMAND).
+ Note: I guess we're never going to get any of the ATA
+ commands... just ATA Packet Commands.
+ */
+
+ registers[0] = 0x11;
+ registers[1] = 0x12;
+ registers[2] = 0x13;
+ registers[3] = 0x14;
+ registers[4] = 0x15;
+ registers[5] = 0x16;
+ registers[6] = 0x17;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = len&0xFF; // (cylL) = expected length (L)
+ data[4] = (len>>8)&0xFF; // (cylH) = expected length (H)
+ data[5] = 0xB0; // (device sel) = slave
+ data[6] = 0xA0; // (command) = ATA PACKET COMMAND
+
+ if (srb->sc_data_direction == SCSI_DATA_WRITE) {
+
+ for (i=7; i<19; i++) {
+ registers[i] = 0x10;
+ data[i] = (i-7 >= srb->cmd_len) ? 0 : srb->cmnd[i-7];
+ }
+
+ result = scm_write_block_test(us, SCM_ATA,
+ registers, data, 19,
+ 0x10, 0x17, 0xFD, 0x30,
+ srb->request_buffer,
+ len, srb->use_sg);
+
+ return result;
+ }
+
+ if ( (result = scm_multiple_write(us,
+ SCM_ATA,
+ registers, data, 7)) != USB_STOR_TRANSPORT_GOOD) {
+ return result;
+ }
+
+ // Write the 12-byte command header.
+
+ if ( (result = scm_write_block(us,
+ SCM_ATA, 0x10, srb->cmnd, 12, 0)) !=
+ USB_STOR_TRANSPORT_GOOD) {
+ return result;
+ }
+
+ // If there is response data to be read in
+ // then do it here.
+
+ if (len != 0 && (srb->sc_data_direction == SCSI_DATA_READ)) {
+
+ // How many bytes to read in? Check cylL register
+
+ if ( (result = scm_read(us, SCM_ATA, 0x14, &status)) !=
+ USB_STOR_TRANSPORT_GOOD) {
+ return result;
+ }
+
+ if (len>0xFF) { // need to read cylH also
+ len = status;
+ if ( (result = scm_read(us, SCM_ATA, 0x15, &status)) !=
+ USB_STOR_TRANSPORT_GOOD) {
+ return result;
+ }
+ len += ((unsigned int)status)<<8;
+ }
+ else
+ len = status;
+
+
+ result = scm_read_block(us, SCM_ATA, 0x10,
+ srb->request_buffer, len, srb->use_sg);
+
+ /* Debug-print the first 32 bytes of the transfer */
+
+ if (!srb->use_sg) {
+ string[0] = 0;
+ for (i=0; i<len && i<32; i++) {
+ sprintf(string+strlen(string), "%02X ",
+ ((unsigned char *)srb->request_buffer)[i]);
+ if ((i%16)==15) {
+ US_DEBUGP("%s\n", string);
+ string[0] = 0;
+ }
+ }
+ if (string[0]!=0)
+ US_DEBUGP("%s\n", string);
+ }
+ }
+
+ // US_DEBUGP("Command result %d\n", result);
+
+ return result;
+}
diff --git a/drivers/usb/storage/scm.h b/drivers/usb/storage/scm.h
new file mode 100644
index 000000000..edc324a84
--- /dev/null
+++ b/drivers/usb/storage/scm.h
@@ -0,0 +1,77 @@
+/* Driver for SCM Microsystems USB-ATAPI cable
+ * Header File
+ *
+ * Current development and maintainance by:
+ * (c) 2000 Robert Baruch (autophile@dol.net)
+ *
+ * See scm.c for more explanation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _USB_SCM_H
+#define _USB_SCM_H
+
+#define SCM_EPP_PORT 0x10
+#define SCM_EPP_REGISTER 0x30
+#define SCM_ATA 0x40
+#define SCM_ISA 0x50
+
+/* SCM User I/O Data registers */
+
+#define SCM_UIO_EPAD 0x80 // Enable Peripheral Control Signals
+#define SCM_UIO_CDT 0x40 // Card Detect (Read Only)
+ // CDT = ACKD & !UI1 & !UI0
+#define SCM_UIO_1 0x20 // I/O 1
+#define SCM_UIO_0 0x10 // I/O 0
+#define SCM_UIO_EPP_ATA 0x08 // 1=EPP mode, 0=ATA mode
+#define SCM_UIO_UI1 0x04 // Input 1
+#define SCM_UIO_UI0 0x02 // Input 0
+#define SCM_UIO_INTR_ACK 0x01 // Interrupt (ATA & ISA)/Acknowledge (EPP)
+
+/* SCM User I/O Enable registers */
+
+#define SCM_UIO_DRVRST 0x80 // Reset Peripheral
+#define SCM_UIO_ACKD 0x40 // Enable Card Detect
+#define SCM_UIO_OE1 0x20 // I/O 1 set=output/clr=input
+ // If ACKD=1, set OE1 to 1 also.
+#define SCM_UIO_OE0 0x10 // I/O 0 set=output/clr=input
+#define SCM_UIO_ADPRST 0x01 // Reset SCM chip
+
+/* SCM-specific commands */
+
+extern int scm_read(struct us_data *us, unsigned char access,
+ unsigned char reg, unsigned char *content);
+extern int scm_write(struct us_data *us, unsigned char access,
+ unsigned char reg, unsigned char content);
+extern int scm_read_block(struct us_data *us, unsigned char access,
+ unsigned char reg, unsigned char *content, unsigned short len,
+ int use_sg);
+extern int scm_write_block(struct us_data *us, unsigned char access,
+ unsigned char reg, unsigned char *content, unsigned short len,
+ int use_sg);
+extern int scm_multiple_write(struct us_data *us, unsigned char access,
+ unsigned char *registers, unsigned char *data_out,
+ unsigned short num_registers);
+extern int scm_read_user_io(struct us_data *us, unsigned char *data_flags);
+extern int scm_write_user_io(struct us_data *us,
+ unsigned char enable_flags, unsigned char data_flags);
+
+/* HP 8200e stuff */
+
+extern int hp8200e_transport(Scsi_Cmnd *srb, struct us_data *us);
+
+
+#endif
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 71487235d..ad0df666b 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -1,7 +1,7 @@
/* Driver for USB Mass Storage compliant devices
* SCSI layer glue code
*
- * $Id: scsiglue.c,v 1.2 2000/06/27 10:20:39 mdharm Exp $
+ * $Id: scsiglue.c,v 1.4 2000/07/20 01:14:56 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -135,7 +135,7 @@ static int release(struct Scsi_Host *psh)
*/
US_DEBUGP("-- sending US_ACT_EXIT command to thread\n");
us->action = US_ACT_EXIT;
- up(&(us->sleeper));
+ wake_up(&(us->wqh));
down(&(us->notify));
/* free the data structure we were using */
@@ -171,9 +171,11 @@ static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
srb->scsi_done = done;
us->action = US_ACT_COMMAND;
- /* wake up the process task */
+ /* release the lock on the structure */
up(&(us->queue_exclusion));
- up(&(us->sleeper));
+
+ /* wake up the process task */
+ wake_up(&(us->wqh));
return 0;
}
@@ -335,7 +337,7 @@ Scsi_Host_Template usb_stor_host_template = {
emulated: TRUE
};
-unsigned char usb_stor_sense_notready[12] = {
+unsigned char usb_stor_sense_notready[18] = {
[0] = 0x70, /* current error */
[2] = 0x02, /* not ready */
[5] = 0x0a, /* additional length */
diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h
index ea8a34504..e9da1bcc0 100644
--- a/drivers/usb/storage/scsiglue.h
+++ b/drivers/usb/storage/scsiglue.h
@@ -1,7 +1,7 @@
/* Driver for USB Mass Storage compliant devices
* SCSI Connecting Glue Header File
*
- * $Id: scsiglue.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ * $Id: scsiglue.h,v 1.2 2000/07/19 22:12:07 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -45,7 +45,7 @@
#include "scsi.h"
#include "hosts.h"
-extern unsigned char usb_stor_sense_notready[12];
+extern unsigned char usb_stor_sense_notready[18];
extern unsigned char us_direction[256/8];
extern Scsi_Host_Template usb_stor_host_template;
extern int usb_stor_scsiSense10to6(Scsi_Cmnd*);
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index c83a7512d..88c847641 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -1,6 +1,6 @@
/* Driver for USB Mass Storage compliant devices
*
- * $Id: transport.c,v 1.2 2000/06/27 10:20:39 mdharm Exp $
+ * $Id: transport.c,v 1.3 2000/07/20 01:06:40 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -60,10 +60,10 @@
*/
static void usb_stor_blocking_completion(urb_t *urb)
{
- api_wrapper_data *awd = (api_wrapper_data *)urb->context;
+ wait_queue_head_t *wqh_ptr = (wait_queue_head_t *)urb->context;
- if (waitqueue_active(awd->wakeup))
- wake_up(awd->wakeup);
+ if (waitqueue_active(wqh_ptr))
+ wake_up(wqh_ptr);
}
/* This is our function to emulate usb_control_msg() but give us enough
@@ -73,9 +73,8 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
u8 request, u8 requesttype, u16 value, u16 index,
void *data, u16 size)
{
- DECLARE_WAITQUEUE(wait, current);
- DECLARE_WAIT_QUEUE_HEAD(wqh);
- api_wrapper_data awd;
+ wait_queue_head_t wqh;
+ wait_queue_t wait;
int status;
devrequest *dr;
@@ -92,9 +91,8 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
dr->length = cpu_to_le16(size);
/* set up data structures for the wakeup system */
- awd.wakeup = &wqh;
- awd.handler = 0;
init_waitqueue_head(&wqh);
+ init_waitqueue_entry(&wait, current);
add_wait_queue(&wqh, &wait);
/* lock the URB */
@@ -103,7 +101,7 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
/* fill the URB */
FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe,
(unsigned char*) dr, data, size,
- usb_stor_blocking_completion, &awd);
+ usb_stor_blocking_completion, &wqh);
/* submit the URB */
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -143,15 +141,13 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
unsigned int len, unsigned int *act_len)
{
- DECLARE_WAITQUEUE(wait, current);
- DECLARE_WAIT_QUEUE_HEAD(wqh);
- api_wrapper_data awd;
+ wait_queue_head_t wqh;
+ wait_queue_t wait;
int status;
/* set up data structures for the wakeup system */
- awd.wakeup = &wqh;
- awd.handler = 0;
init_waitqueue_head(&wqh);
+ init_waitqueue_entry(&wait, current);
add_wait_queue(&wqh, &wait);
/* lock the URB */
@@ -159,7 +155,7 @@ int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
/* fill the URB */
FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
- usb_stor_blocking_completion, &awd);
+ usb_stor_blocking_completion, &wqh);
/* submit the URB */
set_current_state(TASK_UNINTERRUPTIBLE);
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
index fb8699781..e5445f21b 100644
--- a/drivers/usb/storage/transport.h
+++ b/drivers/usb/storage/transport.h
@@ -1,7 +1,7 @@
/* Driver for USB Mass Storage compliant devices
* Transport Functions Header File
*
- * $Id: transport.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ * $Id: transport.h,v 1.3 2000/07/20 23:36:22 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -54,6 +54,9 @@ extern unsigned char us_direction[256/8];
#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */
#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */
#define US_PR_BULK 0x50 /* bulk only */
+#ifdef CONFIG_USB_STORAGE_HP8200e
+#define US_PR_SCM_ATAPI 0x80 /* SCM-ATAPI bridge */
+#endif
/*
* Bulk only data structures
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index d493bc695..060c8f4c0 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -1,6 +1,6 @@
/* Driver for USB Mass Storage compliant devices
*
- * $Id: usb.c,v 1.3 2000/06/27 10:20:39 mdharm Exp $
+ * $Id: usb.c,v 1.6 2000/07/20 23:36:22 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -48,11 +48,11 @@
#include "transport.h"
#include "protocol.h"
#include "debug.h"
+#ifdef CONFIG_USB_STORAGE_HP8200e
+#include "scm.h"
+#endif
#include <linux/module.h>
- /*FIXME: note that this next include is needed for the new sleeping system
- * which is not implemented yet
- */
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -88,6 +88,7 @@ static struct usb_driver storage_driver = {
static int usb_stor_control_thread(void * __us)
{
+ wait_queue_t wait;
struct us_data *us = (struct us_data *)__us;
int action;
@@ -104,15 +105,23 @@ static int usb_stor_control_thread(void * __us)
unlock_kernel();
+ /* set up for wakeups by new commands */
+ init_waitqueue_entry(&wait, current);
+ init_waitqueue_head(&(us->wqh));
+ add_wait_queue(&(us->wqh), &wait);
+
/* signal that we've started the thread */
up(&(us->notify));
for(;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
US_DEBUGP("*** thread sleeping.\n");
- down(&(us->sleeper));
- down(&(us->queue_exclusion));
+ schedule();
US_DEBUGP("*** thread awakened.\n");
+ /* lock access to the queue element */
+ down(&(us->queue_exclusion));
+
/* take the command off the queue */
action = us->action;
us->action = 0;
@@ -216,6 +225,11 @@ static int usb_stor_control_thread(void * __us)
static struct us_unusual_dev us_unusual_dev_list[] = {
{ 0x03f0, 0x0107, 0x0200, 0x0200, "HP USB CD-Writer Plus",
US_SC_8070, US_PR_CB, 0},
+#ifdef CONFIG_USB_STORAGE_HP8200e
+ { 0x03f0, 0x0207, 0x0001, 0x0001, "HP USB CD-Writer Plus 8200e",
+ US_SC_8070, US_PR_SCM_ATAPI,
+ US_FL_ALT_LENGTH | US_FL_NEED_INIT | US_FL_SINGLE_LUN},
+#endif
{ 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita LS-120",
US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN},
{ 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle eUSCSI Bridge",
@@ -518,7 +532,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
}
/* Initialize the mutexes only when the struct is new */
- init_MUTEX_LOCKED(&(ss->sleeper));
init_MUTEX_LOCKED(&(ss->notify));
init_MUTEX_LOCKED(&(ss->ip_waitq));
init_MUTEX(&(ss->queue_exclusion));
@@ -584,6 +597,15 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
ss->max_lun = usb_stor_Bulk_max_lun(ss);
break;
+#ifdef CONFIG_USB_STORAGE_HP8200e
+ case US_PR_SCM_ATAPI:
+ ss->transport_name = "SCM/ATAPI";
+ ss->transport = hp8200e_transport;
+ ss->transport_reset = usb_stor_CB_reset;
+ ss->max_lun = 1;
+#endif
+ break;
+
default:
ss->transport_name = "Unknown";
up(&us_list_semaphore);
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index f18b6c868..8c7008fa9 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -1,7 +1,7 @@
/* Driver for USB Mass Storage compliant devices
* Main Header File
*
- * $Id: usb.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $
+ * $Id: usb.h,v 1.3 2000/07/20 01:14:56 mdharm Exp $
*
* Current development and maintainance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -103,6 +103,7 @@ struct us_unusual_dev {
#define US_FL_ALT_LENGTH 0x00000008 /* use the alternate algorithm for
us_transfer_length() */
#define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */
+#define US_FL_NEED_INIT 0x00000020 /* Device needs initialization */
#define USB_STOR_STRING_LEN 32
@@ -169,9 +170,11 @@ struct us_data {
struct semaphore current_urb_sem; /* to protect irq_urb */
struct urb *current_urb; /* non-int USB requests */
+ /* the waitqueue for sleeping the control thread */
+ wait_queue_head_t wqh; /* to sleep thread on */
+
/* mutual exclusion structures */
struct semaphore notify; /* thread begin/end */
- struct semaphore sleeper; /* to sleep the thread on */
struct semaphore queue_exclusion; /* to protect data structs */
};
diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c
index d1155e0ee..8300fc7d2 100644
--- a/drivers/usb/usb-ohci.c
+++ b/drivers/usb/usb-ohci.c
@@ -622,10 +622,29 @@ static int sohci_free_dev (struct usb_device * usb_dev)
spin_unlock_irqrestore (&usb_ed_lock, flags);
if (cnt > 0) {
- add_wait_queue (&op_wakeup, &wait);
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout (HZ / 10);
- remove_wait_queue (&op_wakeup, &wait);
+
+ if (ohci->disabled) {
+ /* FIXME: Something like this should kick in,
+ * though it's currently an exotic case ...
+ * the controller won't ever be touching
+ * these lists again!!
+ dl_del_list (ohci,
+ le16_to_cpu (ohci->hcca.frame_no) & 1);
+ */
+ warn ("TD leak, %d", cnt);
+
+
+ } else if (!in_interrupt ()) {
+ /* SF interrupt handler calls dl_del_list */
+ add_wait_queue (&op_wakeup, &wait);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout (HZ / 10);
+ remove_wait_queue (&op_wakeup, &wait);
+
+ } else {
+ /* drivers mustn't expect this to work */
+ err ("can't free tds, interrupt context");
+ }
}
}
kfree (dev);
diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c
index bfac6098b..de3b0dd9c 100644
--- a/drivers/usb/usb.c
+++ b/drivers/usb/usb.c
@@ -25,6 +25,21 @@
#include <linux/bitops.h>
#include <linux/malloc.h>
#include <linux/interrupt.h> /* for in_interrupt() */
+
+
+#if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* waitpid() call glue uses this */
+static int errno;
+#endif
+
+
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
@@ -481,6 +496,213 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
return -1;
}
+
+#if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+
+/*
+ * USB hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when USB devices get added or removed.
+ */
+
+static int exec_helper (void *arg)
+{
+ void **params = (void **) arg;
+ char *path = (char *) params [0];
+ char **argv = (char **) params [1];
+ char **envp = (char **) params [2];
+ return exec_usermodehelper (path, argv, envp);
+}
+
+int call_usermodehelper (char *path, char **argv, char **envp)
+{
+ void *params [3] = { path, argv, envp };
+ int pid, pid2, retval;
+ mm_segment_t fs;
+
+ if ((pid = kernel_thread (exec_helper, (void *) params, 0)) < 0) {
+ err ("failed fork of %s, errno = %d", argv [0], -pid);
+ return -1;
+ }
+
+ /* set signal mask? */
+ fs = get_fs ();
+ set_fs (KERNEL_DS); /* retval is in kernel space. */
+ pid2 = waitpid (pid, &retval, __WCLONE); /* "errno" gets assigned */
+ set_fs (fs);
+ /* restore signal mask? */
+
+ if (pid2 != pid) {
+ err ("waitpid(%d) failed, returned %d\n", pid, pid2);
+ return -1;
+ }
+ return retval;
+}
+
+static int to_bcd (char *buf, __u16 *bcdValue)
+{
+ int retval = 0;
+ char *value = (char *) bcdValue;
+ int temp;
+
+ /* digits are 0-9 then ":;<=>?" for devices using
+ * non-bcd (non-standard!) values here ... */
+
+ /* No leading (or later, trailing) zeroes since scripts do
+ * literal matches, and that's how they're doing them. */
+ if ((temp = value [1] & 0xf0) != 0) {
+ temp >>= 4;
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+ }
+
+ temp = value [1] & 0x0f;
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+
+ *buf++ = '.';
+ retval++;
+
+ temp = value [0] & 0xf0;
+ temp >>= 4;
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+
+ if ((temp = value [0] & 0x0f) != 0) {
+ temp += '0';
+ *buf++ = (char) temp;
+ retval++;
+ }
+ *buf++ = 0;
+
+ return retval;
+}
+
+/*
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, or both.
+ *
+ * Some synchronization is important: removes can't start processing
+ * before the add-device processing completes, and vice versa. That keeps
+ * a stack of USB-related identifiers stable while they're in use. If we
+ * know that agents won't complete after they return (such as by forking
+ * a process that completes later), it's enough to just waitpid() for the
+ * agent -- as is currently done.
+ *
+ * The reason: we know we're called either from khubd (the typical case)
+ * or from root hub initialization (init, kapmd, modprobe, etc). In both
+ * cases, we know no other thread can recycle our address, since we must
+ * already have been serialized enough to prevent that.
+ */
+static void call_policy (char *verb, struct usb_device *dev)
+{
+ char *argv [3], **envp, *buf, *scratch;
+ int i = 0, value;
+
+ if (!hotplug_path [0])
+ return;
+ if (in_interrupt ()) {
+ dbg ("In_interrupt");
+ return;
+ }
+ if (!(envp = (char **) kmalloc (20 * sizeof (char *), GFP_KERNEL))) {
+ dbg ("enomem");
+ return;
+ }
+ if (!(buf = kmalloc (256, GFP_KERNEL))) {
+ kfree (envp);
+ dbg ("enomem2");
+ return;
+ }
+
+ /* only one standardized param to hotplug command: type */
+ argv [0] = hotplug_path;
+ argv [1] = "usb";
+ argv [2] = 0;
+
+ /* minimal command environment */
+ envp [i++] = "HOME=/";
+ envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+#ifdef DEBUG
+ /* hint that policy agent should enter no-stdout debug mode */
+ envp [i++] = "DEBUG=kernel";
+#endif
+ /* extensible set of named bus-specific parameters,
+ * supporting multiple driver selection algorithms.
+ */
+ scratch = buf;
+
+ /* action: add, remove */
+ envp [i++] = scratch;
+ scratch += sprintf (scratch, "ACTION=%s", verb) + 1;
+
+#ifdef CONFIG_USB_DEVICEFS
+ /* If this is available, userspace programs can directly read
+ * all the device descriptors we don't tell them about. Or
+ * even act as usermode drivers.
+ *
+ * XXX how little intelligence can we hardwire?
+ * (a) mount point: /devfs, /dev, /proc/bus/usb etc.
+ * (b) naming convention: bus1/device3, 001/003 etc.
+ */
+ envp [i++] = "DEVFS=/proc/bus/usb";
+ envp [i++] = scratch;
+ scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d",
+ dev->bus->busnum, dev->devnum) + 1;
+#endif
+
+ /* per-device configuration hacks are often necessary */
+ envp [i++] = scratch;
+ scratch += sprintf (scratch, "PRODUCT=%x/%x/",
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct);
+ scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1;
+
+ /* otherwise, use a simple (so far) generic driver binding model */
+ envp [i++] = scratch;
+ if (dev->descriptor.bDeviceClass == 0) {
+ int alt = dev->actconfig->interface [0].act_altsetting;
+
+ /* simple/common case: one config, one interface, one driver
+ * unsimple cases: everything else
+ */
+ scratch += sprintf (scratch, "INTERFACE=%d/%d/%d",
+ dev->actconfig->interface [0].altsetting [alt].bInterfaceClass,
+ dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass,
+ dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol)
+ + 1;
+ /* INTERFACE-0, INTERFACE-1, ... ? */
+ } else {
+ /* simple/common case: generic device, handled generically */
+ scratch += sprintf (scratch, "TYPE=%d/%d/%d",
+ dev->descriptor.bDeviceClass,
+ dev->descriptor.bDeviceSubClass,
+ dev->descriptor.bDeviceProtocol) + 1;
+ }
+ envp [i++] = 0;
+ /* assert: (scratch - buf) < sizeof buf */
+
+ /* NOTE: user mode daemons can call the agents too */
+
+ dbg ("kusbd: %s %s", argv [0], argv [1]);
+ value = call_usermodehelper (argv [0], argv, envp);
+ kfree (buf);
+ kfree (envp);
+ dbg ("kusbd policy returned 0x%x", value);
+}
+
+#else
+
+static inline void
+call_policy (char *verb, struct usb_device *dev)
+{ }
+
+#endif /* KMOD && HOTPLUG */
+
+
/*
* This entrypoint gets called for each new device.
*
@@ -506,7 +728,8 @@ static void usb_find_drivers(struct usb_device *dev)
dbg("unhandled interfaces on device");
if (!claimed) {
- warn("This device is not recognized by any installed USB driver.");
+ warn("USB device %d is not claimed by any active driver.",
+ dev->devnum);
#ifdef DEBUG
usb_show_device(dev);
#endif
@@ -1195,6 +1418,8 @@ void usb_disconnect(struct usb_device **pdev)
info("USB disconnect on device %d", dev->devnum);
+ call_policy ("remove", dev);
+
if (dev->actconfig) {
for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
struct usb_interface *interface = &dev->actconfig->interface[i];
@@ -1712,6 +1937,9 @@ int usb_new_device(struct usb_device *dev)
/* find drivers willing to handle this device */
usb_find_drivers(dev);
+ /* userspace may load modules and/or configure further */
+ call_policy ("add", dev);
+
return 0;
}
diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c
index b48b27fd3..a422a7fe2 100644
--- a/drivers/video/hitfb.c
+++ b/drivers/video/hitfb.c
@@ -1,5 +1,5 @@
/*
- * $Id: hitfb.c,v 1.1 2000/06/10 21:45:40 yaegashi Exp $
+ * $Id: hitfb.c,v 1.2 2000/07/04 06:24:46 yaegashi Exp $
* linux/drivers/video/hitfb.c -- Hitachi LCD frame buffer device
* (C) 1999 Mihai Spatar
* (C) 2000 YAEGASHI Takeshi