diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-07-23 14:05:01 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-07-23 14:05:01 +0000 |
commit | f3627cbe9236a062012c836f3b6ee311b43f63f2 (patch) | |
tree | ae854838b9a73b35bd0f3b8f42e5fb7f9cb1d5a9 /drivers | |
parent | fea12a7b3f20bc135ab533491411e9ff753c01c8 (diff) |
Merge with Linux 2.4.0-test5-pre4.
Diffstat (limited to 'drivers')
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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, map->size, + PROT_READ|PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset); + up(¤t->mm->mmap_sem); + } else { + down(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up(¤t->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(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, map->size, + PROT_READ|PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset); + up(¤t->mm->mmap_sem); + } else { + down(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up(¤t->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 |