patch-2.4.7 linux/drivers/sound/ymfpci.c

Next file: linux/drivers/sound/ymfpci.h
Previous file: linux/drivers/sound/wavfront.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.6/linux/drivers/sound/ymfpci.c linux/drivers/sound/ymfpci.c
@@ -23,14 +23,9 @@
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * TODO:
- *  - Use P44Slot for 44.1 playback.
+ *  - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot).
  *  - 96KHz playback for DVD - use pitch of 2.0.
- *  - uLaw for Sun apps.
- *     : Alan says firmly "no software format conversion in kernel".
  *  - Retain DMA buffer on close, do not wait the end of frame.
- *  - Cleanup
- *      ? underused structure members
- *      - Remove remaining P3 tags (debug messages).
  *  - Resolve XXX tagged questions.
  *  - Cannot play 5133Hz.
  *  - 2001/01/07 Consider if we can remove voice_lock, like so:
@@ -66,14 +61,28 @@
 #endif
 #include "ymfpci.h"
 
-static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd);
+/*
+ * I do not believe in debug levels as I never can guess what
+ * part of the code is going to be problematic in the future.
+ * Don't forget to run your klogd with -c 8.
+ */
+/* #define YMFDBG(fmt, arg...)  do{ printk(KERN_DEBUG fmt, ##arg); }while(0) */
+#define YMFDBGW(fmt, arg...)  /* */
+#define YMFDBGI(fmt, arg...)  /* */
+
+static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);
 static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);
-static void ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
+static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice);
 static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank);
 static int ymf_playback_prepare(struct ymf_state *state);
 static int ymf_capture_prepare(struct ymf_state *state);
 static struct ymf_state *ymf_state_alloc(ymfpci_t *unit);
 
+static void ymfpci_aclink_reset(struct pci_dev * pci);
+static void ymfpci_disable_dsp(ymfpci_t *unit);
+static void ymfpci_download_image(ymfpci_t *codec);
+static void ymf_memload(ymfpci_t *unit);
+
 static LIST_HEAD(ymf_devs);
 
 /*
@@ -237,8 +246,7 @@
 }
 
 /*
- * XXX Find if this function exists in the OSS framework.
- * XXX Make sure we do no panic when ADPCM is selected.
+ * We ever allow only a few formats, but let's be generic, for smaller surprise.
  */
 static int ymf_pcm_format_width(int format)
 {
@@ -330,7 +338,7 @@
 	spin_lock_irqsave(&state->unit->reg_lock, flags);
 	dmabuf->hwptr = dmabuf->swptr = 0;
 	dmabuf->total_bytes = 0;
-	dmabuf->count = dmabuf->error = 0;
+	dmabuf->count = 0;
 	spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 
 	/* allocate DMA buffer if not allocated yet */
@@ -579,9 +587,9 @@
 	dmabuf = &ypcm->dmabuf;
 	spin_lock(&codec->reg_lock);
 	if (ypcm->running) {
-/* P3 */ /** printk("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n",
-  voice->number, codec->active_bank, dmabuf->count,
-  voice->bank[0].start, voice->bank[1].start); **/
+		YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n",
+		   voice->number, codec->active_bank, dmabuf->count,
+		   voice->bank[0].start, voice->bank[1].start);
 		silence = (ymf_pcm_format_width(state->format.format) == 16) ?
 		    0 : 0x80;
 		/* We need actual left-hand-side redzone size here. */
@@ -810,8 +818,6 @@
 		end >>= 1;
 	if (w_16)
 		end >>= 1;
-/* P3 */ // printk("ymf_pcm_init_voice: %d: Rate %d Format 0x%08x Delta 0x%x End 0x%x\n",
-//  voice->number, rate, format, delta, end);
 	for (nbank = 0; nbank < 2; nbank++) {
 		bank = &voice->bank[nbank];
 		bank->format = format;
@@ -907,7 +913,7 @@
 
 	if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {
 		/* Somebody started 32 mpg123's in parallel? */
-		/* P3 */ printk("ymfpci%d: cannot allocate voice\n",
+		printk(KERN_INFO "ymfpci%d: cannot allocate voice\n",
 		    state->unit->dev_audio);
 		return err;
 	}
@@ -1178,6 +1184,7 @@
 {
 	struct ymf_state *state = (struct ymf_state *)file->private_data;
 	struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf;
+	struct ymf_unit *unit = state->unit;
 	DECLARE_WAITQUEUE(waita, current);
 	ssize_t ret;
 	unsigned long flags;
@@ -1190,18 +1197,26 @@
 		return -ENXIO;
 	if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
 		return ret;
-	if (!access_ok(VERIFY_WRITE, buffer, count))
-		return -EFAULT;
 	ret = 0;
 
 	add_wait_queue(&dmabuf->wait, &waita);
 	while (count > 0) {
-		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret) ret = -EAGAIN;
+				break;
+			}
+			continue;
+		}
 		swptr = dmabuf->swptr;
 		cnt = dmabuf->dmasize - swptr;
 		if (dmabuf->count < cnt)
 			cnt = dmabuf->count;
-		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
 
 		if (cnt > count)
 			cnt = count;
@@ -1240,7 +1255,7 @@
 			}
 			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 			if (signal_pending(current)) {
-				ret = ret ? ret : -ERESTARTSYS;
+				if (!ret) ret = -ERESTARTSYS;
 				break;
 			}
 			continue;
@@ -1253,19 +1268,24 @@
 
 		swptr = (swptr + cnt) % dmabuf->dmasize;
 
-		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			continue;
+		}
+
 		dmabuf->swptr = swptr;
 		dmabuf->count -= cnt;
-		// spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		// spin_unlock_irqrestore(&unit->reg_lock, flags);
 
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
-		// spin_lock_irqsave(&state->unit->reg_lock, flags);
+		// spin_lock_irqsave(&unit->reg_lock, flags);
 		if (!state->rpcm.running) {
-			ymf_capture_trigger(state->unit, &state->rpcm, 1);
+			ymf_capture_trigger(unit, &state->rpcm, 1);
 		}
-		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
 	}
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&dmabuf->wait, &waita);
@@ -1278,6 +1298,7 @@
 {
 	struct ymf_state *state = (struct ymf_state *)file->private_data;
 	struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf;
+	struct ymf_unit *unit = state->unit;
 	DECLARE_WAITQUEUE(waita, current);
 	ssize_t ret;
 	unsigned long flags;
@@ -1286,7 +1307,7 @@
 	int redzone;
 	int delay;
 
-/* P3 */ /* printk("ymf_write: count %d\n", count); */
+	YMFDBGW("ymf_write: count %d\n", count);
 
 	if (ppos != &file->f_pos)
 		return -ESPIPE;
@@ -1294,8 +1315,6 @@
 		return -ENXIO;
 	if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
 		return ret;
-	if (!access_ok(VERIFY_READ, buffer, count))
-		return -EFAULT;
 	ret = 0;
 
 	/*
@@ -1310,7 +1329,17 @@
 
 	add_wait_queue(&dmabuf->wait, &waita);
 	while (count > 0) {
-		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret) ret = -EAGAIN;
+				break;
+			}
+			continue;
+		}
 		if (dmabuf->count < 0) {
 			printk(KERN_ERR
 			   "ymf_write: count %d, was legal in cs46xx\n",
@@ -1342,22 +1371,22 @@
 		cnt = dmabuf->dmasize - swptr;
 		if (dmabuf->count + cnt > dmabuf->dmasize - redzone)
 			cnt = (dmabuf->dmasize - redzone) - dmabuf->count;
-		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
 
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
-/* P3 */ /* printk("ymf_write: full, count %d swptr %d\n",
-  dmabuf->count, dmabuf->swptr); */
+			YMFDBGW("ymf_write: full, count %d swptr %d\n",
+			   dmabuf->count, dmabuf->swptr);
 			/*
 			 * buffer is full, start the dma machine and
 			 * wait for data to be played
 			 */
-			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			spin_lock_irqsave(&unit->reg_lock, flags);
 			if (!state->wpcm.running) {
-				ymf_playback_trigger(state->unit, &state->wpcm, 1);
+				ymf_playback_trigger(unit, &state->wpcm, 1);
 			}
-			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
 				break;
@@ -1379,7 +1408,11 @@
 			swptr -= dmabuf->dmasize;
 		}
 
-		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			continue;
+		}
 		dmabuf->swptr = swptr;
 		dmabuf->count += cnt;
 
@@ -1393,10 +1426,10 @@
 		delay = state->format.rate / 20;	/* 50ms */
 		delay <<= state->format.shift;
 		if (dmabuf->count >= delay && !state->wpcm.running) {
-			ymf_playback_trigger(state->unit, &state->wpcm, 1);
+			ymf_playback_trigger(unit, &state->wpcm, 1);
 		}
 
-		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
 
 		count -= cnt;
 		buffer += cnt;
@@ -1406,7 +1439,7 @@
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&dmabuf->wait, &waita);
 
-/* P3 */ /* printk("ymf_write: dmabuf.count %d\n", dmabuf->count); */
+	YMFDBGW("ymf_write: dmabuf.count %d\n", dmabuf->count);
 	return ret;
 }
 
@@ -1621,7 +1654,6 @@
 	case SNDCTL_DSP_CHANNELS:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */
 		if (val != 0) {
 			if (file->f_mode & FMODE_WRITE) {
 				ymf_wait_dac(state);
@@ -1667,19 +1699,6 @@
 		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
 		return 0;
 
-#if 0 /* XXX Was dummy implementation anyways. Make sense of this. */
-	case SNDCTL_DSP_SUBDIVIDE:
-		dmabuf = &state->wpcm.dmabuf;
-		if (dmabuf->subdivision)
-			return -EINVAL;
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (val != 1 && val != 2)
-			return -EINVAL;
-		dmabuf->subdivision = val;
-		return 0;
-#endif
-
 	case SNDCTL_DSP_SETFRAGMENT:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
@@ -1729,40 +1748,6 @@
 			    (int *)arg); */
 		return put_user(0, (int *)arg);
 
-#if 0 /* not implememnted, yet? */
-	case SNDCTL_DSP_GETTRIGGER:
-		val = 0;
-		dmabuf = &state->wpcm.dmabuf;
-		if (file->f_mode & FMODE_READ && dmabuf->enable)
-			val |= PCM_ENABLE_INPUT;
-		if (file->f_mode & FMODE_WRITE && dmabuf->enable)
-			val |= PCM_ENABLE_OUTPUT;
-		return put_user(val, (int *)arg);
-
-	case SNDCTL_DSP_SETTRIGGER:
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (file->f_mode & FMODE_READ) {
-			dmabuf = &state->rpcm.dmabuf;
-			if (val & PCM_ENABLE_INPUT) {
-				if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
-					return ret;
-				start_adc(state);
-			} else
-				stop_adc(state);
-		}
-		if (file->f_mode & FMODE_WRITE) {
-			dmabuf = &state->wpcm.dmabuf;
-			if (val & PCM_ENABLE_OUTPUT) {
-				if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
-					return ret;
-				start_dac(state);  // sure?
-			} else
-				stop_dac(state);
-		}
-		return 0;
-#endif
-
 	case SNDCTL_DSP_GETIPTR:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
@@ -1794,18 +1779,6 @@
 	case SNDCTL_DSP_SETDUPLEX:	/* XXX TODO */
 		return -EINVAL;
 
-#if 0 /* XXX implement when an app found that uses it. */
-	case SNDCTL_DSP_GETODELAY:
-		if (!(file->f_mode & FMODE_WRITE))
-			return -EINVAL;
-		spin_lock_irqsave(&state->unit->reg_lock, flags);
-		cs_update_ptr(state);
-		dmabuf = &state->wpcm.dmabuf;
-		val = dmabuf->count;
-		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
-		return put_user(val, (int *)arg);
-#endif
-
 	case SOUND_PCM_READ_RATE:
 		return put_user(state->format.rate, (int *)arg);
 
@@ -1827,7 +1800,6 @@
 		 * Some programs mix up audio devices and ioctls
 		 * or perhaps they expect "universal" ioctls,
 		 * for instance we get SNDCTL_TMR_CONTINUE here.
-		 * XXX Is there sound_generic_ioctl() around?
 		 */
 		 break;
 	}
@@ -2017,6 +1989,79 @@
 };
 
 /*
+ */
+
+static int ymf_suspend(struct pci_dev *pcidev, u32 unused)
+{
+	struct ymf_unit *unit = pci_get_drvdata(pcidev);
+	unsigned long flags;
+	struct ymf_dmabuf *dmabuf;
+	struct list_head *p;
+	struct ymf_state *state;
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+
+	unit->suspended = 1;
+
+	list_for_each(p, &unit->states) {
+		state = list_entry(p, struct ymf_state, chain);
+
+		dmabuf = &state->wpcm.dmabuf;
+		dmabuf->hwptr = dmabuf->swptr = 0;
+		dmabuf->total_bytes = 0;
+		dmabuf->count = 0;
+
+		dmabuf = &state->rpcm.dmabuf;
+		dmabuf->hwptr = dmabuf->swptr = 0;
+		dmabuf->total_bytes = 0;
+		dmabuf->count = 0;
+	}
+
+	ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0);
+	ymfpci_disable_dsp(unit);
+
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+	
+	return 0;
+}
+
+static int ymf_resume(struct pci_dev *pcidev)
+{
+	struct ymf_unit *unit = pci_get_drvdata(pcidev);
+	unsigned long flags;
+	struct list_head *p;
+	struct ymf_state *state;
+
+	ymfpci_aclink_reset(unit->pci);
+	ymfpci_codec_ready(unit, 0, 1);		/* prints diag if not ready. */
+
+#ifdef CONFIG_SOUND_YMFPCI_LEGACY
+	/* XXX At this time the legacy registers are probably deprogrammed. */
+#endif
+
+	ymfpci_download_image(unit);
+
+	ymf_memload(unit);
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+
+	if (unit->start_count) {
+		ymfpci_writel(unit, YDSXGR_MODE, 3);
+		unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1;
+	}
+
+	unit->suspended = 0;
+	list_for_each(p, &unit->states) {
+		state = list_entry(p, struct ymf_state, chain);
+		wake_up(&state->wpcm.dmabuf.wait);
+		wake_up(&state->rpcm.dmabuf.wait);
+	}
+
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+	return 0;
+}
+
+/*
  *  initialization routines
  */
 
@@ -2176,6 +2221,9 @@
 	}
 
 	ymfpci_enable_dsp(codec);
+
+	/* 0.02s sounds not too bad, we may do schedule_timeout() later. */
+	mdelay(20); /* seems we need some delay after downloading image.. */
 }
 
 static int ymfpci_memalloc(ymfpci_t *codec)
@@ -2239,28 +2287,6 @@
 	ptr += (codec->bank_size_effect + 0x00ff) & ~0x00ff;
 	codec->work_base = ptr;
 
-	ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, virt_to_bus(codec->bank_base_playback));
-	ymfpci_writel(codec, YDSXGR_RECCTRLBASE, virt_to_bus(codec->bank_base_capture));
-	ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, virt_to_bus(codec->bank_base_effect));
-	ymfpci_writel(codec, YDSXGR_WORKBASE, virt_to_bus(codec->work_base));
-	ymfpci_writel(codec, YDSXGR_WORKSIZE, codec->work_size >> 2);
-
-	/* S/PDIF output initialization */
-	ymfpci_writew(codec, YDSXGR_SPDIFOUTCTRL, 0);
-	ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS,
-		SND_PCM_AES0_CON_EMPHASIS_NONE |
-		(SND_PCM_AES1_CON_ORIGINAL << 8) |
-		(SND_PCM_AES1_CON_PCM_CODER << 8));
-
-	/* S/PDIF input initialization */
-	ymfpci_writew(codec, YDSXGR_SPDIFINCTRL, 0);
-
-	/* move this volume setup to mixer */
-	ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
-	ymfpci_writel(codec, YDSXGR_BUF441OUTVOL, 0);
-	ymfpci_writel(codec, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
-	ymfpci_writel(codec, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
-
 	return 0;
 }
 
@@ -2274,6 +2300,32 @@
 	kfree(codec->work_ptr);
 }
 
+static void ymf_memload(ymfpci_t *unit)
+{
+
+	ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, virt_to_bus(unit->bank_base_playback));
+	ymfpci_writel(unit, YDSXGR_RECCTRLBASE, virt_to_bus(unit->bank_base_capture));
+	ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, virt_to_bus(unit->bank_base_effect));
+	ymfpci_writel(unit, YDSXGR_WORKBASE, virt_to_bus(unit->work_base));
+	ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2);
+
+	/* S/PDIF output initialization */
+	ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0);
+	ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS,
+		SND_PCM_AES0_CON_EMPHASIS_NONE |
+		(SND_PCM_AES1_CON_ORIGINAL << 8) |
+		(SND_PCM_AES1_CON_PCM_CODER << 8));
+
+	/* S/PDIF input initialization */
+	ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0);
+
+	/* move this volume setup to mixer */
+	ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+	ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0);
+	ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
+	ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
+}
+
 static int ymf_ac97_init(ymfpci_t *unit, int num_ac97)
 {
 	struct ac97_codec *codec;
@@ -2380,12 +2432,9 @@
 
 	ymfpci_download_image(codec);
 
-	udelay(100); /* seems we need some delay after downloading image.. */
-
 	if (ymfpci_memalloc(codec) < 0)
 		goto out_disable_dsp;
-
-	/* ymfpci_proc_init(unit, codec); */
+	ymf_memload(codec);
 
 	if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
 		printk(KERN_ERR "ymfpci: unable to request IRQ %d\n",
@@ -2481,6 +2530,8 @@
 	id_table:	ymf_id_tbl,
 	probe:		ymf_probe_one,
 	remove:         ymf_remove_one,
+	suspend:	ymf_suspend,
+	resume:		ymf_resume
 };
 
 static int __init ymf_init_module(void)

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)