All of lore.kernel.org
 help / color / mirror / Atom feed
From: Georg Chini <georg.chini@triaton-webhosting.com>
To: sparclinux@vger.kernel.org
Subject: PATCH: cs4231 on sbus
Date: Sun, 16 Oct 2005 16:28:19 +0000	[thread overview]
Message-ID: <43527FA3.8090301@triaton-webhosting.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 370 bytes --]

Hello,

here is another cs4231 patch which makes
it work on my ultra2. Tested by Christopher
Zimmermann and myself. It introduces some
sbus_dma routines similar to the ebus_dma
stuff to make the code look nearly the same
for both cases. I hope it does not break ebus.
Patch against 2.6.14-rc2-git4. Thanks to
Christopher for testing.

Regards
             Georg Chini



[-- Attachment #2: cs4231.diff --]
[-- Type: text/plain, Size: 13000 bytes --]

--- linux-2.6.14-rc2/sound/sparc/cs4231.c	2005-09-24 22:55:56.000000000 +0200
+++ linux-2.6.14-rc2_patched/sound/sparc/cs4231.c	2005-10-16 17:31:41.000000000 +0200
@@ -61,6 +61,14 @@
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Sun,CS4231}}");
 
+#ifdef SBUS_SUPPORT
+struct sbus_dma_info {
+       spinlock_t      lock;
+       int             dir;
+       void __iomem    *regs;
+};
+#endif
+
 typedef struct snd_cs4231 {
 	spinlock_t		lock;
 	void __iomem		*port;
@@ -69,6 +77,11 @@
 	struct ebus_dma_info	eb2p;
 #endif
 
+#ifdef SBUS_SUPPORT
+	struct sbus_dma_info	sb2c;
+	struct sbus_dma_info	sb2p;
+#endif
+
 	u32			flags;
 #define CS4231_FLAG_EBUS	0x00000001
 #define CS4231_FLAG_PLAYBACK	0x00000002
@@ -251,6 +264,15 @@
 #define APCPNVA	0x38UL	/* APC Play DMA Next Address */
 #define APCPNC	0x3cUL	/* APC Play Next Count */
 
+/* Defines for SBUS DMA-routines */
+
+#define APCVA  0x0UL	/* APC DMA Address */
+#define APCC   0x4UL	/* APC Count */
+#define APCNVA 0x8UL	/* APC DMA Next Address */
+#define APCNC  0xcUL	/* APC Next Count */
+#define APC_PLAY 0x30UL	/* Play registers start at 0x30 */
+#define APC_RECORD 0x20UL /* Record registers start at 0x20 */
+
 /* APCCSR bits */
 
 #define APC_INT_PENDING 0x800000 /* Interrupt Pending */
@@ -472,6 +494,103 @@
 }
 
 /*
+ * SBUS DMA routines
+ */
+#ifdef SBUS_SUPPORT
+
+int sbus_dma_request(struct sbus_dma_info *base, dma_addr_t bus_addr, size_t len)
+{
+	unsigned long flags;
+	u32 test, csr;
+	int err;
+	
+	if (len >= (1 << 24))
+		return -EINVAL;
+	spin_lock_irqsave(&base->lock, flags);
+	csr = sbus_readl(base->regs + APCCSR);
+	err = -EINVAL;
+	test = APC_CDMA_READY;
+	if ( base->dir == APC_PLAY )
+		test = APC_PDMA_READY;
+	if (!(csr & test))
+		goto out;
+	err = -EBUSY;
+	csr = sbus_readl(base->regs + APCCSR);
+	test = APC_XINT_CNVA;
+	if ( base->dir == APC_PLAY )
+		test = APC_XINT_PNVA;
+	if (!(csr & test))
+		goto out;
+	err = 0;
+	sbus_writel(bus_addr, base->regs + base->dir + APCNVA);
+	sbus_writel(len, base->regs + base->dir + APCNC);
+out:
+	spin_unlock_irqrestore(&base->lock, flags);
+	return err;
+}
+
+void sbus_dma_prepare(struct sbus_dma_info *base)
+{
+	unsigned long flags;
+	u32 csr, test;
+
+	spin_lock_irqsave(&base->lock, flags);
+	csr = sbus_readl(base->regs + APCCSR);
+	test =  APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA |
+		APC_XINT_PLAY | APC_XINT_PEMP | APC_XINT_GENL |
+		 APC_XINT_PENA;
+	if ( base->dir == APC_RECORD )
+		test = APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA |
+			APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL;
+	csr |= test;
+	sbus_writel(csr, base->regs + APCCSR);
+	spin_unlock_irqrestore(&base->lock, flags);
+}
+
+void sbus_dma_enable(struct sbus_dma_info *base, int on)
+{
+	unsigned long flags;
+	u32 csr, shift;
+
+	spin_lock_irqsave(&base->lock, flags);
+	if (!on) {
+		if (base->dir == APC_PLAY) { 
+			sbus_writel(0, base->regs + base->dir + APCNVA); 
+			sbus_writel(1, base->regs + base->dir + APCC); 
+		}
+		else
+		{
+			sbus_writel(0, base->regs + base->dir + APCNC); 
+			sbus_writel(0, base->regs + base->dir + APCVA); 
+		} 
+	} 
+	udelay(500); 
+	csr = sbus_readl(base->regs + APCCSR);
+	shift = 0;
+	if ( base->dir == APC_PLAY )
+		shift = 1;
+	if (on)
+		csr &= ~(APC_CPAUSE << shift);
+	else
+		csr |= (APC_CPAUSE << shift); 
+	sbus_writel(csr, base->regs + APCCSR);
+	if (on)
+		csr |= (APC_CDMA_READY << shift);
+	else
+		csr &= ~(APC_CDMA_READY << shift);
+	sbus_writel(csr, base->regs + APCCSR);
+	
+	spin_unlock_irqrestore(&base->lock, flags);
+}
+
+unsigned int sbus_dma_addr(struct sbus_dma_info *base)
+{
+        return sbus_readl(base->regs + base->dir + APCVA);
+}
+
+#endif
+
+/*
  *  CS4231 detection / MCE routines
  */
 
@@ -589,29 +708,21 @@
 #endif
 
 #ifdef SBUS_SUPPORT
-static void snd_cs4231_sbus_advance_dma(snd_pcm_substream_t *substream, unsigned int *periods_sent)
+static void snd_cs4231_sbus_advance_dma(struct sbus_dma_info *p, snd_pcm_substream_t *substream, unsigned int *periods_sent)
 {
-	cs4231_t *chip = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
-	unsigned int period_size = snd_pcm_lib_period_bytes(substream);
-	unsigned int offset = period_size * (*periods_sent % runtime->periods);
-
-	if (runtime->period_size > 0xffff + 1)
-		BUG();
-
-	switch (substream->stream) {
-	case SNDRV_PCM_STREAM_PLAYBACK:
-		sbus_writel(runtime->dma_addr + offset, chip->port + APCPNVA);
-		sbus_writel(period_size, chip->port + APCPNC);
-		break;
-	case SNDRV_PCM_STREAM_CAPTURE:
-		sbus_writel(runtime->dma_addr + offset, chip->port + APCCNVA);
-		sbus_writel(period_size, chip->port + APCCNC);
-		break;
-	}
+	 while (1) {  
+		unsigned int period_size = snd_pcm_lib_period_bytes(substream);
+		unsigned int offset = period_size * (*periods_sent);
 
-	(*periods_sent) = (*periods_sent + 1) % runtime->periods;
+		if (period_size > 0xffff + 1)
+			BUG();
+	
+		if (sbus_dma_request(p, runtime->dma_addr + offset, period_size))
+			return;
+		(*periods_sent) = (*periods_sent + 1) % runtime->periods;
+	} 
 }
 #endif
 
@@ -646,59 +757,27 @@
 	} else {
 #endif
 #ifdef SBUS_SUPPORT
-	u32 csr = sbus_readl(chip->port + APCCSR);
-	/* I don't know why, but on sbus the period counter must
-	 * only start counting after the first period is sent.
-	 * Therefore this dummy thing.
-	 */
-	unsigned int dummy = 0;
-
-	switch (what) {
-	case CS4231_PLAYBACK_ENABLE:
+	if (what & CS4231_PLAYBACK_ENABLE) {
 		if (on) {
-			csr &= ~APC_XINT_PLAY;
-			sbus_writel(csr, chip->port + APCCSR);
-
-			csr &= ~APC_PPAUSE;
-			sbus_writel(csr, chip->port + APCCSR);
-
-			snd_cs4231_sbus_advance_dma(substream, &dummy);
-
-			csr |=  APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA |
-				APC_XINT_PLAY | APC_XINT_EMPT | APC_XINT_GENL |
-				APC_XINT_PENA | APC_PDMA_READY;
-			sbus_writel(csr, chip->port + APCCSR);
+			sbus_dma_prepare(&chip->sb2p);
+			sbus_dma_enable(&chip->sb2p, 1);
+			snd_cs4231_sbus_advance_dma(&chip->sb2p,
+				chip->playback_substream,
+				&chip->p_periods_sent);
 		} else {
-			csr |= APC_PPAUSE;
-			sbus_writel(csr, chip->port + APCCSR);
-
-			csr &= ~APC_PDMA_READY;
-			sbus_writel(csr, chip->port + APCCSR);
+			sbus_dma_enable(&chip->sb2p, 0);
 		}
-		break;
-	case CS4231_RECORD_ENABLE:
+	}
+	if (what & CS4231_RECORD_ENABLE) {
 		if (on) {
-			csr &= ~APC_XINT_CAPT;
-			sbus_writel(csr, chip->port + APCCSR);
-
-			csr &= ~APC_CPAUSE;
-			sbus_writel(csr, chip->port + APCCSR);
-
-			snd_cs4231_sbus_advance_dma(substream, &dummy);
-
-			csr |=  APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA |
-				APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL |
-				APC_CDMA_READY;
-
-			sbus_writel(csr, chip->port + APCCSR);
+			sbus_dma_prepare(&chip->sb2c);
+			sbus_dma_enable(&chip->sb2c, 1);
+			snd_cs4231_sbus_advance_dma(&chip->sb2c,
+				chip->capture_substream,
+				&chip->c_periods_sent);
 		} else {
-			csr |= APC_CPAUSE;
-			sbus_writel(csr, chip->port + APCCSR);
-
-			csr &= ~APC_CDMA_READY;
-			sbus_writel(csr, chip->port + APCCSR);
+			sbus_dma_enable(&chip->sb2c, 0);
 		}
-		break;
 	}
 #endif
 #ifdef EBUS_SUPPORT
@@ -1136,10 +1215,7 @@
 	if (runtime->period_size > 0xffff + 1)
 		BUG();
 
-	snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (runtime->period_size - 1) & 0x00ff);
-	snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (runtime->period_size - 1) >> 8 & 0x00ff);
 	chip->p_periods_sent = 0;
-
 	spin_unlock_irqrestore(&chip->lock, flags);
 
 	return 0;
@@ -1171,16 +1247,14 @@
 static int snd_cs4231_capture_prepare(snd_pcm_substream_t *substream)
 {
 	cs4231_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
 	unsigned long flags;
 
 	spin_lock_irqsave(&chip->lock, flags);
 	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE |
 					    CS4231_RECORD_PIO);
 
-	snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (runtime->period_size - 1) & 0x00ff);
-	snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (runtime->period_size - 1) >> 8 & 0x00ff);
 
+	chip->c_periods_sent = 0;
 	spin_unlock_irqrestore(&chip->lock, flags);
 
 	return 0;
@@ -1199,40 +1273,20 @@
 		chip->capture_substream->runtime->overrange++;
 }
 
-static irqreturn_t snd_cs4231_generic_interrupt(cs4231_t *chip)
+#ifdef SBUS_SUPPORT
+static irqreturn_t snd_cs4231_sbus_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	unsigned long flags;
 	unsigned char status;
+	u32 csr;
+	cs4231_t *chip = dev_id;
 
 	/*This is IRQ is not raised by the cs4231*/
 	if (!(__cs4231_readb(chip, CS4231P(chip, STATUS)) & CS4231_GLOBALIRQ))
 		return IRQ_NONE;
 
-	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
-
-	if (status & CS4231_TIMER_IRQ) {
-		if (chip->timer)
-			snd_timer_interrupt(chip->timer, chip->timer->sticks);
-	}		
-
-	if (status & CS4231_RECORD_IRQ)
-		snd_cs4231_overrange(chip);
-
-	/* ACK the CS4231 interrupt. */
-	spin_lock_irqsave(&chip->lock, flags);
-	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
-	spin_unlock_irqrestore(&chip->lock, flags);
-
-	return 0;
-}
-
-#ifdef SBUS_SUPPORT
-static irqreturn_t snd_cs4231_sbus_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	cs4231_t *chip = dev_id;
-
 	/* ACK the APC interrupt. */
-	u32 csr = sbus_readl(chip->port + APCCSR);
+	csr = sbus_readl(chip->port + APCCSR);
 
 	sbus_writel(csr, chip->port + APCCSR);
 
@@ -1240,20 +1294,36 @@
 	    (csr & APC_PLAY_INT) &&
 	    (csr & APC_XINT_PNVA) &&
 	    !(csr & APC_XINT_EMPT)) {
-		snd_cs4231_sbus_advance_dma(chip->playback_substream,
-					    &chip->p_periods_sent);
 		snd_pcm_period_elapsed(chip->playback_substream);
+		snd_cs4231_sbus_advance_dma(&chip->sb2p, chip->playback_substream,
+					    &chip->p_periods_sent);
 	}
 
 	if ((chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) &&
 	    (csr & APC_CAPT_INT) &&
-	    (csr & APC_XINT_CNVA)) {
-		snd_cs4231_sbus_advance_dma(chip->capture_substream,
-					    &chip->c_periods_sent);
+	    (csr & APC_XINT_CNVA) &&
+	    !(csr & APC_XINT_EMPT)) {
 		snd_pcm_period_elapsed(chip->capture_substream);
+		snd_cs4231_sbus_advance_dma(&chip->sb2c,chip->capture_substream,
+					    &chip->c_periods_sent);
 	}
+	
+	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
+
+	if (status & CS4231_TIMER_IRQ) {
+		if (chip->timer)
+			snd_timer_interrupt(chip->timer, chip->timer->sticks);
+	}		
+
+	if (status & CS4231_RECORD_IRQ)
+		snd_cs4231_overrange(chip);
 
-	return snd_cs4231_generic_interrupt(chip);
+	/* ACK the CS4231 interrupt. */
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
 }
 #endif
 
@@ -1284,24 +1354,29 @@
 static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t *substream)
 {
 	cs4231_t *chip = snd_pcm_substream_chip(substream);
-	size_t ptr, residue, period_bytes;
-
+	size_t ptr;
+#ifdef EBUS_SUPPORT
+	size_t residue, period_bytes;
+#endif
+	
 	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
 		return 0;
+#ifdef EBUS_SUPPORT
 	period_bytes = snd_pcm_lib_period_bytes(substream);
 	ptr = period_bytes * chip->p_periods_sent;
-#ifdef EBUS_SUPPORT
 	if (chip->flags & CS4231_FLAG_EBUS) {
 		residue = ebus_dma_residue(&chip->eb2p);
+		ptr += period_bytes - residue;
 	} else {
 #endif
 #ifdef SBUS_SUPPORT
-		residue = sbus_readl(chip->port + APCPC);
+		ptr = sbus_dma_addr(&chip->sb2p);
+		if (ptr != 0)
+			ptr -= substream->runtime->dma_addr;
 #endif
 #ifdef EBUS_SUPPORT
 	}
 #endif
-	ptr += period_bytes - residue;
 
 	return bytes_to_frames(substream->runtime, ptr);
 }
@@ -1309,24 +1384,29 @@
 static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream)
 {
 	cs4231_t *chip = snd_pcm_substream_chip(substream);
-	size_t ptr, residue, period_bytes;
+	size_t ptr;
+#ifdef EBUS_SUPPORT
+	size_t residue, period_bytes;
+#endif
 	
 	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
 		return 0;
+#ifdef EBUS_SUPPORT
 	period_bytes = snd_pcm_lib_period_bytes(substream);
 	ptr = period_bytes * chip->c_periods_sent;
-#ifdef EBUS_SUPPORT
 	if (chip->flags & CS4231_FLAG_EBUS) {
 		residue = ebus_dma_residue(&chip->eb2c);
+		ptr += period_bytes - residue;
 	} else {
 #endif
 #ifdef SBUS_SUPPORT
-		residue = sbus_readl(chip->port + APCCC);
+		ptr = sbus_dma_addr(&chip->sb2c);
+		if (ptr != 0)
+			ptr -= substream->runtime->dma_addr;
 #endif
 #ifdef EBUS_SUPPORT
 	}
 #endif
-	ptr += period_bytes - residue;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -1983,6 +2063,8 @@
 		return -ENOMEM;
 
 	spin_lock_init(&chip->lock);
+	spin_lock_init(&chip->sb2c.lock);
+	spin_lock_init(&chip->sb2p.lock);
 	init_MUTEX(&chip->mce_mutex);
 	init_MUTEX(&chip->open_mutex);
 	chip->card = card;
@@ -1998,6 +2080,11 @@
 		return -EIO;
 	}
 
+	chip->sb2c.regs = chip->port;
+	chip->sb2p.regs = chip->port;
+	chip->sb2c.dir = APC_RECORD;
+	chip->sb2p.dir = APC_PLAY;
+
 	if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt,
 			SA_SHIRQ, "cs4231", chip)) {
 		snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %s\n",

             reply	other threads:[~2005-10-16 16:28 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-10-16 16:28 Georg Chini [this message]
2005-10-29 12:23 ` PATCH: cs4231 on SBus Georg Chini
2005-11-04 21:23 ` PATCH: cs4231 on sbus David S. Miller
2005-11-04 21:50 ` Georg Chini
2005-11-04 22:44 ` David S. Miller
2005-11-05  0:24 ` PATCH: cs4231 on SBus David S. Miller
2005-11-05 11:40 ` Georg Chini
2005-11-05 19:53 ` David S. Miller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=43527FA3.8090301@triaton-webhosting.com \
    --to=georg.chini@triaton-webhosting.com \
    --cc=sparclinux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.