xref: /openbmc/linux/sound/parisc/harmony.c (revision e8e0929d7290cab7c5b1a3e5f5f54f73daf38038)
11da177e4SLinus Torvalds /* Hewlett-Packard Harmony audio driver
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  *   This is a driver for the Harmony audio chipset found
41da177e4SLinus Torvalds  *   on the LASI ASIC of various early HP PA-RISC workstations.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *   Copyright (C) 2004, Kyle McMartin <kyle@{debian.org,parisc-linux.org}>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *     Based on the previous Harmony incarnations by,
91da177e4SLinus Torvalds  *       Copyright 2000 (c) Linuxcare Canada, Alex deVries
101da177e4SLinus Torvalds  *       Copyright 2000-2003 (c) Helge Deller
111da177e4SLinus Torvalds  *       Copyright 2001 (c) Matthieu Delahaye
121da177e4SLinus Torvalds  *       Copyright 2001 (c) Jean-Christophe Vaugeois
131da177e4SLinus Torvalds  *       Copyright 2003 (c) Laurent Canet
141da177e4SLinus Torvalds  *       Copyright 2004 (c) Stuart Brady
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
171da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License, version 2, as
181da177e4SLinus Torvalds  *   published by the Free Software Foundation.
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
211da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
221da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
231da177e4SLinus Torvalds  *   GNU General Public License for more details.
241da177e4SLinus Torvalds  *
251da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
261da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
271da177e4SLinus Torvalds  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
281da177e4SLinus Torvalds  *
291da177e4SLinus Torvalds  * Notes:
301da177e4SLinus Torvalds  *   - graveyard and silence buffers last for lifetime of
311da177e4SLinus Torvalds  *     the driver. playback and capture buffers are allocated
321da177e4SLinus Torvalds  *     per _open()/_close().
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  * TODO:
351da177e4SLinus Torvalds  *
361da177e4SLinus Torvalds  */
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds #include <linux/init.h>
391da177e4SLinus Torvalds #include <linux/slab.h>
401da177e4SLinus Torvalds #include <linux/time.h>
411da177e4SLinus Torvalds #include <linux/wait.h>
421da177e4SLinus Torvalds #include <linux/delay.h>
431da177e4SLinus Torvalds #include <linux/module.h>
441da177e4SLinus Torvalds #include <linux/interrupt.h>
451da177e4SLinus Torvalds #include <linux/spinlock.h>
461da177e4SLinus Torvalds #include <linux/dma-mapping.h>
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds #include <sound/core.h>
491da177e4SLinus Torvalds #include <sound/pcm.h>
501da177e4SLinus Torvalds #include <sound/control.h>
511da177e4SLinus Torvalds #include <sound/rawmidi.h>
521da177e4SLinus Torvalds #include <sound/initval.h>
531da177e4SLinus Torvalds #include <sound/info.h>
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds #include <asm/io.h>
561da177e4SLinus Torvalds #include <asm/hardware.h>
571da177e4SLinus Torvalds #include <asm/parisc-device.h>
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds #include "harmony.h"
601da177e4SLinus Torvalds 
6103f9ae25STakashi Iwai static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
6203f9ae25STakashi Iwai static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
6303f9ae25STakashi Iwai module_param(index, int, 0444);
6403f9ae25STakashi Iwai MODULE_PARM_DESC(index, "Index value for Harmony driver.");
6503f9ae25STakashi Iwai module_param(id, charp, 0444);
6603f9ae25STakashi Iwai MODULE_PARM_DESC(id, "ID string for Harmony driver.");
6703f9ae25STakashi Iwai 
6803f9ae25STakashi Iwai 
691da177e4SLinus Torvalds static struct parisc_device_id snd_harmony_devtable[] = {
701da177e4SLinus Torvalds 	/* bushmaster / flounder */
711da177e4SLinus Torvalds 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A },
721da177e4SLinus Torvalds 	/* 712 / 715 */
731da177e4SLinus Torvalds 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B },
741da177e4SLinus Torvalds 	/* pace */
751da177e4SLinus Torvalds 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E },
761da177e4SLinus Torvalds 	/* outfield / coral II */
771da177e4SLinus Torvalds 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F },
781da177e4SLinus Torvalds 	{ 0, }
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds #define NAME "harmony"
841da177e4SLinus Torvalds #define PFX  NAME ": "
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds static unsigned int snd_harmony_rates[] = {
871da177e4SLinus Torvalds 	5512, 6615, 8000, 9600,
881da177e4SLinus Torvalds 	11025, 16000, 18900, 22050,
891da177e4SLinus Torvalds 	27428, 32000, 33075, 37800,
901da177e4SLinus Torvalds 	44100, 48000
911da177e4SLinus Torvalds };
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds static unsigned int rate_bits[14] = {
941da177e4SLinus Torvalds 	HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,
951da177e4SLinus Torvalds 	HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,
961da177e4SLinus Torvalds 	HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,
971da177e4SLinus Torvalds 	HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ,
981da177e4SLinus Torvalds 	HARMONY_SR_44KHZ, HARMONY_SR_48KHZ
991da177e4SLinus Torvalds };
1001da177e4SLinus Torvalds 
10167b1020dSTakashi Iwai static struct snd_pcm_hw_constraint_list hw_constraint_rates = {
1021da177e4SLinus Torvalds 	.count = ARRAY_SIZE(snd_harmony_rates),
1031da177e4SLinus Torvalds 	.list = snd_harmony_rates,
1041da177e4SLinus Torvalds 	.mask = 0,
1051da177e4SLinus Torvalds };
1061da177e4SLinus Torvalds 
10767b1020dSTakashi Iwai static inline unsigned long
10867b1020dSTakashi Iwai harmony_read(struct snd_harmony *h, unsigned r)
1091da177e4SLinus Torvalds {
1101da177e4SLinus Torvalds 	return __raw_readl(h->iobase + r);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
11367b1020dSTakashi Iwai static inline void
11467b1020dSTakashi Iwai harmony_write(struct snd_harmony *h, unsigned r, unsigned long v)
1151da177e4SLinus Torvalds {
1161da177e4SLinus Torvalds 	__raw_writel(v, h->iobase + r);
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds 
11967b1020dSTakashi Iwai static inline void
12067b1020dSTakashi Iwai harmony_wait_for_control(struct snd_harmony *h)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds 	while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ;
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
12567b1020dSTakashi Iwai static inline void
12667b1020dSTakashi Iwai harmony_reset(struct snd_harmony *h)
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds 	harmony_write(h, HARMONY_RESET, 1);
1291da177e4SLinus Torvalds 	mdelay(50);
1301da177e4SLinus Torvalds 	harmony_write(h, HARMONY_RESET, 0);
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds static void
13467b1020dSTakashi Iwai harmony_disable_interrupts(struct snd_harmony *h)
1351da177e4SLinus Torvalds {
1361da177e4SLinus Torvalds 	u32 dstatus;
1371da177e4SLinus Torvalds 	harmony_wait_for_control(h);
1381da177e4SLinus Torvalds 	dstatus = harmony_read(h, HARMONY_DSTATUS);
1391da177e4SLinus Torvalds 	dstatus &= ~HARMONY_DSTATUS_IE;
1401da177e4SLinus Torvalds 	harmony_write(h, HARMONY_DSTATUS, dstatus);
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds static void
14467b1020dSTakashi Iwai harmony_enable_interrupts(struct snd_harmony *h)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	u32 dstatus;
1471da177e4SLinus Torvalds 	harmony_wait_for_control(h);
1481da177e4SLinus Torvalds 	dstatus = harmony_read(h, HARMONY_DSTATUS);
1491da177e4SLinus Torvalds 	dstatus |= HARMONY_DSTATUS_IE;
1501da177e4SLinus Torvalds 	harmony_write(h, HARMONY_DSTATUS, dstatus);
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds static void
15467b1020dSTakashi Iwai harmony_mute(struct snd_harmony *h)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds 	unsigned long flags;
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 	spin_lock_irqsave(&h->mixer_lock, flags);
1591da177e4SLinus Torvalds 	harmony_wait_for_control(h);
1601da177e4SLinus Torvalds 	harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE);
1611da177e4SLinus Torvalds 	spin_unlock_irqrestore(&h->mixer_lock, flags);
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds static void
16567b1020dSTakashi Iwai harmony_unmute(struct snd_harmony *h)
1661da177e4SLinus Torvalds {
1671da177e4SLinus Torvalds 	unsigned long flags;
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	spin_lock_irqsave(&h->mixer_lock, flags);
1701da177e4SLinus Torvalds 	harmony_wait_for_control(h);
1711da177e4SLinus Torvalds 	harmony_write(h, HARMONY_GAINCTL, h->st.gain);
1721da177e4SLinus Torvalds 	spin_unlock_irqrestore(&h->mixer_lock, flags);
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds static void
17667b1020dSTakashi Iwai harmony_set_control(struct snd_harmony *h)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds 	u32 ctrl;
1791da177e4SLinus Torvalds 	unsigned long flags;
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	spin_lock_irqsave(&h->lock, flags);
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	ctrl = (HARMONY_CNTL_C      |
1841da177e4SLinus Torvalds 		(h->st.format << 6) |
1851da177e4SLinus Torvalds 		(h->st.stereo << 5) |
1861da177e4SLinus Torvalds 		(h->st.rate));
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	harmony_wait_for_control(h);
1891da177e4SLinus Torvalds 	harmony_write(h, HARMONY_CNTL, ctrl);
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds 	spin_unlock_irqrestore(&h->lock, flags);
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds static irqreturn_t
1957d12e780SDavid Howells snd_harmony_interrupt(int irq, void *dev)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	u32 dstatus;
19867b1020dSTakashi Iwai 	struct snd_harmony *h = dev;
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	spin_lock(&h->lock);
2011da177e4SLinus Torvalds 	harmony_disable_interrupts(h);
2021da177e4SLinus Torvalds 	harmony_wait_for_control(h);
2031da177e4SLinus Torvalds 	dstatus = harmony_read(h, HARMONY_DSTATUS);
2041da177e4SLinus Torvalds 	spin_unlock(&h->lock);
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	if (dstatus & HARMONY_DSTATUS_PN) {
2073a165680SStuart Brady 		if (h->psubs && h->st.playing) {
2081da177e4SLinus Torvalds 			spin_lock(&h->lock);
2091da177e4SLinus Torvalds 			h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
2101da177e4SLinus Torvalds 			h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 			harmony_write(h, HARMONY_PNXTADD,
2131da177e4SLinus Torvalds 				      h->pbuf.addr + h->pbuf.buf);
2141da177e4SLinus Torvalds 			h->stats.play_intr++;
2151da177e4SLinus Torvalds 			spin_unlock(&h->lock);
2161da177e4SLinus Torvalds                         snd_pcm_period_elapsed(h->psubs);
2171da177e4SLinus Torvalds 		} else {
2181da177e4SLinus Torvalds 			spin_lock(&h->lock);
2191da177e4SLinus Torvalds 			harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
2201da177e4SLinus Torvalds 			h->stats.silence_intr++;
2211da177e4SLinus Torvalds 			spin_unlock(&h->lock);
2221da177e4SLinus Torvalds 		}
2231da177e4SLinus Torvalds 	}
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	if (dstatus & HARMONY_DSTATUS_RN) {
2263a165680SStuart Brady 		if (h->csubs && h->st.capturing) {
2271da177e4SLinus Torvalds 			spin_lock(&h->lock);
2281da177e4SLinus Torvalds 			h->cbuf.buf += h->cbuf.count;
2291da177e4SLinus Torvalds 			h->cbuf.buf %= h->cbuf.size;
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 			harmony_write(h, HARMONY_RNXTADD,
2321da177e4SLinus Torvalds 				      h->cbuf.addr + h->cbuf.buf);
2331da177e4SLinus Torvalds 			h->stats.rec_intr++;
2341da177e4SLinus Torvalds 			spin_unlock(&h->lock);
2351da177e4SLinus Torvalds                         snd_pcm_period_elapsed(h->csubs);
2361da177e4SLinus Torvalds 		} else {
2371da177e4SLinus Torvalds 			spin_lock(&h->lock);
2381da177e4SLinus Torvalds 			harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
2391da177e4SLinus Torvalds 			h->stats.graveyard_intr++;
2401da177e4SLinus Torvalds 			spin_unlock(&h->lock);
2411da177e4SLinus Torvalds 		}
2421da177e4SLinus Torvalds 	}
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	spin_lock(&h->lock);
2451da177e4SLinus Torvalds 	harmony_enable_interrupts(h);
2461da177e4SLinus Torvalds 	spin_unlock(&h->lock);
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	return IRQ_HANDLED;
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds static unsigned int
2521da177e4SLinus Torvalds snd_harmony_rate_bits(int rate)
2531da177e4SLinus Torvalds {
2541da177e4SLinus Torvalds 	unsigned int i;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(snd_harmony_rates); i++)
2571da177e4SLinus Torvalds 		if (snd_harmony_rates[i] == rate)
2581da177e4SLinus Torvalds 			return rate_bits[i];
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 	return HARMONY_SR_44KHZ;
2611da177e4SLinus Torvalds }
2621da177e4SLinus Torvalds 
26367b1020dSTakashi Iwai static struct snd_pcm_hardware snd_harmony_playback =
2641da177e4SLinus Torvalds {
2651da177e4SLinus Torvalds 	.info =	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
2661da177e4SLinus Torvalds 		 SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
2671da177e4SLinus Torvalds 		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
2681da177e4SLinus Torvalds 	.formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
2691da177e4SLinus Torvalds 		    SNDRV_PCM_FMTBIT_A_LAW),
2701da177e4SLinus Torvalds 	.rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
2711da177e4SLinus Torvalds 		  SNDRV_PCM_RATE_KNOT),
2721da177e4SLinus Torvalds 	.rate_min = 5512,
2731da177e4SLinus Torvalds 	.rate_max = 48000,
2741da177e4SLinus Torvalds 	.channels_min =	1,
2751da177e4SLinus Torvalds 	.channels_max =	2,
2761da177e4SLinus Torvalds 	.buffer_bytes_max = MAX_BUF_SIZE,
2771da177e4SLinus Torvalds 	.period_bytes_min = BUF_SIZE,
2781da177e4SLinus Torvalds 	.period_bytes_max = BUF_SIZE,
2791da177e4SLinus Torvalds 	.periods_min = 1,
2801da177e4SLinus Torvalds 	.periods_max = MAX_BUFS,
2811da177e4SLinus Torvalds 	.fifo_size = 0,
2821da177e4SLinus Torvalds };
2831da177e4SLinus Torvalds 
28467b1020dSTakashi Iwai static struct snd_pcm_hardware snd_harmony_capture =
2851da177e4SLinus Torvalds {
2861da177e4SLinus Torvalds         .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
2871da177e4SLinus Torvalds                  SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
2881da177e4SLinus Torvalds                  SNDRV_PCM_INFO_BLOCK_TRANSFER),
2891da177e4SLinus Torvalds         .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
2901da177e4SLinus Torvalds                     SNDRV_PCM_FMTBIT_A_LAW),
2911da177e4SLinus Torvalds         .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
2921da177e4SLinus Torvalds 		  SNDRV_PCM_RATE_KNOT),
2931da177e4SLinus Torvalds         .rate_min = 5512,
2941da177e4SLinus Torvalds         .rate_max = 48000,
2951da177e4SLinus Torvalds         .channels_min = 1,
2961da177e4SLinus Torvalds         .channels_max = 2,
2971da177e4SLinus Torvalds         .buffer_bytes_max = MAX_BUF_SIZE,
2981da177e4SLinus Torvalds         .period_bytes_min = BUF_SIZE,
2991da177e4SLinus Torvalds         .period_bytes_max = BUF_SIZE,
3001da177e4SLinus Torvalds         .periods_min = 1,
3011da177e4SLinus Torvalds         .periods_max = MAX_BUFS,
3021da177e4SLinus Torvalds         .fifo_size = 0,
3031da177e4SLinus Torvalds };
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds static int
30667b1020dSTakashi Iwai snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
3071da177e4SLinus Torvalds {
30867b1020dSTakashi Iwai 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds 	if (h->st.capturing)
3111da177e4SLinus Torvalds 		return -EBUSY;
3121da177e4SLinus Torvalds 
31303f9ae25STakashi Iwai 	spin_lock(&h->lock);
3141da177e4SLinus Torvalds 	switch (cmd) {
3151da177e4SLinus Torvalds 	case SNDRV_PCM_TRIGGER_START:
3161da177e4SLinus Torvalds 		h->st.playing = 1;
3171da177e4SLinus Torvalds 		harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr);
3181da177e4SLinus Torvalds 		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
3191da177e4SLinus Torvalds 		harmony_unmute(h);
3201da177e4SLinus Torvalds 		harmony_enable_interrupts(h);
3211da177e4SLinus Torvalds 		break;
3221da177e4SLinus Torvalds 	case SNDRV_PCM_TRIGGER_STOP:
3231da177e4SLinus Torvalds 		h->st.playing = 0;
3241da177e4SLinus Torvalds 		harmony_mute(h);
3253a165680SStuart Brady 		harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
3261da177e4SLinus Torvalds 		harmony_disable_interrupts(h);
3271da177e4SLinus Torvalds 		break;
3281da177e4SLinus Torvalds 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3291da177e4SLinus Torvalds 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3301da177e4SLinus Torvalds 	case SNDRV_PCM_TRIGGER_SUSPEND:
3311da177e4SLinus Torvalds 	default:
33203f9ae25STakashi Iwai 		spin_unlock(&h->lock);
3331da177e4SLinus Torvalds 		snd_BUG();
3341da177e4SLinus Torvalds 		return -EINVAL;
3351da177e4SLinus Torvalds 	}
33603f9ae25STakashi Iwai 	spin_unlock(&h->lock);
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	return 0;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds static int
34267b1020dSTakashi Iwai snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
3431da177e4SLinus Torvalds {
34467b1020dSTakashi Iwai         struct snd_harmony *h = snd_pcm_substream_chip(ss);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 	if (h->st.playing)
3471da177e4SLinus Torvalds 		return -EBUSY;
3481da177e4SLinus Torvalds 
34903f9ae25STakashi Iwai 	spin_lock(&h->lock);
3501da177e4SLinus Torvalds         switch (cmd) {
3511da177e4SLinus Torvalds         case SNDRV_PCM_TRIGGER_START:
3521da177e4SLinus Torvalds 		h->st.capturing = 1;
3531da177e4SLinus Torvalds                 harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
3541da177e4SLinus Torvalds                 harmony_write(h, HARMONY_RNXTADD, h->cbuf.addr);
3551da177e4SLinus Torvalds 		harmony_unmute(h);
3561da177e4SLinus Torvalds                 harmony_enable_interrupts(h);
3571da177e4SLinus Torvalds 		break;
3581da177e4SLinus Torvalds         case SNDRV_PCM_TRIGGER_STOP:
3591da177e4SLinus Torvalds 		h->st.capturing = 0;
3601da177e4SLinus Torvalds 		harmony_mute(h);
3613a165680SStuart Brady 		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
3621da177e4SLinus Torvalds 		harmony_disable_interrupts(h);
3631da177e4SLinus Torvalds 		break;
3641da177e4SLinus Torvalds         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3651da177e4SLinus Torvalds         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3661da177e4SLinus Torvalds         case SNDRV_PCM_TRIGGER_SUSPEND:
3671da177e4SLinus Torvalds 	default:
36803f9ae25STakashi Iwai 		spin_unlock(&h->lock);
3691da177e4SLinus Torvalds 		snd_BUG();
3701da177e4SLinus Torvalds                 return -EINVAL;
3711da177e4SLinus Torvalds         }
37203f9ae25STakashi Iwai 	spin_unlock(&h->lock);
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds         return 0;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds static int
37867b1020dSTakashi Iwai snd_harmony_set_data_format(struct snd_harmony *h, int fmt, int force)
3791da177e4SLinus Torvalds {
3801da177e4SLinus Torvalds 	int o = h->st.format;
3811da177e4SLinus Torvalds 	int n;
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds 	switch(fmt) {
3841da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_S16_BE:
3851da177e4SLinus Torvalds 		n = HARMONY_DF_16BIT_LINEAR;
3861da177e4SLinus Torvalds 		break;
3871da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_A_LAW:
3881da177e4SLinus Torvalds 		n = HARMONY_DF_8BIT_ALAW;
3891da177e4SLinus Torvalds 		break;
3901da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_MU_LAW:
3911da177e4SLinus Torvalds 		n = HARMONY_DF_8BIT_ULAW;
3921da177e4SLinus Torvalds 		break;
3931da177e4SLinus Torvalds 	default:
3941da177e4SLinus Torvalds 		n = HARMONY_DF_16BIT_LINEAR;
3951da177e4SLinus Torvalds 		break;
3961da177e4SLinus Torvalds 	}
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	if (force || o != n) {
3991da177e4SLinus Torvalds 		snd_pcm_format_set_silence(fmt, h->sdma.area, SILENCE_BUFSZ /
4001da177e4SLinus Torvalds 					   (snd_pcm_format_physical_width(fmt)
4011da177e4SLinus Torvalds 					    / 8));
4021da177e4SLinus Torvalds 	}
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 	return n;
4051da177e4SLinus Torvalds }
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds static int
40867b1020dSTakashi Iwai snd_harmony_playback_prepare(struct snd_pcm_substream *ss)
4091da177e4SLinus Torvalds {
41067b1020dSTakashi Iwai 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
41167b1020dSTakashi Iwai 	struct snd_pcm_runtime *rt = ss->runtime;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	if (h->st.capturing)
4141da177e4SLinus Torvalds 		return -EBUSY;
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds 	h->pbuf.size = snd_pcm_lib_buffer_bytes(ss);
4171da177e4SLinus Torvalds 	h->pbuf.count = snd_pcm_lib_period_bytes(ss);
4183a165680SStuart Brady 	if (h->pbuf.buf >= h->pbuf.size)
4191da177e4SLinus Torvalds 		h->pbuf.buf = 0;
4201da177e4SLinus Torvalds 	h->st.playing = 0;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	h->st.rate = snd_harmony_rate_bits(rt->rate);
4231da177e4SLinus Torvalds 	h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds 	if (rt->channels == 2)
4261da177e4SLinus Torvalds 		h->st.stereo = HARMONY_SS_STEREO;
4271da177e4SLinus Torvalds 	else
4281da177e4SLinus Torvalds 		h->st.stereo = HARMONY_SS_MONO;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	harmony_set_control(h);
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	h->pbuf.addr = rt->dma_addr;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	return 0;
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds static int
43867b1020dSTakashi Iwai snd_harmony_capture_prepare(struct snd_pcm_substream *ss)
4391da177e4SLinus Torvalds {
44067b1020dSTakashi Iwai         struct snd_harmony *h = snd_pcm_substream_chip(ss);
44167b1020dSTakashi Iwai         struct snd_pcm_runtime *rt = ss->runtime;
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 	if (h->st.playing)
4441da177e4SLinus Torvalds 		return -EBUSY;
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds         h->cbuf.size = snd_pcm_lib_buffer_bytes(ss);
4471da177e4SLinus Torvalds         h->cbuf.count = snd_pcm_lib_period_bytes(ss);
4483a165680SStuart Brady 	if (h->cbuf.buf >= h->cbuf.size)
4491da177e4SLinus Torvalds 	        h->cbuf.buf = 0;
4501da177e4SLinus Torvalds 	h->st.capturing = 0;
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds         h->st.rate = snd_harmony_rate_bits(rt->rate);
4531da177e4SLinus Torvalds         h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds         if (rt->channels == 2)
4561da177e4SLinus Torvalds                 h->st.stereo = HARMONY_SS_STEREO;
4571da177e4SLinus Torvalds         else
4581da177e4SLinus Torvalds                 h->st.stereo = HARMONY_SS_MONO;
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds         harmony_set_control(h);
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds         h->cbuf.addr = rt->dma_addr;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds         return 0;
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds static snd_pcm_uframes_t
46867b1020dSTakashi Iwai snd_harmony_playback_pointer(struct snd_pcm_substream *ss)
4691da177e4SLinus Torvalds {
47067b1020dSTakashi Iwai 	struct snd_pcm_runtime *rt = ss->runtime;
47167b1020dSTakashi Iwai 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
4721da177e4SLinus Torvalds 	unsigned long pcuradd;
4731da177e4SLinus Torvalds 	unsigned long played;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	if (!(h->st.playing) || (h->psubs == NULL))
4761da177e4SLinus Torvalds 		return 0;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	if ((h->pbuf.addr == 0) || (h->pbuf.size == 0))
4791da177e4SLinus Torvalds 		return 0;
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds 	pcuradd = harmony_read(h, HARMONY_PCURADD);
4821da177e4SLinus Torvalds 	played = pcuradd - h->pbuf.addr;
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds #ifdef HARMONY_DEBUG
4851da177e4SLinus Torvalds 	printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes\n",
4861da177e4SLinus Torvalds 	       pcuradd, h->pbuf.addr, played);
4871da177e4SLinus Torvalds #endif
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	if (pcuradd > h->pbuf.addr + h->pbuf.size) {
4901da177e4SLinus Torvalds 		return 0;
4911da177e4SLinus Torvalds 	}
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	return bytes_to_frames(rt, played);
4941da177e4SLinus Torvalds }
4951da177e4SLinus Torvalds 
4961da177e4SLinus Torvalds static snd_pcm_uframes_t
49767b1020dSTakashi Iwai snd_harmony_capture_pointer(struct snd_pcm_substream *ss)
4981da177e4SLinus Torvalds {
49967b1020dSTakashi Iwai         struct snd_pcm_runtime *rt = ss->runtime;
50067b1020dSTakashi Iwai         struct snd_harmony *h = snd_pcm_substream_chip(ss);
5011da177e4SLinus Torvalds         unsigned long rcuradd;
5021da177e4SLinus Torvalds         unsigned long caught;
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds         if (!(h->st.capturing) || (h->csubs == NULL))
5051da177e4SLinus Torvalds                 return 0;
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds         if ((h->cbuf.addr == 0) || (h->cbuf.size == 0))
5081da177e4SLinus Torvalds                 return 0;
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds         rcuradd = harmony_read(h, HARMONY_RCURADD);
5111da177e4SLinus Torvalds         caught = rcuradd - h->cbuf.addr;
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds #ifdef HARMONY_DEBUG
5141da177e4SLinus Torvalds         printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes\n",
5151da177e4SLinus Torvalds                rcuradd, h->cbuf.addr, caught);
5161da177e4SLinus Torvalds #endif
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds         if (rcuradd > h->cbuf.addr + h->cbuf.size) {
5191da177e4SLinus Torvalds 		return 0;
5201da177e4SLinus Torvalds 	}
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds         return bytes_to_frames(rt, caught);
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds static int
52667b1020dSTakashi Iwai snd_harmony_playback_open(struct snd_pcm_substream *ss)
5271da177e4SLinus Torvalds {
52867b1020dSTakashi Iwai 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
52967b1020dSTakashi Iwai 	struct snd_pcm_runtime *rt = ss->runtime;
5301da177e4SLinus Torvalds 	int err;
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 	h->psubs = ss;
5331da177e4SLinus Torvalds 	rt->hw = snd_harmony_playback;
5341da177e4SLinus Torvalds 	snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
5351da177e4SLinus Torvalds 				   &hw_constraint_rates);
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
5381da177e4SLinus Torvalds 	if (err < 0)
5391da177e4SLinus Torvalds 		return err;
5401da177e4SLinus Torvalds 
5411da177e4SLinus Torvalds 	return 0;
5421da177e4SLinus Torvalds }
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds static int
54567b1020dSTakashi Iwai snd_harmony_capture_open(struct snd_pcm_substream *ss)
5461da177e4SLinus Torvalds {
54767b1020dSTakashi Iwai         struct snd_harmony *h = snd_pcm_substream_chip(ss);
54867b1020dSTakashi Iwai         struct snd_pcm_runtime *rt = ss->runtime;
5491da177e4SLinus Torvalds         int err;
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds         h->csubs = ss;
5521da177e4SLinus Torvalds         rt->hw = snd_harmony_capture;
5531da177e4SLinus Torvalds         snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
5541da177e4SLinus Torvalds                                    &hw_constraint_rates);
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds         err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
5571da177e4SLinus Torvalds         if (err < 0)
5581da177e4SLinus Torvalds                 return err;
5591da177e4SLinus Torvalds 
5601da177e4SLinus Torvalds         return 0;
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds static int
56467b1020dSTakashi Iwai snd_harmony_playback_close(struct snd_pcm_substream *ss)
5651da177e4SLinus Torvalds {
56667b1020dSTakashi Iwai 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
5671da177e4SLinus Torvalds 	h->psubs = NULL;
5681da177e4SLinus Torvalds 	return 0;
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds static int
57267b1020dSTakashi Iwai snd_harmony_capture_close(struct snd_pcm_substream *ss)
5731da177e4SLinus Torvalds {
57467b1020dSTakashi Iwai         struct snd_harmony *h = snd_pcm_substream_chip(ss);
5751da177e4SLinus Torvalds         h->csubs = NULL;
5761da177e4SLinus Torvalds         return 0;
5771da177e4SLinus Torvalds }
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds static int
58067b1020dSTakashi Iwai snd_harmony_hw_params(struct snd_pcm_substream *ss,
58167b1020dSTakashi Iwai 		      struct snd_pcm_hw_params *hw)
5821da177e4SLinus Torvalds {
5831da177e4SLinus Torvalds 	int err;
58467b1020dSTakashi Iwai 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 	err = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw));
5871da177e4SLinus Torvalds 	if (err > 0 && h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS)
5881da177e4SLinus Torvalds 		ss->runtime->dma_addr = __pa(ss->runtime->dma_area);
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 	return err;
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds static int
59467b1020dSTakashi Iwai snd_harmony_hw_free(struct snd_pcm_substream *ss)
5951da177e4SLinus Torvalds {
5961da177e4SLinus Torvalds 	return snd_pcm_lib_free_pages(ss);
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
59967b1020dSTakashi Iwai static struct snd_pcm_ops snd_harmony_playback_ops = {
6001da177e4SLinus Torvalds 	.open =	snd_harmony_playback_open,
6011da177e4SLinus Torvalds 	.close = snd_harmony_playback_close,
6021da177e4SLinus Torvalds 	.ioctl = snd_pcm_lib_ioctl,
6031da177e4SLinus Torvalds 	.hw_params = snd_harmony_hw_params,
6041da177e4SLinus Torvalds 	.hw_free = snd_harmony_hw_free,
6051da177e4SLinus Torvalds 	.prepare = snd_harmony_playback_prepare,
6061da177e4SLinus Torvalds 	.trigger = snd_harmony_playback_trigger,
6071da177e4SLinus Torvalds  	.pointer = snd_harmony_playback_pointer,
6081da177e4SLinus Torvalds };
6091da177e4SLinus Torvalds 
61067b1020dSTakashi Iwai static struct snd_pcm_ops snd_harmony_capture_ops = {
6111da177e4SLinus Torvalds         .open = snd_harmony_capture_open,
6121da177e4SLinus Torvalds         .close = snd_harmony_capture_close,
6131da177e4SLinus Torvalds         .ioctl = snd_pcm_lib_ioctl,
6141da177e4SLinus Torvalds         .hw_params = snd_harmony_hw_params,
6151da177e4SLinus Torvalds         .hw_free = snd_harmony_hw_free,
6161da177e4SLinus Torvalds         .prepare = snd_harmony_capture_prepare,
6171da177e4SLinus Torvalds         .trigger = snd_harmony_capture_trigger,
6181da177e4SLinus Torvalds         .pointer = snd_harmony_capture_pointer,
6191da177e4SLinus Torvalds };
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds static int
62267b1020dSTakashi Iwai snd_harmony_pcm_init(struct snd_harmony *h)
6231da177e4SLinus Torvalds {
62467b1020dSTakashi Iwai 	struct snd_pcm *pcm;
6251da177e4SLinus Torvalds 	int err;
6261da177e4SLinus Torvalds 
627*e8e0929dSJulia Lawall 	if (snd_BUG_ON(!h))
628*e8e0929dSJulia Lawall 		return -EINVAL;
629*e8e0929dSJulia Lawall 
6301da177e4SLinus Torvalds 	harmony_disable_interrupts(h);
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds    	err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm);
6331da177e4SLinus Torvalds 	if (err < 0)
6341da177e4SLinus Torvalds 		return err;
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
6371da177e4SLinus Torvalds 			&snd_harmony_playback_ops);
6381da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
6391da177e4SLinus Torvalds 			&snd_harmony_capture_ops);
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	pcm->private_data = h;
6421da177e4SLinus Torvalds 	pcm->info_flags = 0;
6431da177e4SLinus Torvalds 	strcpy(pcm->name, "harmony");
6441da177e4SLinus Torvalds 	h->pcm = pcm;
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds 	h->psubs = NULL;
6471da177e4SLinus Torvalds 	h->csubs = NULL;
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds 	/* initialize graveyard buffer */
6501da177e4SLinus Torvalds 	h->dma.type = SNDRV_DMA_TYPE_DEV;
6511da177e4SLinus Torvalds 	h->dma.dev = &h->dev->dev;
6521da177e4SLinus Torvalds 	err = snd_dma_alloc_pages(h->dma.type,
6531da177e4SLinus Torvalds 				  h->dma.dev,
6541da177e4SLinus Torvalds 				  BUF_SIZE*GRAVEYARD_BUFS,
6551da177e4SLinus Torvalds 				  &h->gdma);
6561da177e4SLinus Torvalds 	if (err < 0) {
6571da177e4SLinus Torvalds 		printk(KERN_ERR PFX "cannot allocate graveyard buffer!\n");
6581da177e4SLinus Torvalds 		return err;
6591da177e4SLinus Torvalds 	}
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 	/* initialize silence buffers */
6621da177e4SLinus Torvalds 	err = snd_dma_alloc_pages(h->dma.type,
6631da177e4SLinus Torvalds 				  h->dma.dev,
6641da177e4SLinus Torvalds 				  BUF_SIZE*SILENCE_BUFS,
6651da177e4SLinus Torvalds 				  &h->sdma);
6661da177e4SLinus Torvalds 	if (err < 0) {
6671da177e4SLinus Torvalds 		printk(KERN_ERR PFX "cannot allocate silence buffer!\n");
6681da177e4SLinus Torvalds 		return err;
6691da177e4SLinus Torvalds 	}
6701da177e4SLinus Torvalds 
6711da177e4SLinus Torvalds 	/* pre-allocate space for DMA */
6721da177e4SLinus Torvalds 	err = snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type,
6731da177e4SLinus Torvalds 						    h->dma.dev,
6741da177e4SLinus Torvalds 						    MAX_BUF_SIZE,
6751da177e4SLinus Torvalds 						    MAX_BUF_SIZE);
6761da177e4SLinus Torvalds 	if (err < 0) {
6771da177e4SLinus Torvalds 		printk(KERN_ERR PFX "buffer allocation error: %d\n", err);
6781da177e4SLinus Torvalds 		return err;
6791da177e4SLinus Torvalds 	}
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	h->st.format = snd_harmony_set_data_format(h,
6821da177e4SLinus Torvalds 		SNDRV_PCM_FORMAT_S16_BE, 1);
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	return 0;
6851da177e4SLinus Torvalds }
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds static void
68867b1020dSTakashi Iwai snd_harmony_set_new_gain(struct snd_harmony *h)
6891da177e4SLinus Torvalds {
6901da177e4SLinus Torvalds  	harmony_wait_for_control(h);
6911da177e4SLinus Torvalds 	harmony_write(h, HARMONY_GAINCTL, h->st.gain);
6921da177e4SLinus Torvalds }
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds static int
69567b1020dSTakashi Iwai snd_harmony_mixercontrol_info(struct snd_kcontrol *kc,
69667b1020dSTakashi Iwai 			      struct snd_ctl_elem_info *uinfo)
6971da177e4SLinus Torvalds {
6981da177e4SLinus Torvalds 	int mask = (kc->private_value >> 16) & 0xff;
6991da177e4SLinus Torvalds 	int left_shift = (kc->private_value) & 0xff;
7001da177e4SLinus Torvalds 	int right_shift = (kc->private_value >> 8) & 0xff;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :
7031da177e4SLinus Torvalds 		       SNDRV_CTL_ELEM_TYPE_INTEGER;
7041da177e4SLinus Torvalds 	uinfo->count = left_shift == right_shift ? 1 : 2;
7051da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
7061da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
7071da177e4SLinus Torvalds 
7081da177e4SLinus Torvalds 	return 0;
7091da177e4SLinus Torvalds }
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds static int
71267b1020dSTakashi Iwai snd_harmony_volume_get(struct snd_kcontrol *kc,
71367b1020dSTakashi Iwai 		       struct snd_ctl_elem_value *ucontrol)
7141da177e4SLinus Torvalds {
71567b1020dSTakashi Iwai 	struct snd_harmony *h = snd_kcontrol_chip(kc);
7161da177e4SLinus Torvalds 	int shift_left = (kc->private_value) & 0xff;
7171da177e4SLinus Torvalds 	int shift_right = (kc->private_value >> 8) & 0xff;
7181da177e4SLinus Torvalds 	int mask = (kc->private_value >> 16) & 0xff;
7191da177e4SLinus Torvalds 	int invert = (kc->private_value >> 24) & 0xff;
7201da177e4SLinus Torvalds 	int left, right;
7211da177e4SLinus Torvalds 
72203f9ae25STakashi Iwai 	spin_lock_irq(&h->mixer_lock);
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds 	left = (h->st.gain >> shift_left) & mask;
7251da177e4SLinus Torvalds 	right = (h->st.gain >> shift_right) & mask;
7261da177e4SLinus Torvalds 	if (invert) {
7271da177e4SLinus Torvalds 		left = mask - left;
7281da177e4SLinus Torvalds 		right = mask - right;
7291da177e4SLinus Torvalds 	}
7303a165680SStuart Brady 
7311da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = left;
7323a165680SStuart Brady 	if (shift_left != shift_right)
7331da177e4SLinus Torvalds 		ucontrol->value.integer.value[1] = right;
7341da177e4SLinus Torvalds 
73503f9ae25STakashi Iwai 	spin_unlock_irq(&h->mixer_lock);
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	return 0;
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds static int
74167b1020dSTakashi Iwai snd_harmony_volume_put(struct snd_kcontrol *kc,
74267b1020dSTakashi Iwai 		       struct snd_ctl_elem_value *ucontrol)
7431da177e4SLinus Torvalds {
74467b1020dSTakashi Iwai 	struct snd_harmony *h = snd_kcontrol_chip(kc);
7451da177e4SLinus Torvalds 	int shift_left = (kc->private_value) & 0xff;
7461da177e4SLinus Torvalds 	int shift_right = (kc->private_value >> 8) & 0xff;
7471da177e4SLinus Torvalds 	int mask = (kc->private_value >> 16) & 0xff;
7481da177e4SLinus Torvalds 	int invert = (kc->private_value >> 24) & 0xff;
7491da177e4SLinus Torvalds 	int left, right;
7501da177e4SLinus Torvalds 	int old_gain = h->st.gain;
7511da177e4SLinus Torvalds 
75203f9ae25STakashi Iwai 	spin_lock_irq(&h->mixer_lock);
7531da177e4SLinus Torvalds 
7543a165680SStuart Brady 	left = ucontrol->value.integer.value[0] & mask;
7553a165680SStuart Brady 	if (invert)
7563a165680SStuart Brady 		left = mask - left;
7573a165680SStuart Brady 	h->st.gain &= ~( (mask << shift_left ) );
7583a165680SStuart Brady  	h->st.gain |= (left << shift_left);
7593a165680SStuart Brady 
7603a165680SStuart Brady 	if (shift_left != shift_right) {
7613a165680SStuart Brady 		right = ucontrol->value.integer.value[1] & mask;
7623a165680SStuart Brady 		if (invert)
7633a165680SStuart Brady 			right = mask - right;
7643a165680SStuart Brady 		h->st.gain &= ~( (mask << shift_right) );
7653a165680SStuart Brady 		h->st.gain |= (right << shift_right);
7663a165680SStuart Brady 	}
7673a165680SStuart Brady 
7681da177e4SLinus Torvalds 	snd_harmony_set_new_gain(h);
7691da177e4SLinus Torvalds 
77003f9ae25STakashi Iwai 	spin_unlock_irq(&h->mixer_lock);
7711da177e4SLinus Torvalds 
7723a165680SStuart Brady 	return h->st.gain != old_gain;
7733a165680SStuart Brady }
7743a165680SStuart Brady 
7753a165680SStuart Brady static int
77667b1020dSTakashi Iwai snd_harmony_captureroute_info(struct snd_kcontrol *kc,
77767b1020dSTakashi Iwai 			      struct snd_ctl_elem_info *uinfo)
7783a165680SStuart Brady {
7793a165680SStuart Brady 	static char *texts[2] = { "Line", "Mic" };
7803a165680SStuart Brady 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
7813a165680SStuart Brady 	uinfo->count = 1;
7823a165680SStuart Brady 	uinfo->value.enumerated.items = 2;
7833a165680SStuart Brady 	if (uinfo->value.enumerated.item > 1)
7843a165680SStuart Brady 		uinfo->value.enumerated.item = 1;
7853a165680SStuart Brady 	strcpy(uinfo->value.enumerated.name,
7863a165680SStuart Brady 	       texts[uinfo->value.enumerated.item]);
7873a165680SStuart Brady 	return 0;
7883a165680SStuart Brady }
7893a165680SStuart Brady 
7903a165680SStuart Brady static int
79167b1020dSTakashi Iwai snd_harmony_captureroute_get(struct snd_kcontrol *kc,
79267b1020dSTakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
7933a165680SStuart Brady {
79467b1020dSTakashi Iwai 	struct snd_harmony *h = snd_kcontrol_chip(kc);
7953a165680SStuart Brady 	int value;
7963a165680SStuart Brady 
79703f9ae25STakashi Iwai 	spin_lock_irq(&h->mixer_lock);
7983a165680SStuart Brady 
7993a165680SStuart Brady 	value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1;
8003a165680SStuart Brady 	ucontrol->value.enumerated.item[0] = value;
8013a165680SStuart Brady 
80203f9ae25STakashi Iwai 	spin_unlock_irq(&h->mixer_lock);
8033a165680SStuart Brady 
8043a165680SStuart Brady 	return 0;
8053a165680SStuart Brady }
8063a165680SStuart Brady 
8073a165680SStuart Brady static int
80867b1020dSTakashi Iwai snd_harmony_captureroute_put(struct snd_kcontrol *kc,
80967b1020dSTakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
8103a165680SStuart Brady {
81167b1020dSTakashi Iwai 	struct snd_harmony *h = snd_kcontrol_chip(kc);
8123a165680SStuart Brady 	int value;
8133a165680SStuart Brady 	int old_gain = h->st.gain;
8143a165680SStuart Brady 
81503f9ae25STakashi Iwai 	spin_lock_irq(&h->mixer_lock);
8163a165680SStuart Brady 
8173a165680SStuart Brady 	value = ucontrol->value.enumerated.item[0] & 1;
8183a165680SStuart Brady 	h->st.gain &= ~HARMONY_GAIN_IS_MASK;
8193a165680SStuart Brady  	h->st.gain |= value << HARMONY_GAIN_IS_SHIFT;
8203a165680SStuart Brady 
8213a165680SStuart Brady 	snd_harmony_set_new_gain(h);
8223a165680SStuart Brady 
82303f9ae25STakashi Iwai 	spin_unlock_irq(&h->mixer_lock);
8243a165680SStuart Brady 
8253a165680SStuart Brady 	return h->st.gain != old_gain;
8261da177e4SLinus Torvalds }
8271da177e4SLinus Torvalds 
82867b1020dSTakashi Iwai #define HARMONY_CONTROLS	ARRAY_SIZE(snd_harmony_controls)
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds #define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \
8311da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,                \
8321da177e4SLinus Torvalds   .info = snd_harmony_mixercontrol_info,                             \
8331da177e4SLinus Torvalds   .get = snd_harmony_volume_get, .put = snd_harmony_volume_put,      \
8341da177e4SLinus Torvalds   .private_value = ((left_shift) | ((right_shift) << 8) |            \
8351da177e4SLinus Torvalds                    ((mask) << 16) | ((invert) << 24)) }
8361da177e4SLinus Torvalds 
83767b1020dSTakashi Iwai static struct snd_kcontrol_new snd_harmony_controls[] = {
8383a165680SStuart Brady 	HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT,
8391da177e4SLinus Torvalds 		       HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1),
8401da177e4SLinus Torvalds 	HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT,
8411da177e4SLinus Torvalds 		       HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0),
8423a165680SStuart Brady 	HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT,
8433a165680SStuart Brady 		       HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1),
8443a165680SStuart Brady 	{
8453a165680SStuart Brady 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8463a165680SStuart Brady 		.name = "Input Route",
8473a165680SStuart Brady 		.info = snd_harmony_captureroute_info,
8483a165680SStuart Brady 		.get = snd_harmony_captureroute_get,
8493a165680SStuart Brady 		.put = snd_harmony_captureroute_put
8503a165680SStuart Brady 	},
8513a165680SStuart Brady 	HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT,
8523a165680SStuart Brady 		       HARMONY_GAIN_SE_SHIFT, 1, 0),
8533a165680SStuart Brady 	HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT,
8543a165680SStuart Brady 		       HARMONY_GAIN_LE_SHIFT, 1, 0),
8553a165680SStuart Brady 	HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT,
8563a165680SStuart Brady 		       HARMONY_GAIN_HE_SHIFT, 1, 0),
8571da177e4SLinus Torvalds };
8581da177e4SLinus Torvalds 
859992378a0SHelge Deller static void __devinit
86067b1020dSTakashi Iwai snd_harmony_mixer_reset(struct snd_harmony *h)
8611da177e4SLinus Torvalds {
8621da177e4SLinus Torvalds 	harmony_mute(h);
8631da177e4SLinus Torvalds 	harmony_reset(h);
8641da177e4SLinus Torvalds 	h->st.gain = HARMONY_GAIN_DEFAULT;
8651da177e4SLinus Torvalds 	harmony_unmute(h);
8661da177e4SLinus Torvalds }
8671da177e4SLinus Torvalds 
868992378a0SHelge Deller static int __devinit
86967b1020dSTakashi Iwai snd_harmony_mixer_init(struct snd_harmony *h)
8701da177e4SLinus Torvalds {
871*e8e0929dSJulia Lawall 	struct snd_card *card;
8721da177e4SLinus Torvalds 	int idx, err;
8731da177e4SLinus Torvalds 
8745e246b85STakashi Iwai 	if (snd_BUG_ON(!h))
875c86a456bSTakashi Iwai 		return -EINVAL;
876*e8e0929dSJulia Lawall 	card = h->card;
8771da177e4SLinus Torvalds 	strcpy(card->mixername, "Harmony Gain control interface");
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 	for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
8801da177e4SLinus Torvalds 		err = snd_ctl_add(card,
8811da177e4SLinus Torvalds 				  snd_ctl_new1(&snd_harmony_controls[idx], h));
8821da177e4SLinus Torvalds 		if (err < 0)
8831da177e4SLinus Torvalds 			return err;
8841da177e4SLinus Torvalds 	}
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds 	snd_harmony_mixer_reset(h);
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 	return 0;
8891da177e4SLinus Torvalds }
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds static int
89267b1020dSTakashi Iwai snd_harmony_free(struct snd_harmony *h)
8931da177e4SLinus Torvalds {
8941da177e4SLinus Torvalds         if (h->gdma.addr)
8951da177e4SLinus Torvalds                 snd_dma_free_pages(&h->gdma);
8961da177e4SLinus Torvalds         if (h->sdma.addr)
8971da177e4SLinus Torvalds                 snd_dma_free_pages(&h->sdma);
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	if (h->irq >= 0)
9001da177e4SLinus Torvalds 		free_irq(h->irq, h);
9011da177e4SLinus Torvalds 
9021da177e4SLinus Torvalds 	if (h->iobase)
9031da177e4SLinus Torvalds 		iounmap(h->iobase);
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	parisc_set_drvdata(h->dev, NULL);
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 	kfree(h);
9081da177e4SLinus Torvalds 	return 0;
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds static int
91267b1020dSTakashi Iwai snd_harmony_dev_free(struct snd_device *dev)
9131da177e4SLinus Torvalds {
91467b1020dSTakashi Iwai 	struct snd_harmony *h = dev->device_data;
9151da177e4SLinus Torvalds 	return snd_harmony_free(h);
9161da177e4SLinus Torvalds }
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds static int __devinit
91967b1020dSTakashi Iwai snd_harmony_create(struct snd_card *card,
9201da177e4SLinus Torvalds 		   struct parisc_device *padev,
92167b1020dSTakashi Iwai 		   struct snd_harmony **rchip)
9221da177e4SLinus Torvalds {
9231da177e4SLinus Torvalds 	int err;
92467b1020dSTakashi Iwai 	struct snd_harmony *h;
92567b1020dSTakashi Iwai 	static struct snd_device_ops ops = {
9261da177e4SLinus Torvalds 		.dev_free = snd_harmony_dev_free,
9271da177e4SLinus Torvalds 	};
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 	*rchip = NULL;
9301da177e4SLinus Torvalds 
93103f9ae25STakashi Iwai 	h = kzalloc(sizeof(*h), GFP_KERNEL);
9321da177e4SLinus Torvalds 	if (h == NULL)
9331da177e4SLinus Torvalds 		return -ENOMEM;
9341da177e4SLinus Torvalds 
93553f01bbaSMatthew Wilcox 	h->hpa = padev->hpa.start;
9361da177e4SLinus Torvalds 	h->card = card;
9371da177e4SLinus Torvalds 	h->dev = padev;
93803f9ae25STakashi Iwai 	h->irq = -1;
93953f01bbaSMatthew Wilcox 	h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE);
9401da177e4SLinus Torvalds 	if (h->iobase == NULL) {
9411da177e4SLinus Torvalds 		printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
942b04b4f78SAlexander Beregalov 		       (unsigned long)padev->hpa.start);
9431da177e4SLinus Torvalds 		err = -EBUSY;
9441da177e4SLinus Torvalds 		goto free_and_ret;
9451da177e4SLinus Torvalds 	}
9461da177e4SLinus Torvalds 
94703f9ae25STakashi Iwai 	err = request_irq(padev->irq, snd_harmony_interrupt, 0,
9481da177e4SLinus Torvalds 			  "harmony", h);
9491da177e4SLinus Torvalds 	if (err) {
9501da177e4SLinus Torvalds 		printk(KERN_ERR PFX "could not obtain interrupt %d",
95103f9ae25STakashi Iwai 		       padev->irq);
9521da177e4SLinus Torvalds 		goto free_and_ret;
9531da177e4SLinus Torvalds 	}
95403f9ae25STakashi Iwai 	h->irq = padev->irq;
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds 	spin_lock_init(&h->mixer_lock);
9571da177e4SLinus Torvalds 	spin_lock_init(&h->lock);
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds         if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
9601da177e4SLinus Torvalds                                   h, &ops)) < 0) {
9611da177e4SLinus Torvalds                 goto free_and_ret;
9621da177e4SLinus Torvalds         }
9631da177e4SLinus Torvalds 
964a76af199STakashi Iwai 	snd_card_set_dev(card, &padev->dev);
965a76af199STakashi Iwai 
9661da177e4SLinus Torvalds 	*rchip = h;
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds 	return 0;
9691da177e4SLinus Torvalds 
9701da177e4SLinus Torvalds free_and_ret:
9711da177e4SLinus Torvalds 	snd_harmony_free(h);
9721da177e4SLinus Torvalds 	return err;
9731da177e4SLinus Torvalds }
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds static int __devinit
9761da177e4SLinus Torvalds snd_harmony_probe(struct parisc_device *padev)
9771da177e4SLinus Torvalds {
9781da177e4SLinus Torvalds 	int err;
97967b1020dSTakashi Iwai 	struct snd_card *card;
98067b1020dSTakashi Iwai 	struct snd_harmony *h;
9811da177e4SLinus Torvalds 
982bd7dd77cSTakashi Iwai 	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
983bd7dd77cSTakashi Iwai 	if (err < 0)
984bd7dd77cSTakashi Iwai 		return err;
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 	err = snd_harmony_create(card, padev, &h);
98703f9ae25STakashi Iwai 	if (err < 0)
9881da177e4SLinus Torvalds 		goto free_and_ret;
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds 	err = snd_harmony_pcm_init(h);
99103f9ae25STakashi Iwai 	if (err < 0)
9921da177e4SLinus Torvalds 		goto free_and_ret;
9931da177e4SLinus Torvalds 
9941da177e4SLinus Torvalds 	err = snd_harmony_mixer_init(h);
99503f9ae25STakashi Iwai 	if (err < 0)
9961da177e4SLinus Torvalds 		goto free_and_ret;
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 	strcpy(card->driver, "harmony");
9991da177e4SLinus Torvalds 	strcpy(card->shortname, "Harmony");
10001da177e4SLinus Torvalds 	sprintf(card->longname, "%s at 0x%lx, irq %i",
10011da177e4SLinus Torvalds 		card->shortname, h->hpa, h->irq);
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	err = snd_card_register(card);
100403f9ae25STakashi Iwai 	if (err < 0)
10051da177e4SLinus Torvalds 		goto free_and_ret;
10061da177e4SLinus Torvalds 
100703f9ae25STakashi Iwai 	parisc_set_drvdata(padev, card);
10081da177e4SLinus Torvalds 	return 0;
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds free_and_ret:
10111da177e4SLinus Torvalds 	snd_card_free(card);
10121da177e4SLinus Torvalds 	return err;
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds 
10151da177e4SLinus Torvalds static int __devexit
10161da177e4SLinus Torvalds snd_harmony_remove(struct parisc_device *padev)
10171da177e4SLinus Torvalds {
101803f9ae25STakashi Iwai 	snd_card_free(parisc_get_drvdata(padev));
101903f9ae25STakashi Iwai 	parisc_set_drvdata(padev, NULL);
10201da177e4SLinus Torvalds 	return 0;
10211da177e4SLinus Torvalds }
10221da177e4SLinus Torvalds 
10231da177e4SLinus Torvalds static struct parisc_driver snd_harmony_driver = {
10241da177e4SLinus Torvalds 	.name = "harmony",
10251da177e4SLinus Torvalds 	.id_table = snd_harmony_devtable,
10261da177e4SLinus Torvalds 	.probe = snd_harmony_probe,
102782ced6fdSJean Delvare 	.remove = __devexit_p(snd_harmony_remove),
10281da177e4SLinus Torvalds };
10291da177e4SLinus Torvalds 
10301da177e4SLinus Torvalds static int __init
10311da177e4SLinus Torvalds alsa_harmony_init(void)
10321da177e4SLinus Torvalds {
103303f9ae25STakashi Iwai 	return register_parisc_driver(&snd_harmony_driver);
10341da177e4SLinus Torvalds }
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds static void __exit
10371da177e4SLinus Torvalds alsa_harmony_fini(void)
10381da177e4SLinus Torvalds {
103967b1020dSTakashi Iwai 	unregister_parisc_driver(&snd_harmony_driver);
10401da177e4SLinus Torvalds }
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds MODULE_LICENSE("GPL");
10431da177e4SLinus Torvalds MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
10441da177e4SLinus Torvalds MODULE_DESCRIPTION("Harmony sound driver");
10451da177e4SLinus Torvalds 
10461da177e4SLinus Torvalds module_init(alsa_harmony_init);
10471da177e4SLinus Torvalds module_exit(alsa_harmony_fini);
1048