diff -ru linux-2.4.20-pre4-ac1.orig/Documentation/Configure.help linux-2.4.20-pre4-ac1/Documentation/Configure.help
--- linux-2.4.20-pre4-ac1.orig/Documentation/Configure.help	Sat Aug 24 11:01:29 2002
+++ linux-2.4.20-pre4-ac1/Documentation/Configure.help	Sun Aug 25 21:18:35 2002
@@ -731,6 +731,17 @@
   say M here and read <file:Documentation/modules.txt>.  The module
   will be called ide-cd.o.
 
+Use DMA for IDE/ATAPI CD-ROM audio
+CONFIG_DMA_IDECD
+  Reading audio from IDE CDROMs always uses PIO.  This option
+  teaches the kernel to use DMA for the CDROMREADAUDIO ioctl.
+
+  Total time to read a CD using cdparanoia improves by up to 20%.
+  But sometimes there is no change.
+
+  On old systems you may want to say N. However if the drive doesn't
+  support DMA the kernel falls back to PIO safely.
+
 Include IDE/ATAPI TAPE support
 CONFIG_BLK_DEV_IDETAPE
   If you have an IDE tape drive using the ATAPI protocol, say Y.
diff -ru linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.c linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.c
--- linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.c	Sat Aug 24 10:55:26 2002
+++ linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.c	Mon Aug 26 14:15:09 2002
@@ -1447,10 +1447,28 @@
 	int ireason, len, stat, thislen;
 	struct request *rq = HWGROUP(drive)->rq;
 	struct packet_command *pc = (struct packet_command *)rq->buffer;
+	struct cdrom_info *info = drive->driver_data;
+	int dma = info->dma;
+	int dma_error;
 	ide_startstop_t startstop;
 	u8 lowcyl = 0, highcyl = 0;
 
 	/* Check for errors. */
+	if (dma) {
+		info->dma = 0;
+		dma_error = HWIF(drive)->ide_dma_end(drive);
+		if (dma_error) {
+			/*
+			 * We don't disable drive DMA for packet DMA errors.
+			 * It's handled in cdda_read_audio()
+			 */
+			/* HWIF(drive)->ide_dma_off(drive); */
+			pc->stat = 2;	/* 2 -> DMA error */
+			printk(KERN_ERR "CDROM packet DMA error\n");
+		}
+	}
+
+	/* Check for errors. */
 	if (cdrom_decode_status(&startstop, drive, 0, &stat))
 		return startstop;
 
@@ -1461,6 +1479,14 @@
 
 	len = lowcyl + (256 * highcyl);
 
+	if (dma) {
+		/*
+		 * If DMA succeeded, we have all the data
+		 */
+		pc->buffer += pc->buflen;
+		pc->buflen = 0;
+	}
+
 	/* If DRQ is clear, the command has completed.
 	   Complain if we still have data left to transfer. */
 	if ((stat & DRQ_STAT) == 0) {
@@ -1566,7 +1592,11 @@
 	struct packet_command *pc = (struct packet_command *)rq->buffer;
 	struct cdrom_info *info = drive->driver_data;
 
-	info->dma = 0;
+	if (rq->bh) {
+		info->dma = 1;
+	} else {
+		info->dma = 0;
+	}
 	info->cmd = 0;
 	pc->stat = 0;
 	len = pc->buflen;
@@ -1588,6 +1618,13 @@
 		sleep = schedule_timeout(sleep);
 	} while (sleep);
 }
+ 
+/*
+ * end_buffer_io_sync() is not exported
+ */
+static void cdrom_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+{
+}
 
 static
 int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc)
@@ -1601,7 +1638,25 @@
 
 	/* Start of retry loop. */
 	do {
+		struct buffer_head bh;
+
 		ide_init_drive_cmd (&req);
+
+		if (pc->do_dma) {
+			/* Hack up a buffer_head for IDE DMA's use */
+			memset(&bh, 0, sizeof(bh));
+			bh.b_size = pc->buflen;
+			bh.b_data = pc->buffer;
+			bh.b_state = (1 << BH_Lock) | (1 << BH_Mapped) |
+					(1 << BH_Req);
+			bh.b_end_io = cdrom_end_buffer_io_sync;
+#if 0		/* Needed by end_buffer_io_sync, but not cdrom_end_buffer_io_sync */
+			atomic_set(&bh.b_count, 1);
+			init_waitqueue_head(&bh.b_wait);
+#endif
+			req.bh = &bh;
+		}
+
 		req.cmd = PACKET_COMMAND;
 		req.buffer = (char *)pc;
 		ide_do_drive_cmd(drive, &req, ide_wait);
@@ -1681,7 +1736,8 @@
 	/* Check for errors. */
 	if (dma) {
 		info->dma = 0;
-		if ((dma_error = HWIF(drive)->ide_dma_end(drive))) {
+		dma_error = HWIF(drive)->ide_dma_end(drive);
+		if (dma_error) {
 			printk("ide-cd: write dma error\n");
 			HWIF(drive)->ide_dma_off(drive);
 		}
@@ -2348,7 +2404,12 @@
 	pc.quiet = cgc->quiet;
 	pc.timeout = cgc->timeout;
 	pc.sense = cgc->sense;
-	return cgc->stat = cdrom_queue_packet_command(drive, &pc);
+	if (cgc->do_dma && drive->using_dma)
+		pc.do_dma = 1;
+	cgc->stat = cdrom_queue_packet_command(drive, &pc);
+	if (pc.stat == 2)	/* DMA error: fall back to lower mode */
+		cgc->dma_error = 1;
+	return cgc->stat;
 }
 
 static
diff -ru linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.h linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.h
--- linux-2.4.20-pre4-ac1.orig/drivers/ide/ide-cd.h	Sat Aug 24 11:09:06 2002
+++ linux-2.4.20-pre4-ac1/drivers/ide/ide-cd.h	Sun Aug 25 21:24:16 2002
@@ -111,6 +111,7 @@
 	int quiet;
 	int timeout;
 	struct request_sense *sense;
+	int do_dma;
 	unsigned char c[12];
 };
 
diff -ru linux-2.4.20-pre4-ac1.orig/drivers/cdrom/cdrom.c linux-2.4.20-pre4-ac1/drivers/cdrom/cdrom.c
--- linux-2.4.20-pre4-ac1.orig/drivers/cdrom/cdrom.c	Sat Aug 24 10:58:29 2002
+++ linux-2.4.20-pre4-ac1/drivers/cdrom/cdrom.c	Sun Aug 25 21:04:57 2002
@@ -2067,6 +2067,109 @@
 	return ret;
 }
 
+/*
+ * CDROM audio read, with DMA support.  Added in 2.4.18-pre4, akpm.
+ *
+ * Initially, we try to perform multiframe bus-mastering.  If the IDE
+ * layer experiences a DMA error, we fall back to single-frame DMA.
+ * If the IDE layer again detects a DMA error, we fall back to multiframe
+ * PIO.
+ *
+ * We do not want to disable drive-level DMA at any stage, because
+ * some devices can perform non-packet DMA quite happily, but appear
+ * to not be able to perform packet DMA correctly.
+ *
+ * If the drive is not using_dma, we never attempt packet DMA.
+ */
+static int cdda_read_audio(int cmd,
+			struct cdrom_device_info *cdi,
+			struct cdrom_generic_command *cgc,
+			struct cdrom_read_audio *ra)
+{
+	int lba;
+	unsigned frames_todo;
+	int ret;
+	void *xferbuf = 0;
+	unsigned nr_local_frames;
+	char *useraddr;
+
+	ret = -EINVAL;
+	if (ra->addr_format == CDROM_MSF) {
+		lba = msf_to_lba(ra->addr.msf.minute,
+				 ra->addr.msf.second,
+				 ra->addr.msf.frame);
+	} else if (ra->addr_format == CDROM_LBA) {
+		lba = ra->addr.lba;
+	} else {
+		goto out;
+	}
+
+	if (lba < 0 || ra->nframes <= 0)
+		goto out;
+
+	/*
+	 * We can't sensibly support more that 64k because we later
+	 * use a buffer_head to map the temp buffer.  And b_count is
+	 * unisgned short.
+	 */
+	nr_local_frames = ra->nframes;
+	if (nr_local_frames * CD_FRAMESIZE_RAW > 32768)
+		nr_local_frames = 32768 / CD_FRAMESIZE_RAW;
+
+	if (cdi->dma_mode == CDROM_DMA_SINGLE)
+		nr_local_frames = 1;
+
+	do {
+		xferbuf = kmalloc(CD_FRAMESIZE_RAW * nr_local_frames, GFP_KERNEL);
+	} while (!xferbuf && nr_local_frames--);
+	ret = -ENOMEM;
+	if (!xferbuf)
+		goto out;
+
+	cgc->buffer = xferbuf;
+	cgc->data_direction = CGC_DATA_READ;
+#ifdef CONFIG_DMA_IDECD
+	if (cdi->dma_mode != CDROM_DMA_NONE)
+		cgc->do_dma = 1;
+#endif
+	frames_todo = ra->nframes;
+	useraddr = ra->buf;
+retry:
+	while (frames_todo) {
+		unsigned frames_now = min(frames_todo, nr_local_frames);
+
+		cgc->dma_error = 0;
+		ret = cdrom_read_block(cdi, cgc, lba, frames_now, 1, CD_FRAMESIZE_RAW);
+		if (ret) {
+			/*
+			 * Here we implement DMA size fallback
+			 */
+			if (cgc->dma_error && cdi->dma_mode == CDROM_DMA_MULTI) {
+				printk(KERN_WARNING "CDROM: falling back to "
+					"single frame DMA\n");
+				cdi->dma_mode = CDROM_DMA_SINGLE;
+				nr_local_frames = 1;
+				goto retry;
+			} else if (cgc->dma_error && cdi->dma_mode == CDROM_DMA_SINGLE) {
+				printk(KERN_WARNING "CDROM: disabled DMA\n");
+				cdi->dma_mode = CDROM_DMA_NONE;
+				goto retry;
+			}
+			goto out;
+		}
+		ret = -EFAULT;
+		if (copy_to_user(useraddr, cgc->buffer, CD_FRAMESIZE_RAW * frames_now))
+			goto out;
+		useraddr += CD_FRAMESIZE_RAW * frames_now;
+		frames_todo -= frames_now;
+		lba += frames_now;
+	}
+	ret = 0;
+out:
+	kfree(xferbuf);
+	return ret;
+}
+
 static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
 		     unsigned long arg)
 {		
@@ -2133,57 +2236,10 @@
 		}
 	case CDROMREADAUDIO: {
 		struct cdrom_read_audio ra;
-		int lba, nr;
 
 		IOCTL_IN(arg, struct cdrom_read_audio, ra);
 
-		if (ra.addr_format == CDROM_MSF)
-			lba = msf_to_lba(ra.addr.msf.minute,
-					 ra.addr.msf.second,
-					 ra.addr.msf.frame);
-		else if (ra.addr_format == CDROM_LBA)
-			lba = ra.addr.lba;
-		else
-			return -EINVAL;
-
-		/* FIXME: we need upper bound checking, too!! */
-		if (lba < 0 || ra.nframes <= 0)
-			return -EINVAL;
-
-		/*
-		 * start with will ra.nframes size, back down if alloc fails
-		 */
-		nr = ra.nframes;
-		do {
-			cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL);
-			if (cgc.buffer)
-				break;
-
-			nr >>= 1;
-		} while (nr);
-
-		if (!nr)
-			return -ENOMEM;
-
-		if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW)) {
-			kfree(cgc.buffer);
-			return -EFAULT;
-		}
-		cgc.data_direction = CGC_DATA_READ;
-		while (ra.nframes > 0) {
-			if (nr > ra.nframes)
-				nr = ra.nframes;
-
-			ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
-			if (ret)
-				break;
-			__copy_to_user(ra.buf, cgc.buffer, CD_FRAMESIZE_RAW*nr);
-			ra.buf += CD_FRAMESIZE_RAW * nr;
-			ra.nframes -= nr;
-			lba += nr;
-		}
-		kfree(cgc.buffer);
-		return ret;
+		return cdda_read_audio(cmd, cdi, &cgc, &ra);
 		}
 	case CDROMSUBCHNL: {
 		struct cdrom_subchnl q;
diff -ru linux-2.4.20-pre4-ac1.orig/drivers/ide/Config.in linux-2.4.20-pre4-ac1/drivers/ide/Config.in
--- linux-2.4.20-pre4-ac1.orig/drivers/ide/Config.in	Sat Aug 24 10:55:26 2002
+++ linux-2.4.20-pre4-ac1/drivers/ide/Config.in	Sun Aug 25 21:20:26 2002
@@ -29,6 +29,9 @@
 
    dep_tristate '  PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA
    dep_tristate '  Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
+   if [ "$CONFIG_BLK_DEV_IDECD" != "n" ]; then
+      dep_mbool '    Use DMA for IDE/ATAPI CD-ROM audio' CONFIG_DMA_IDECD $CONFIG_BLK_DEV_IDECD
+   fi
    dep_mbool '    Reduce media failure retries support' CONFIG_BLK_DEV_IDECD_BAILOUT $CONFIG_BLK_DEV_IDECD
    dep_tristate '  Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
    dep_tristate '  Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
diff -rauN linux-2.4.19-rc1/include/linux/cdrom.h linux-2.4.19-rc1-idecd/include/linux/cdrom.h
--- linux-2.4.19-rc1/include/linux/cdrom.h	Thu Nov 22 20:47:04 2001
+++ linux-2.4.19-rc1-idecd/include/linux/cdrom.h	Sat Jul 13 12:09:08 2002
@@ -287,6 +287,10 @@
 	unsigned char		data_direction;
 	int			quiet;
 	int			timeout;
+#ifdef CONFIG_DMA_IDECD
+	int			do_dma;		/* Try to use DMA */
+	int			dma_error;	/* A DMA_specific error occurred */
+#endif
 	void			*reserved[1];
 };
 
@@ -743,10 +747,21 @@
     	char name[20];                  /* name of the device type */
 /* per-device flags */
         __u8 sanyo_slot		: 2;	/* Sanyo 3 CD changer support */
-        __u8 reserved		: 6;	/* not used yet */
+#ifdef CONFIG_DMA_IDECD
+        __u8 dma_mode		: 2;	/* See below */
+        __u8 reserved		: 4;	/* not used yet */
+#else
+	 __u8 reserved		: 6;	/* not used yet */
+#endif
 	struct cdrom_write_settings write;
 };
 
+#ifdef CONFIG_DMA_IDECD
+#define CDROM_DMA_MULTI		0	/* Multiframe DMA (default) */
+#define CDROM_DMA_SINGLE	1	/* Single frame DMA */
+#define CDROM_DMA_NONE		2	/* Multiframe PIO */
+#endif
+
 struct cdrom_device_ops {
 /* routines */
 	int (*open) (struct cdrom_device_info *, int);
