Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a820fa7 authored by Georg Chini's avatar Georg Chini Committed by David S. Miller
Browse files

[SPARC]: Make SBUS dma code similar to EBUS



From: Georg Chini <georg.chini@triaton-webhosting.com>

Introduce some sbus_dma routines similar to the
ebus_dma stuff to make the code look nearly the same
for both cases.

Thanks to Christopher for testing.

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ee1858d3
Loading
Loading
Loading
Loading
+199 −112
Original line number Diff line number Diff line
@@ -61,6 +61,14 @@ MODULE_DESCRIPTION("Sun CS4231");
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 @@ typedef struct snd_cs4231 {
	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 @@ static cs4231_t *cs4231_list;
#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 */
@@ -471,6 +493,103 @@ static unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg)
	return ret;
}

/*
 * 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,30 +708,22 @@ static void snd_cs4231_ebus_advance_dma(struct ebus_dma_info *p, snd_pcm_substre
#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;

	 while (1) {  
		unsigned int period_size = snd_pcm_lib_period_bytes(substream);
	unsigned int offset = period_size * (*periods_sent % runtime->periods);
		unsigned int offset = period_size * (*periods_sent);

	if (runtime->period_size > 0xffff + 1)
		if (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;
	}

		if (sbus_dma_request(p, runtime->dma_addr + offset, period_size))
			return;
		(*periods_sent) = (*periods_sent + 1) % runtime->periods;
	} 
}
#endif

static void cs4231_dma_trigger(snd_pcm_substream_t *substream, unsigned int what, int on)
@@ -646,59 +757,27 @@ static void cs4231_dma_trigger(snd_pcm_substream_t *substream, unsigned int what
	} 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 @@ static int snd_cs4231_playback_prepare(snd_pcm_substream_t *substream)
	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_hw_free(snd_pcm_substream_t *substream)
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 @@ static void snd_cs4231_overrange(cs4231_t *chip)
		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 @@ static irqreturn_t snd_cs4231_sbus_interrupt(int irq, void *dev_id, struct pt_re
	    (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);
	}		

	return snd_cs4231_generic_interrupt(chip);
	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;
}
#endif

@@ -1284,24 +1354,29 @@ static void snd_cs4231_ebus_capture_callback(struct ebus_dma_info *p, int event,
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_playback_pointer(snd_pcm_substream_t *substr
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 @@ static int __init snd_cs4231_sbus_create(snd_card_t *card,
		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 @@ static int __init snd_cs4231_sbus_create(snd_card_t *card,
		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",