182c29810SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /* Hewlett-Packard Harmony audio driver
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * This is a driver for the Harmony audio chipset found
51da177e4SLinus Torvalds * on the LASI ASIC of various early HP PA-RISC workstations.
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Copyright (C) 2004, Kyle McMartin <kyle@{debian.org,parisc-linux.org}>
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Based on the previous Harmony incarnations by,
101da177e4SLinus Torvalds * Copyright 2000 (c) Linuxcare Canada, Alex deVries
111da177e4SLinus Torvalds * Copyright 2000-2003 (c) Helge Deller
121da177e4SLinus Torvalds * Copyright 2001 (c) Matthieu Delahaye
131da177e4SLinus Torvalds * Copyright 2001 (c) Jean-Christophe Vaugeois
141da177e4SLinus Torvalds * Copyright 2003 (c) Laurent Canet
151da177e4SLinus Torvalds * Copyright 2004 (c) Stuart Brady
161da177e4SLinus Torvalds *
171da177e4SLinus Torvalds * Notes:
181da177e4SLinus Torvalds * - graveyard and silence buffers last for lifetime of
191da177e4SLinus Torvalds * the driver. playback and capture buffers are allocated
201da177e4SLinus Torvalds * per _open()/_close().
211da177e4SLinus Torvalds *
221da177e4SLinus Torvalds * TODO:
231da177e4SLinus Torvalds */
241da177e4SLinus Torvalds
251da177e4SLinus Torvalds #include <linux/init.h>
261da177e4SLinus Torvalds #include <linux/slab.h>
271da177e4SLinus Torvalds #include <linux/time.h>
281da177e4SLinus Torvalds #include <linux/wait.h>
291da177e4SLinus Torvalds #include <linux/delay.h>
301da177e4SLinus Torvalds #include <linux/module.h>
311da177e4SLinus Torvalds #include <linux/interrupt.h>
321da177e4SLinus Torvalds #include <linux/spinlock.h>
331da177e4SLinus Torvalds #include <linux/dma-mapping.h>
346cbbfe1cSTakashi Iwai #include <linux/io.h>
351da177e4SLinus Torvalds
361da177e4SLinus Torvalds #include <sound/core.h>
371da177e4SLinus Torvalds #include <sound/pcm.h>
381da177e4SLinus Torvalds #include <sound/control.h>
391da177e4SLinus Torvalds #include <sound/rawmidi.h>
401da177e4SLinus Torvalds #include <sound/initval.h>
411da177e4SLinus Torvalds #include <sound/info.h>
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds #include <asm/hardware.h>
441da177e4SLinus Torvalds #include <asm/parisc-device.h>
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds #include "harmony.h"
471da177e4SLinus Torvalds
4803f9ae25STakashi Iwai static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
4903f9ae25STakashi Iwai static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
5003f9ae25STakashi Iwai module_param(index, int, 0444);
5103f9ae25STakashi Iwai MODULE_PARM_DESC(index, "Index value for Harmony driver.");
5203f9ae25STakashi Iwai module_param(id, charp, 0444);
5303f9ae25STakashi Iwai MODULE_PARM_DESC(id, "ID string for Harmony driver.");
5403f9ae25STakashi Iwai
5503f9ae25STakashi Iwai
56c8dbaa22SHelge Deller static const struct parisc_device_id snd_harmony_devtable[] __initconst = {
571da177e4SLinus Torvalds /* bushmaster / flounder */
581da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A },
591da177e4SLinus Torvalds /* 712 / 715 */
601da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B },
611da177e4SLinus Torvalds /* pace */
621da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E },
631da177e4SLinus Torvalds /* outfield / coral II */
641da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F },
651da177e4SLinus Torvalds { 0, }
661da177e4SLinus Torvalds };
671da177e4SLinus Torvalds
681da177e4SLinus Torvalds MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds #define NAME "harmony"
711da177e4SLinus Torvalds #define PFX NAME ": "
721da177e4SLinus Torvalds
73d236d361STakashi Iwai static const unsigned int snd_harmony_rates[] = {
741da177e4SLinus Torvalds 5512, 6615, 8000, 9600,
751da177e4SLinus Torvalds 11025, 16000, 18900, 22050,
761da177e4SLinus Torvalds 27428, 32000, 33075, 37800,
771da177e4SLinus Torvalds 44100, 48000
781da177e4SLinus Torvalds };
791da177e4SLinus Torvalds
80d236d361STakashi Iwai static const unsigned int rate_bits[14] = {
811da177e4SLinus Torvalds HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,
821da177e4SLinus Torvalds HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,
831da177e4SLinus Torvalds HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,
841da177e4SLinus Torvalds HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ,
851da177e4SLinus Torvalds HARMONY_SR_44KHZ, HARMONY_SR_48KHZ
861da177e4SLinus Torvalds };
871da177e4SLinus Torvalds
88d236d361STakashi Iwai static const struct snd_pcm_hw_constraint_list hw_constraint_rates = {
891da177e4SLinus Torvalds .count = ARRAY_SIZE(snd_harmony_rates),
901da177e4SLinus Torvalds .list = snd_harmony_rates,
911da177e4SLinus Torvalds .mask = 0,
921da177e4SLinus Torvalds };
931da177e4SLinus Torvalds
9467b1020dSTakashi Iwai static inline unsigned long
harmony_read(struct snd_harmony * h,unsigned r)9567b1020dSTakashi Iwai harmony_read(struct snd_harmony *h, unsigned r)
961da177e4SLinus Torvalds {
971da177e4SLinus Torvalds return __raw_readl(h->iobase + r);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds
10067b1020dSTakashi Iwai static inline void
harmony_write(struct snd_harmony * h,unsigned r,unsigned long v)10167b1020dSTakashi Iwai harmony_write(struct snd_harmony *h, unsigned r, unsigned long v)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds __raw_writel(v, h->iobase + r);
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds
10667b1020dSTakashi Iwai static inline void
harmony_wait_for_control(struct snd_harmony * h)10767b1020dSTakashi Iwai harmony_wait_for_control(struct snd_harmony *h)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ;
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds
11267b1020dSTakashi Iwai static inline void
harmony_reset(struct snd_harmony * h)11367b1020dSTakashi Iwai harmony_reset(struct snd_harmony *h)
1141da177e4SLinus Torvalds {
1151da177e4SLinus Torvalds harmony_write(h, HARMONY_RESET, 1);
1161da177e4SLinus Torvalds mdelay(50);
1171da177e4SLinus Torvalds harmony_write(h, HARMONY_RESET, 0);
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds static void
harmony_disable_interrupts(struct snd_harmony * h)12167b1020dSTakashi Iwai harmony_disable_interrupts(struct snd_harmony *h)
1221da177e4SLinus Torvalds {
1231da177e4SLinus Torvalds u32 dstatus;
1241da177e4SLinus Torvalds harmony_wait_for_control(h);
1251da177e4SLinus Torvalds dstatus = harmony_read(h, HARMONY_DSTATUS);
1261da177e4SLinus Torvalds dstatus &= ~HARMONY_DSTATUS_IE;
1271da177e4SLinus Torvalds harmony_write(h, HARMONY_DSTATUS, dstatus);
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds
1301da177e4SLinus Torvalds static void
harmony_enable_interrupts(struct snd_harmony * h)13167b1020dSTakashi Iwai harmony_enable_interrupts(struct snd_harmony *h)
1321da177e4SLinus Torvalds {
1331da177e4SLinus Torvalds u32 dstatus;
1341da177e4SLinus Torvalds harmony_wait_for_control(h);
1351da177e4SLinus Torvalds dstatus = harmony_read(h, HARMONY_DSTATUS);
1361da177e4SLinus Torvalds dstatus |= HARMONY_DSTATUS_IE;
1371da177e4SLinus Torvalds harmony_write(h, HARMONY_DSTATUS, dstatus);
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds
1401da177e4SLinus Torvalds static void
harmony_mute(struct snd_harmony * h)14167b1020dSTakashi Iwai harmony_mute(struct snd_harmony *h)
1421da177e4SLinus Torvalds {
1431da177e4SLinus Torvalds unsigned long flags;
1441da177e4SLinus Torvalds
1451da177e4SLinus Torvalds spin_lock_irqsave(&h->mixer_lock, flags);
1461da177e4SLinus Torvalds harmony_wait_for_control(h);
1471da177e4SLinus Torvalds harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE);
1481da177e4SLinus Torvalds spin_unlock_irqrestore(&h->mixer_lock, flags);
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds
1511da177e4SLinus Torvalds static void
harmony_unmute(struct snd_harmony * h)15267b1020dSTakashi Iwai harmony_unmute(struct snd_harmony *h)
1531da177e4SLinus Torvalds {
1541da177e4SLinus Torvalds unsigned long flags;
1551da177e4SLinus Torvalds
1561da177e4SLinus Torvalds spin_lock_irqsave(&h->mixer_lock, flags);
1571da177e4SLinus Torvalds harmony_wait_for_control(h);
1581da177e4SLinus Torvalds harmony_write(h, HARMONY_GAINCTL, h->st.gain);
1591da177e4SLinus Torvalds spin_unlock_irqrestore(&h->mixer_lock, flags);
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds
1621da177e4SLinus Torvalds static void
harmony_set_control(struct snd_harmony * h)16367b1020dSTakashi Iwai harmony_set_control(struct snd_harmony *h)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds u32 ctrl;
1661da177e4SLinus Torvalds unsigned long flags;
1671da177e4SLinus Torvalds
1681da177e4SLinus Torvalds spin_lock_irqsave(&h->lock, flags);
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds ctrl = (HARMONY_CNTL_C |
1711da177e4SLinus Torvalds (h->st.format << 6) |
1721da177e4SLinus Torvalds (h->st.stereo << 5) |
1731da177e4SLinus Torvalds (h->st.rate));
1741da177e4SLinus Torvalds
1751da177e4SLinus Torvalds harmony_wait_for_control(h);
1761da177e4SLinus Torvalds harmony_write(h, HARMONY_CNTL, ctrl);
1771da177e4SLinus Torvalds
1781da177e4SLinus Torvalds spin_unlock_irqrestore(&h->lock, flags);
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds
1811da177e4SLinus Torvalds static irqreturn_t
snd_harmony_interrupt(int irq,void * dev)1827d12e780SDavid Howells snd_harmony_interrupt(int irq, void *dev)
1831da177e4SLinus Torvalds {
1841da177e4SLinus Torvalds u32 dstatus;
18567b1020dSTakashi Iwai struct snd_harmony *h = dev;
1861da177e4SLinus Torvalds
1871da177e4SLinus Torvalds spin_lock(&h->lock);
1881da177e4SLinus Torvalds harmony_disable_interrupts(h);
1891da177e4SLinus Torvalds harmony_wait_for_control(h);
1901da177e4SLinus Torvalds dstatus = harmony_read(h, HARMONY_DSTATUS);
1911da177e4SLinus Torvalds spin_unlock(&h->lock);
1921da177e4SLinus Torvalds
1931da177e4SLinus Torvalds if (dstatus & HARMONY_DSTATUS_PN) {
1943a165680SStuart Brady if (h->psubs && h->st.playing) {
1951da177e4SLinus Torvalds spin_lock(&h->lock);
1961da177e4SLinus Torvalds h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
1971da177e4SLinus Torvalds h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD,
2001da177e4SLinus Torvalds h->pbuf.addr + h->pbuf.buf);
2011da177e4SLinus Torvalds h->stats.play_intr++;
2021da177e4SLinus Torvalds spin_unlock(&h->lock);
2031da177e4SLinus Torvalds snd_pcm_period_elapsed(h->psubs);
2041da177e4SLinus Torvalds } else {
2051da177e4SLinus Torvalds spin_lock(&h->lock);
2061da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
2071da177e4SLinus Torvalds h->stats.silence_intr++;
2081da177e4SLinus Torvalds spin_unlock(&h->lock);
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds
2121da177e4SLinus Torvalds if (dstatus & HARMONY_DSTATUS_RN) {
2133a165680SStuart Brady if (h->csubs && h->st.capturing) {
2141da177e4SLinus Torvalds spin_lock(&h->lock);
2151da177e4SLinus Torvalds h->cbuf.buf += h->cbuf.count;
2161da177e4SLinus Torvalds h->cbuf.buf %= h->cbuf.size;
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD,
2191da177e4SLinus Torvalds h->cbuf.addr + h->cbuf.buf);
2201da177e4SLinus Torvalds h->stats.rec_intr++;
2211da177e4SLinus Torvalds spin_unlock(&h->lock);
2221da177e4SLinus Torvalds snd_pcm_period_elapsed(h->csubs);
2231da177e4SLinus Torvalds } else {
2241da177e4SLinus Torvalds spin_lock(&h->lock);
2251da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
2261da177e4SLinus Torvalds h->stats.graveyard_intr++;
2271da177e4SLinus Torvalds spin_unlock(&h->lock);
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds
2311da177e4SLinus Torvalds spin_lock(&h->lock);
2321da177e4SLinus Torvalds harmony_enable_interrupts(h);
2331da177e4SLinus Torvalds spin_unlock(&h->lock);
2341da177e4SLinus Torvalds
2351da177e4SLinus Torvalds return IRQ_HANDLED;
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds
2381da177e4SLinus Torvalds static unsigned int
snd_harmony_rate_bits(int rate)2391da177e4SLinus Torvalds snd_harmony_rate_bits(int rate)
2401da177e4SLinus Torvalds {
2411da177e4SLinus Torvalds unsigned int i;
2421da177e4SLinus Torvalds
2431da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(snd_harmony_rates); i++)
2441da177e4SLinus Torvalds if (snd_harmony_rates[i] == rate)
2451da177e4SLinus Torvalds return rate_bits[i];
2461da177e4SLinus Torvalds
2471da177e4SLinus Torvalds return HARMONY_SR_44KHZ;
2481da177e4SLinus Torvalds }
2491da177e4SLinus Torvalds
250a977d045SBhumika Goyal static const struct snd_pcm_hardware snd_harmony_playback =
2511da177e4SLinus Torvalds {
2521da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
2531da177e4SLinus Torvalds SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
2541da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER),
2551da177e4SLinus Torvalds .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
2561da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_A_LAW),
2571da177e4SLinus Torvalds .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
2581da177e4SLinus Torvalds SNDRV_PCM_RATE_KNOT),
2591da177e4SLinus Torvalds .rate_min = 5512,
2601da177e4SLinus Torvalds .rate_max = 48000,
2611da177e4SLinus Torvalds .channels_min = 1,
2621da177e4SLinus Torvalds .channels_max = 2,
2631da177e4SLinus Torvalds .buffer_bytes_max = MAX_BUF_SIZE,
2641da177e4SLinus Torvalds .period_bytes_min = BUF_SIZE,
2651da177e4SLinus Torvalds .period_bytes_max = BUF_SIZE,
2661da177e4SLinus Torvalds .periods_min = 1,
2671da177e4SLinus Torvalds .periods_max = MAX_BUFS,
2681da177e4SLinus Torvalds .fifo_size = 0,
2691da177e4SLinus Torvalds };
2701da177e4SLinus Torvalds
271a977d045SBhumika Goyal static const struct snd_pcm_hardware snd_harmony_capture =
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
2741da177e4SLinus Torvalds SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
2751da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER),
2761da177e4SLinus Torvalds .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
2771da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_A_LAW),
2781da177e4SLinus Torvalds .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
2791da177e4SLinus Torvalds SNDRV_PCM_RATE_KNOT),
2801da177e4SLinus Torvalds .rate_min = 5512,
2811da177e4SLinus Torvalds .rate_max = 48000,
2821da177e4SLinus Torvalds .channels_min = 1,
2831da177e4SLinus Torvalds .channels_max = 2,
2841da177e4SLinus Torvalds .buffer_bytes_max = MAX_BUF_SIZE,
2851da177e4SLinus Torvalds .period_bytes_min = BUF_SIZE,
2861da177e4SLinus Torvalds .period_bytes_max = BUF_SIZE,
2871da177e4SLinus Torvalds .periods_min = 1,
2881da177e4SLinus Torvalds .periods_max = MAX_BUFS,
2891da177e4SLinus Torvalds .fifo_size = 0,
2901da177e4SLinus Torvalds };
2911da177e4SLinus Torvalds
2921da177e4SLinus Torvalds static int
snd_harmony_playback_trigger(struct snd_pcm_substream * ss,int cmd)29367b1020dSTakashi Iwai snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
2941da177e4SLinus Torvalds {
29567b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
2961da177e4SLinus Torvalds
2971da177e4SLinus Torvalds if (h->st.capturing)
2981da177e4SLinus Torvalds return -EBUSY;
2991da177e4SLinus Torvalds
30003f9ae25STakashi Iwai spin_lock(&h->lock);
3011da177e4SLinus Torvalds switch (cmd) {
3021da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START:
3031da177e4SLinus Torvalds h->st.playing = 1;
3041da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr);
3051da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
3061da177e4SLinus Torvalds harmony_unmute(h);
3071da177e4SLinus Torvalds harmony_enable_interrupts(h);
3081da177e4SLinus Torvalds break;
3091da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP:
3101da177e4SLinus Torvalds h->st.playing = 0;
3111da177e4SLinus Torvalds harmony_mute(h);
3123a165680SStuart Brady harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
3131da177e4SLinus Torvalds harmony_disable_interrupts(h);
3141da177e4SLinus Torvalds break;
3151da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3161da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3171da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND:
3181da177e4SLinus Torvalds default:
31903f9ae25STakashi Iwai spin_unlock(&h->lock);
3201da177e4SLinus Torvalds snd_BUG();
3211da177e4SLinus Torvalds return -EINVAL;
3221da177e4SLinus Torvalds }
32303f9ae25STakashi Iwai spin_unlock(&h->lock);
3241da177e4SLinus Torvalds
3251da177e4SLinus Torvalds return 0;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds
3281da177e4SLinus Torvalds static int
snd_harmony_capture_trigger(struct snd_pcm_substream * ss,int cmd)32967b1020dSTakashi Iwai snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
3301da177e4SLinus Torvalds {
33167b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
3321da177e4SLinus Torvalds
3331da177e4SLinus Torvalds if (h->st.playing)
3341da177e4SLinus Torvalds return -EBUSY;
3351da177e4SLinus Torvalds
33603f9ae25STakashi Iwai spin_lock(&h->lock);
3371da177e4SLinus Torvalds switch (cmd) {
3381da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START:
3391da177e4SLinus Torvalds h->st.capturing = 1;
3401da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
3411da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD, h->cbuf.addr);
3421da177e4SLinus Torvalds harmony_unmute(h);
3431da177e4SLinus Torvalds harmony_enable_interrupts(h);
3441da177e4SLinus Torvalds break;
3451da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP:
3461da177e4SLinus Torvalds h->st.capturing = 0;
3471da177e4SLinus Torvalds harmony_mute(h);
3483a165680SStuart Brady harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
3491da177e4SLinus Torvalds harmony_disable_interrupts(h);
3501da177e4SLinus Torvalds break;
3511da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3521da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3531da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND:
3541da177e4SLinus Torvalds default:
35503f9ae25STakashi Iwai spin_unlock(&h->lock);
3561da177e4SLinus Torvalds snd_BUG();
3571da177e4SLinus Torvalds return -EINVAL;
3581da177e4SLinus Torvalds }
35903f9ae25STakashi Iwai spin_unlock(&h->lock);
3601da177e4SLinus Torvalds
3611da177e4SLinus Torvalds return 0;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds
3641da177e4SLinus Torvalds static int
snd_harmony_set_data_format(struct snd_harmony * h,int fmt,int force)36567b1020dSTakashi Iwai snd_harmony_set_data_format(struct snd_harmony *h, int fmt, int force)
3661da177e4SLinus Torvalds {
3671da177e4SLinus Torvalds int o = h->st.format;
3681da177e4SLinus Torvalds int n;
3691da177e4SLinus Torvalds
3701da177e4SLinus Torvalds switch(fmt) {
3711da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_S16_BE:
3721da177e4SLinus Torvalds n = HARMONY_DF_16BIT_LINEAR;
3731da177e4SLinus Torvalds break;
3741da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_A_LAW:
3751da177e4SLinus Torvalds n = HARMONY_DF_8BIT_ALAW;
3761da177e4SLinus Torvalds break;
3771da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_MU_LAW:
3781da177e4SLinus Torvalds n = HARMONY_DF_8BIT_ULAW;
3791da177e4SLinus Torvalds break;
3801da177e4SLinus Torvalds default:
3811da177e4SLinus Torvalds n = HARMONY_DF_16BIT_LINEAR;
3821da177e4SLinus Torvalds break;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds
3851da177e4SLinus Torvalds if (force || o != n) {
3861da177e4SLinus Torvalds snd_pcm_format_set_silence(fmt, h->sdma.area, SILENCE_BUFSZ /
3871da177e4SLinus Torvalds (snd_pcm_format_physical_width(fmt)
3881da177e4SLinus Torvalds / 8));
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds return n;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds
3941da177e4SLinus Torvalds static int
snd_harmony_playback_prepare(struct snd_pcm_substream * ss)39567b1020dSTakashi Iwai snd_harmony_playback_prepare(struct snd_pcm_substream *ss)
3961da177e4SLinus Torvalds {
39767b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
39867b1020dSTakashi Iwai struct snd_pcm_runtime *rt = ss->runtime;
3991da177e4SLinus Torvalds
4001da177e4SLinus Torvalds if (h->st.capturing)
4011da177e4SLinus Torvalds return -EBUSY;
4021da177e4SLinus Torvalds
4031da177e4SLinus Torvalds h->pbuf.size = snd_pcm_lib_buffer_bytes(ss);
4041da177e4SLinus Torvalds h->pbuf.count = snd_pcm_lib_period_bytes(ss);
4053a165680SStuart Brady if (h->pbuf.buf >= h->pbuf.size)
4061da177e4SLinus Torvalds h->pbuf.buf = 0;
4071da177e4SLinus Torvalds h->st.playing = 0;
4081da177e4SLinus Torvalds
4091da177e4SLinus Torvalds h->st.rate = snd_harmony_rate_bits(rt->rate);
4101da177e4SLinus Torvalds h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
4111da177e4SLinus Torvalds
4121da177e4SLinus Torvalds if (rt->channels == 2)
4131da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_STEREO;
4141da177e4SLinus Torvalds else
4151da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_MONO;
4161da177e4SLinus Torvalds
4171da177e4SLinus Torvalds harmony_set_control(h);
4181da177e4SLinus Torvalds
4191da177e4SLinus Torvalds h->pbuf.addr = rt->dma_addr;
4201da177e4SLinus Torvalds
4211da177e4SLinus Torvalds return 0;
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds
4241da177e4SLinus Torvalds static int
snd_harmony_capture_prepare(struct snd_pcm_substream * ss)42567b1020dSTakashi Iwai snd_harmony_capture_prepare(struct snd_pcm_substream *ss)
4261da177e4SLinus Torvalds {
42767b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
42867b1020dSTakashi Iwai struct snd_pcm_runtime *rt = ss->runtime;
4291da177e4SLinus Torvalds
4301da177e4SLinus Torvalds if (h->st.playing)
4311da177e4SLinus Torvalds return -EBUSY;
4321da177e4SLinus Torvalds
4331da177e4SLinus Torvalds h->cbuf.size = snd_pcm_lib_buffer_bytes(ss);
4341da177e4SLinus Torvalds h->cbuf.count = snd_pcm_lib_period_bytes(ss);
4353a165680SStuart Brady if (h->cbuf.buf >= h->cbuf.size)
4361da177e4SLinus Torvalds h->cbuf.buf = 0;
4371da177e4SLinus Torvalds h->st.capturing = 0;
4381da177e4SLinus Torvalds
4391da177e4SLinus Torvalds h->st.rate = snd_harmony_rate_bits(rt->rate);
4401da177e4SLinus Torvalds h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
4411da177e4SLinus Torvalds
4421da177e4SLinus Torvalds if (rt->channels == 2)
4431da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_STEREO;
4441da177e4SLinus Torvalds else
4451da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_MONO;
4461da177e4SLinus Torvalds
4471da177e4SLinus Torvalds harmony_set_control(h);
4481da177e4SLinus Torvalds
4491da177e4SLinus Torvalds h->cbuf.addr = rt->dma_addr;
4501da177e4SLinus Torvalds
4511da177e4SLinus Torvalds return 0;
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds
4541da177e4SLinus Torvalds static snd_pcm_uframes_t
snd_harmony_playback_pointer(struct snd_pcm_substream * ss)45567b1020dSTakashi Iwai snd_harmony_playback_pointer(struct snd_pcm_substream *ss)
4561da177e4SLinus Torvalds {
45767b1020dSTakashi Iwai struct snd_pcm_runtime *rt = ss->runtime;
45867b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
4591da177e4SLinus Torvalds unsigned long pcuradd;
4601da177e4SLinus Torvalds unsigned long played;
4611da177e4SLinus Torvalds
4621da177e4SLinus Torvalds if (!(h->st.playing) || (h->psubs == NULL))
4631da177e4SLinus Torvalds return 0;
4641da177e4SLinus Torvalds
4651da177e4SLinus Torvalds if ((h->pbuf.addr == 0) || (h->pbuf.size == 0))
4661da177e4SLinus Torvalds return 0;
4671da177e4SLinus Torvalds
4681da177e4SLinus Torvalds pcuradd = harmony_read(h, HARMONY_PCURADD);
4691da177e4SLinus Torvalds played = pcuradd - h->pbuf.addr;
4701da177e4SLinus Torvalds
4711da177e4SLinus Torvalds #ifdef HARMONY_DEBUG
4721da177e4SLinus Torvalds printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes\n",
4731da177e4SLinus Torvalds pcuradd, h->pbuf.addr, played);
4741da177e4SLinus Torvalds #endif
4751da177e4SLinus Torvalds
4761da177e4SLinus Torvalds if (pcuradd > h->pbuf.addr + h->pbuf.size) {
4771da177e4SLinus Torvalds return 0;
4781da177e4SLinus Torvalds }
4791da177e4SLinus Torvalds
4801da177e4SLinus Torvalds return bytes_to_frames(rt, played);
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds
4831da177e4SLinus Torvalds static snd_pcm_uframes_t
snd_harmony_capture_pointer(struct snd_pcm_substream * ss)48467b1020dSTakashi Iwai snd_harmony_capture_pointer(struct snd_pcm_substream *ss)
4851da177e4SLinus Torvalds {
48667b1020dSTakashi Iwai struct snd_pcm_runtime *rt = ss->runtime;
48767b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
4881da177e4SLinus Torvalds unsigned long rcuradd;
4891da177e4SLinus Torvalds unsigned long caught;
4901da177e4SLinus Torvalds
4911da177e4SLinus Torvalds if (!(h->st.capturing) || (h->csubs == NULL))
4921da177e4SLinus Torvalds return 0;
4931da177e4SLinus Torvalds
4941da177e4SLinus Torvalds if ((h->cbuf.addr == 0) || (h->cbuf.size == 0))
4951da177e4SLinus Torvalds return 0;
4961da177e4SLinus Torvalds
4971da177e4SLinus Torvalds rcuradd = harmony_read(h, HARMONY_RCURADD);
4981da177e4SLinus Torvalds caught = rcuradd - h->cbuf.addr;
4991da177e4SLinus Torvalds
5001da177e4SLinus Torvalds #ifdef HARMONY_DEBUG
5011da177e4SLinus Torvalds printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes\n",
5021da177e4SLinus Torvalds rcuradd, h->cbuf.addr, caught);
5031da177e4SLinus Torvalds #endif
5041da177e4SLinus Torvalds
5051da177e4SLinus Torvalds if (rcuradd > h->cbuf.addr + h->cbuf.size) {
5061da177e4SLinus Torvalds return 0;
5071da177e4SLinus Torvalds }
5081da177e4SLinus Torvalds
5091da177e4SLinus Torvalds return bytes_to_frames(rt, caught);
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds
5121da177e4SLinus Torvalds static int
snd_harmony_playback_open(struct snd_pcm_substream * ss)51367b1020dSTakashi Iwai snd_harmony_playback_open(struct snd_pcm_substream *ss)
5141da177e4SLinus Torvalds {
51567b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
51667b1020dSTakashi Iwai struct snd_pcm_runtime *rt = ss->runtime;
5171da177e4SLinus Torvalds int err;
5181da177e4SLinus Torvalds
5191da177e4SLinus Torvalds h->psubs = ss;
5201da177e4SLinus Torvalds rt->hw = snd_harmony_playback;
5211da177e4SLinus Torvalds snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
5221da177e4SLinus Torvalds &hw_constraint_rates);
5231da177e4SLinus Torvalds
5241da177e4SLinus Torvalds err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
5251da177e4SLinus Torvalds if (err < 0)
5261da177e4SLinus Torvalds return err;
5271da177e4SLinus Torvalds
5281da177e4SLinus Torvalds return 0;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds
5311da177e4SLinus Torvalds static int
snd_harmony_capture_open(struct snd_pcm_substream * ss)53267b1020dSTakashi Iwai snd_harmony_capture_open(struct snd_pcm_substream *ss)
5331da177e4SLinus Torvalds {
53467b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
53567b1020dSTakashi Iwai struct snd_pcm_runtime *rt = ss->runtime;
5361da177e4SLinus Torvalds int err;
5371da177e4SLinus Torvalds
5381da177e4SLinus Torvalds h->csubs = ss;
5391da177e4SLinus Torvalds rt->hw = snd_harmony_capture;
5401da177e4SLinus Torvalds snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
5411da177e4SLinus Torvalds &hw_constraint_rates);
5421da177e4SLinus Torvalds
5431da177e4SLinus Torvalds err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
5441da177e4SLinus Torvalds if (err < 0)
5451da177e4SLinus Torvalds return err;
5461da177e4SLinus Torvalds
5471da177e4SLinus Torvalds return 0;
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds
5501da177e4SLinus Torvalds static int
snd_harmony_playback_close(struct snd_pcm_substream * ss)55167b1020dSTakashi Iwai snd_harmony_playback_close(struct snd_pcm_substream *ss)
5521da177e4SLinus Torvalds {
55367b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
5541da177e4SLinus Torvalds h->psubs = NULL;
5551da177e4SLinus Torvalds return 0;
5561da177e4SLinus Torvalds }
5571da177e4SLinus Torvalds
5581da177e4SLinus Torvalds static int
snd_harmony_capture_close(struct snd_pcm_substream * ss)55967b1020dSTakashi Iwai snd_harmony_capture_close(struct snd_pcm_substream *ss)
5601da177e4SLinus Torvalds {
56167b1020dSTakashi Iwai struct snd_harmony *h = snd_pcm_substream_chip(ss);
5621da177e4SLinus Torvalds h->csubs = NULL;
5631da177e4SLinus Torvalds return 0;
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds
566c2992b19SArvind Yadav static const struct snd_pcm_ops snd_harmony_playback_ops = {
5671da177e4SLinus Torvalds .open = snd_harmony_playback_open,
5681da177e4SLinus Torvalds .close = snd_harmony_playback_close,
5691da177e4SLinus Torvalds .prepare = snd_harmony_playback_prepare,
5701da177e4SLinus Torvalds .trigger = snd_harmony_playback_trigger,
5711da177e4SLinus Torvalds .pointer = snd_harmony_playback_pointer,
5721da177e4SLinus Torvalds };
5731da177e4SLinus Torvalds
574c2992b19SArvind Yadav static const struct snd_pcm_ops snd_harmony_capture_ops = {
5751da177e4SLinus Torvalds .open = snd_harmony_capture_open,
5761da177e4SLinus Torvalds .close = snd_harmony_capture_close,
5771da177e4SLinus Torvalds .prepare = snd_harmony_capture_prepare,
5781da177e4SLinus Torvalds .trigger = snd_harmony_capture_trigger,
5791da177e4SLinus Torvalds .pointer = snd_harmony_capture_pointer,
5801da177e4SLinus Torvalds };
5811da177e4SLinus Torvalds
5821da177e4SLinus Torvalds static int
snd_harmony_pcm_init(struct snd_harmony * h)58367b1020dSTakashi Iwai snd_harmony_pcm_init(struct snd_harmony *h)
5841da177e4SLinus Torvalds {
58567b1020dSTakashi Iwai struct snd_pcm *pcm;
5861da177e4SLinus Torvalds int err;
5871da177e4SLinus Torvalds
588e8e0929dSJulia Lawall if (snd_BUG_ON(!h))
589e8e0929dSJulia Lawall return -EINVAL;
590e8e0929dSJulia Lawall
5911da177e4SLinus Torvalds harmony_disable_interrupts(h);
5921da177e4SLinus Torvalds
5931da177e4SLinus Torvalds err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm);
5941da177e4SLinus Torvalds if (err < 0)
5951da177e4SLinus Torvalds return err;
5961da177e4SLinus Torvalds
5971da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
5981da177e4SLinus Torvalds &snd_harmony_playback_ops);
5991da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
6001da177e4SLinus Torvalds &snd_harmony_capture_ops);
6011da177e4SLinus Torvalds
6021da177e4SLinus Torvalds pcm->private_data = h;
6031da177e4SLinus Torvalds pcm->info_flags = 0;
6041da177e4SLinus Torvalds strcpy(pcm->name, "harmony");
6051da177e4SLinus Torvalds h->pcm = pcm;
6061da177e4SLinus Torvalds
6071da177e4SLinus Torvalds h->psubs = NULL;
6081da177e4SLinus Torvalds h->csubs = NULL;
6091da177e4SLinus Torvalds
6101da177e4SLinus Torvalds /* initialize graveyard buffer */
6111da177e4SLinus Torvalds h->dma.type = SNDRV_DMA_TYPE_DEV;
6121da177e4SLinus Torvalds h->dma.dev = &h->dev->dev;
6131da177e4SLinus Torvalds err = snd_dma_alloc_pages(h->dma.type,
6141da177e4SLinus Torvalds h->dma.dev,
6151da177e4SLinus Torvalds BUF_SIZE*GRAVEYARD_BUFS,
6161da177e4SLinus Torvalds &h->gdma);
6171da177e4SLinus Torvalds if (err < 0) {
6181da177e4SLinus Torvalds printk(KERN_ERR PFX "cannot allocate graveyard buffer!\n");
6191da177e4SLinus Torvalds return err;
6201da177e4SLinus Torvalds }
6211da177e4SLinus Torvalds
6221da177e4SLinus Torvalds /* initialize silence buffers */
6231da177e4SLinus Torvalds err = snd_dma_alloc_pages(h->dma.type,
6241da177e4SLinus Torvalds h->dma.dev,
6251da177e4SLinus Torvalds BUF_SIZE*SILENCE_BUFS,
6261da177e4SLinus Torvalds &h->sdma);
6271da177e4SLinus Torvalds if (err < 0) {
6281da177e4SLinus Torvalds printk(KERN_ERR PFX "cannot allocate silence buffer!\n");
6291da177e4SLinus Torvalds return err;
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds
6321da177e4SLinus Torvalds /* pre-allocate space for DMA */
633005f3e67STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, h->dma.type, h->dma.dev,
634f32e5616STakashi Iwai MAX_BUF_SIZE, MAX_BUF_SIZE);
6351da177e4SLinus Torvalds
6361da177e4SLinus Torvalds h->st.format = snd_harmony_set_data_format(h,
6371da177e4SLinus Torvalds SNDRV_PCM_FORMAT_S16_BE, 1);
6381da177e4SLinus Torvalds
6391da177e4SLinus Torvalds return 0;
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds
6421da177e4SLinus Torvalds static void
snd_harmony_set_new_gain(struct snd_harmony * h)64367b1020dSTakashi Iwai snd_harmony_set_new_gain(struct snd_harmony *h)
6441da177e4SLinus Torvalds {
6451da177e4SLinus Torvalds harmony_wait_for_control(h);
6461da177e4SLinus Torvalds harmony_write(h, HARMONY_GAINCTL, h->st.gain);
6471da177e4SLinus Torvalds }
6481da177e4SLinus Torvalds
6491da177e4SLinus Torvalds static int
snd_harmony_mixercontrol_info(struct snd_kcontrol * kc,struct snd_ctl_elem_info * uinfo)65067b1020dSTakashi Iwai snd_harmony_mixercontrol_info(struct snd_kcontrol *kc,
65167b1020dSTakashi Iwai struct snd_ctl_elem_info *uinfo)
6521da177e4SLinus Torvalds {
6531da177e4SLinus Torvalds int mask = (kc->private_value >> 16) & 0xff;
6541da177e4SLinus Torvalds int left_shift = (kc->private_value) & 0xff;
6551da177e4SLinus Torvalds int right_shift = (kc->private_value >> 8) & 0xff;
6561da177e4SLinus Torvalds
6571da177e4SLinus Torvalds uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :
6581da177e4SLinus Torvalds SNDRV_CTL_ELEM_TYPE_INTEGER;
6591da177e4SLinus Torvalds uinfo->count = left_shift == right_shift ? 1 : 2;
6601da177e4SLinus Torvalds uinfo->value.integer.min = 0;
6611da177e4SLinus Torvalds uinfo->value.integer.max = mask;
6621da177e4SLinus Torvalds
6631da177e4SLinus Torvalds return 0;
6641da177e4SLinus Torvalds }
6651da177e4SLinus Torvalds
6661da177e4SLinus Torvalds static int
snd_harmony_volume_get(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)66767b1020dSTakashi Iwai snd_harmony_volume_get(struct snd_kcontrol *kc,
66867b1020dSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
6691da177e4SLinus Torvalds {
67067b1020dSTakashi Iwai struct snd_harmony *h = snd_kcontrol_chip(kc);
6711da177e4SLinus Torvalds int shift_left = (kc->private_value) & 0xff;
6721da177e4SLinus Torvalds int shift_right = (kc->private_value >> 8) & 0xff;
6731da177e4SLinus Torvalds int mask = (kc->private_value >> 16) & 0xff;
6741da177e4SLinus Torvalds int invert = (kc->private_value >> 24) & 0xff;
6751da177e4SLinus Torvalds int left, right;
6761da177e4SLinus Torvalds
67703f9ae25STakashi Iwai spin_lock_irq(&h->mixer_lock);
6781da177e4SLinus Torvalds
6791da177e4SLinus Torvalds left = (h->st.gain >> shift_left) & mask;
6801da177e4SLinus Torvalds right = (h->st.gain >> shift_right) & mask;
6811da177e4SLinus Torvalds if (invert) {
6821da177e4SLinus Torvalds left = mask - left;
6831da177e4SLinus Torvalds right = mask - right;
6841da177e4SLinus Torvalds }
6853a165680SStuart Brady
6861da177e4SLinus Torvalds ucontrol->value.integer.value[0] = left;
6873a165680SStuart Brady if (shift_left != shift_right)
6881da177e4SLinus Torvalds ucontrol->value.integer.value[1] = right;
6891da177e4SLinus Torvalds
69003f9ae25STakashi Iwai spin_unlock_irq(&h->mixer_lock);
6911da177e4SLinus Torvalds
6921da177e4SLinus Torvalds return 0;
6931da177e4SLinus Torvalds }
6941da177e4SLinus Torvalds
6951da177e4SLinus Torvalds static int
snd_harmony_volume_put(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)69667b1020dSTakashi Iwai snd_harmony_volume_put(struct snd_kcontrol *kc,
69767b1020dSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
6981da177e4SLinus Torvalds {
69967b1020dSTakashi Iwai struct snd_harmony *h = snd_kcontrol_chip(kc);
7001da177e4SLinus Torvalds int shift_left = (kc->private_value) & 0xff;
7011da177e4SLinus Torvalds int shift_right = (kc->private_value >> 8) & 0xff;
7021da177e4SLinus Torvalds int mask = (kc->private_value >> 16) & 0xff;
7031da177e4SLinus Torvalds int invert = (kc->private_value >> 24) & 0xff;
7041da177e4SLinus Torvalds int left, right;
7051da177e4SLinus Torvalds int old_gain = h->st.gain;
7061da177e4SLinus Torvalds
70703f9ae25STakashi Iwai spin_lock_irq(&h->mixer_lock);
7081da177e4SLinus Torvalds
7093a165680SStuart Brady left = ucontrol->value.integer.value[0] & mask;
7103a165680SStuart Brady if (invert)
7113a165680SStuart Brady left = mask - left;
7123a165680SStuart Brady h->st.gain &= ~( (mask << shift_left ) );
7133a165680SStuart Brady h->st.gain |= (left << shift_left);
7143a165680SStuart Brady
7153a165680SStuart Brady if (shift_left != shift_right) {
7163a165680SStuart Brady right = ucontrol->value.integer.value[1] & mask;
7173a165680SStuart Brady if (invert)
7183a165680SStuart Brady right = mask - right;
7193a165680SStuart Brady h->st.gain &= ~( (mask << shift_right) );
7203a165680SStuart Brady h->st.gain |= (right << shift_right);
7213a165680SStuart Brady }
7223a165680SStuart Brady
7231da177e4SLinus Torvalds snd_harmony_set_new_gain(h);
7241da177e4SLinus Torvalds
72503f9ae25STakashi Iwai spin_unlock_irq(&h->mixer_lock);
7261da177e4SLinus Torvalds
7273a165680SStuart Brady return h->st.gain != old_gain;
7283a165680SStuart Brady }
7293a165680SStuart Brady
7303a165680SStuart Brady static int
snd_harmony_captureroute_info(struct snd_kcontrol * kc,struct snd_ctl_elem_info * uinfo)73167b1020dSTakashi Iwai snd_harmony_captureroute_info(struct snd_kcontrol *kc,
73267b1020dSTakashi Iwai struct snd_ctl_elem_info *uinfo)
7333a165680SStuart Brady {
7343c6a73ccSTakashi Iwai static const char * const texts[2] = { "Line", "Mic" };
7353c6a73ccSTakashi Iwai
7363c6a73ccSTakashi Iwai return snd_ctl_enum_info(uinfo, 1, 2, texts);
7373a165680SStuart Brady }
7383a165680SStuart Brady
7393a165680SStuart Brady static int
snd_harmony_captureroute_get(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)74067b1020dSTakashi Iwai snd_harmony_captureroute_get(struct snd_kcontrol *kc,
74167b1020dSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
7423a165680SStuart Brady {
74367b1020dSTakashi Iwai struct snd_harmony *h = snd_kcontrol_chip(kc);
7443a165680SStuart Brady int value;
7453a165680SStuart Brady
74603f9ae25STakashi Iwai spin_lock_irq(&h->mixer_lock);
7473a165680SStuart Brady
7483a165680SStuart Brady value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1;
7493a165680SStuart Brady ucontrol->value.enumerated.item[0] = value;
7503a165680SStuart Brady
75103f9ae25STakashi Iwai spin_unlock_irq(&h->mixer_lock);
7523a165680SStuart Brady
7533a165680SStuart Brady return 0;
7543a165680SStuart Brady }
7553a165680SStuart Brady
7563a165680SStuart Brady static int
snd_harmony_captureroute_put(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)75767b1020dSTakashi Iwai snd_harmony_captureroute_put(struct snd_kcontrol *kc,
75867b1020dSTakashi Iwai struct snd_ctl_elem_value *ucontrol)
7593a165680SStuart Brady {
76067b1020dSTakashi Iwai struct snd_harmony *h = snd_kcontrol_chip(kc);
7613a165680SStuart Brady int value;
7623a165680SStuart Brady int old_gain = h->st.gain;
7633a165680SStuart Brady
76403f9ae25STakashi Iwai spin_lock_irq(&h->mixer_lock);
7653a165680SStuart Brady
7663a165680SStuart Brady value = ucontrol->value.enumerated.item[0] & 1;
7673a165680SStuart Brady h->st.gain &= ~HARMONY_GAIN_IS_MASK;
7683a165680SStuart Brady h->st.gain |= value << HARMONY_GAIN_IS_SHIFT;
7693a165680SStuart Brady
7703a165680SStuart Brady snd_harmony_set_new_gain(h);
7713a165680SStuart Brady
77203f9ae25STakashi Iwai spin_unlock_irq(&h->mixer_lock);
7733a165680SStuart Brady
7743a165680SStuart Brady return h->st.gain != old_gain;
7751da177e4SLinus Torvalds }
7761da177e4SLinus Torvalds
77767b1020dSTakashi Iwai #define HARMONY_CONTROLS ARRAY_SIZE(snd_harmony_controls)
7781da177e4SLinus Torvalds
7791da177e4SLinus Torvalds #define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \
7801da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
7811da177e4SLinus Torvalds .info = snd_harmony_mixercontrol_info, \
7821da177e4SLinus Torvalds .get = snd_harmony_volume_get, .put = snd_harmony_volume_put, \
7831da177e4SLinus Torvalds .private_value = ((left_shift) | ((right_shift) << 8) | \
7841da177e4SLinus Torvalds ((mask) << 16) | ((invert) << 24)) }
7851da177e4SLinus Torvalds
78646708e62STakashi Iwai static const struct snd_kcontrol_new snd_harmony_controls[] = {
7873a165680SStuart Brady HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT,
7881da177e4SLinus Torvalds HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1),
7891da177e4SLinus Torvalds HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT,
7901da177e4SLinus Torvalds HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0),
7913a165680SStuart Brady HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT,
7923a165680SStuart Brady HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1),
7933a165680SStuart Brady {
7943a165680SStuart Brady .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7953a165680SStuart Brady .name = "Input Route",
7963a165680SStuart Brady .info = snd_harmony_captureroute_info,
7973a165680SStuart Brady .get = snd_harmony_captureroute_get,
7983a165680SStuart Brady .put = snd_harmony_captureroute_put
7993a165680SStuart Brady },
8003a165680SStuart Brady HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT,
8013a165680SStuart Brady HARMONY_GAIN_SE_SHIFT, 1, 0),
8023a165680SStuart Brady HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT,
8033a165680SStuart Brady HARMONY_GAIN_LE_SHIFT, 1, 0),
8043a165680SStuart Brady HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT,
8053a165680SStuart Brady HARMONY_GAIN_HE_SHIFT, 1, 0),
8061da177e4SLinus Torvalds };
8071da177e4SLinus Torvalds
80805bcf503SBill Pemberton static void
snd_harmony_mixer_reset(struct snd_harmony * h)80967b1020dSTakashi Iwai snd_harmony_mixer_reset(struct snd_harmony *h)
8101da177e4SLinus Torvalds {
8111da177e4SLinus Torvalds harmony_mute(h);
8121da177e4SLinus Torvalds harmony_reset(h);
8131da177e4SLinus Torvalds h->st.gain = HARMONY_GAIN_DEFAULT;
8141da177e4SLinus Torvalds harmony_unmute(h);
8151da177e4SLinus Torvalds }
8161da177e4SLinus Torvalds
81705bcf503SBill Pemberton static int
snd_harmony_mixer_init(struct snd_harmony * h)81867b1020dSTakashi Iwai snd_harmony_mixer_init(struct snd_harmony *h)
8191da177e4SLinus Torvalds {
820e8e0929dSJulia Lawall struct snd_card *card;
8211da177e4SLinus Torvalds int idx, err;
8221da177e4SLinus Torvalds
8235e246b85STakashi Iwai if (snd_BUG_ON(!h))
824c86a456bSTakashi Iwai return -EINVAL;
825e8e0929dSJulia Lawall card = h->card;
8261da177e4SLinus Torvalds strcpy(card->mixername, "Harmony Gain control interface");
8271da177e4SLinus Torvalds
8281da177e4SLinus Torvalds for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
8291da177e4SLinus Torvalds err = snd_ctl_add(card,
8301da177e4SLinus Torvalds snd_ctl_new1(&snd_harmony_controls[idx], h));
8311da177e4SLinus Torvalds if (err < 0)
8321da177e4SLinus Torvalds return err;
8331da177e4SLinus Torvalds }
8341da177e4SLinus Torvalds
8351da177e4SLinus Torvalds snd_harmony_mixer_reset(h);
8361da177e4SLinus Torvalds
8371da177e4SLinus Torvalds return 0;
8381da177e4SLinus Torvalds }
8391da177e4SLinus Torvalds
8401da177e4SLinus Torvalds static int
snd_harmony_free(struct snd_harmony * h)84167b1020dSTakashi Iwai snd_harmony_free(struct snd_harmony *h)
8421da177e4SLinus Torvalds {
8431da177e4SLinus Torvalds if (h->gdma.addr)
8441da177e4SLinus Torvalds snd_dma_free_pages(&h->gdma);
8451da177e4SLinus Torvalds if (h->sdma.addr)
8461da177e4SLinus Torvalds snd_dma_free_pages(&h->sdma);
8471da177e4SLinus Torvalds
8481da177e4SLinus Torvalds if (h->irq >= 0)
8491da177e4SLinus Torvalds free_irq(h->irq, h);
8501da177e4SLinus Torvalds
8511da177e4SLinus Torvalds iounmap(h->iobase);
8521da177e4SLinus Torvalds kfree(h);
8531da177e4SLinus Torvalds return 0;
8541da177e4SLinus Torvalds }
8551da177e4SLinus Torvalds
8561da177e4SLinus Torvalds static int
snd_harmony_dev_free(struct snd_device * dev)85767b1020dSTakashi Iwai snd_harmony_dev_free(struct snd_device *dev)
8581da177e4SLinus Torvalds {
85967b1020dSTakashi Iwai struct snd_harmony *h = dev->device_data;
8601da177e4SLinus Torvalds return snd_harmony_free(h);
8611da177e4SLinus Torvalds }
8621da177e4SLinus Torvalds
86305bcf503SBill Pemberton static int
snd_harmony_create(struct snd_card * card,struct parisc_device * padev,struct snd_harmony ** rchip)86467b1020dSTakashi Iwai snd_harmony_create(struct snd_card *card,
8651da177e4SLinus Torvalds struct parisc_device *padev,
86667b1020dSTakashi Iwai struct snd_harmony **rchip)
8671da177e4SLinus Torvalds {
8681da177e4SLinus Torvalds int err;
86967b1020dSTakashi Iwai struct snd_harmony *h;
87065341589STakashi Iwai static const struct snd_device_ops ops = {
8711da177e4SLinus Torvalds .dev_free = snd_harmony_dev_free,
8721da177e4SLinus Torvalds };
8731da177e4SLinus Torvalds
8741da177e4SLinus Torvalds *rchip = NULL;
8751da177e4SLinus Torvalds
87603f9ae25STakashi Iwai h = kzalloc(sizeof(*h), GFP_KERNEL);
8771da177e4SLinus Torvalds if (h == NULL)
8781da177e4SLinus Torvalds return -ENOMEM;
8791da177e4SLinus Torvalds
88053f01bbaSMatthew Wilcox h->hpa = padev->hpa.start;
8811da177e4SLinus Torvalds h->card = card;
8821da177e4SLinus Torvalds h->dev = padev;
88303f9ae25STakashi Iwai h->irq = -1;
8844bdc0d67SChristoph Hellwig h->iobase = ioremap(padev->hpa.start, HARMONY_SIZE);
8851da177e4SLinus Torvalds if (h->iobase == NULL) {
8861da177e4SLinus Torvalds printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
887b04b4f78SAlexander Beregalov (unsigned long)padev->hpa.start);
8881da177e4SLinus Torvalds err = -EBUSY;
8891da177e4SLinus Torvalds goto free_and_ret;
8901da177e4SLinus Torvalds }
8911da177e4SLinus Torvalds
89203f9ae25STakashi Iwai err = request_irq(padev->irq, snd_harmony_interrupt, 0,
8931da177e4SLinus Torvalds "harmony", h);
8941da177e4SLinus Torvalds if (err) {
8951da177e4SLinus Torvalds printk(KERN_ERR PFX "could not obtain interrupt %d",
89603f9ae25STakashi Iwai padev->irq);
8971da177e4SLinus Torvalds goto free_and_ret;
8981da177e4SLinus Torvalds }
89903f9ae25STakashi Iwai h->irq = padev->irq;
9001da177e4SLinus Torvalds
9011da177e4SLinus Torvalds spin_lock_init(&h->mixer_lock);
9021da177e4SLinus Torvalds spin_lock_init(&h->lock);
9031da177e4SLinus Torvalds
9046ea9a2b8STakashi Iwai err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops);
9056ea9a2b8STakashi Iwai if (err < 0)
9061da177e4SLinus Torvalds goto free_and_ret;
9071da177e4SLinus Torvalds
9081da177e4SLinus Torvalds *rchip = h;
9091da177e4SLinus Torvalds
9101da177e4SLinus Torvalds return 0;
9111da177e4SLinus Torvalds
9121da177e4SLinus Torvalds free_and_ret:
9131da177e4SLinus Torvalds snd_harmony_free(h);
9141da177e4SLinus Torvalds return err;
9151da177e4SLinus Torvalds }
9161da177e4SLinus Torvalds
917c8dbaa22SHelge Deller static int __init
snd_harmony_probe(struct parisc_device * padev)9181da177e4SLinus Torvalds snd_harmony_probe(struct parisc_device *padev)
9191da177e4SLinus Torvalds {
9201da177e4SLinus Torvalds int err;
92167b1020dSTakashi Iwai struct snd_card *card;
92267b1020dSTakashi Iwai struct snd_harmony *h;
9231da177e4SLinus Torvalds
9245f32c314STakashi Iwai err = snd_card_new(&padev->dev, index, id, THIS_MODULE, 0, &card);
925bd7dd77cSTakashi Iwai if (err < 0)
926bd7dd77cSTakashi Iwai return err;
9271da177e4SLinus Torvalds
9281da177e4SLinus Torvalds err = snd_harmony_create(card, padev, &h);
92903f9ae25STakashi Iwai if (err < 0)
9301da177e4SLinus Torvalds goto free_and_ret;
9311da177e4SLinus Torvalds
9321da177e4SLinus Torvalds err = snd_harmony_pcm_init(h);
93303f9ae25STakashi Iwai if (err < 0)
9341da177e4SLinus Torvalds goto free_and_ret;
9351da177e4SLinus Torvalds
9361da177e4SLinus Torvalds err = snd_harmony_mixer_init(h);
93703f9ae25STakashi Iwai if (err < 0)
9381da177e4SLinus Torvalds goto free_and_ret;
9391da177e4SLinus Torvalds
9401da177e4SLinus Torvalds strcpy(card->driver, "harmony");
9411da177e4SLinus Torvalds strcpy(card->shortname, "Harmony");
9421da177e4SLinus Torvalds sprintf(card->longname, "%s at 0x%lx, irq %i",
9431da177e4SLinus Torvalds card->shortname, h->hpa, h->irq);
9441da177e4SLinus Torvalds
9451da177e4SLinus Torvalds err = snd_card_register(card);
94603f9ae25STakashi Iwai if (err < 0)
9471da177e4SLinus Torvalds goto free_and_ret;
9481da177e4SLinus Torvalds
94903f9ae25STakashi Iwai parisc_set_drvdata(padev, card);
9501da177e4SLinus Torvalds return 0;
9511da177e4SLinus Torvalds
9521da177e4SLinus Torvalds free_and_ret:
9531da177e4SLinus Torvalds snd_card_free(card);
9541da177e4SLinus Torvalds return err;
9551da177e4SLinus Torvalds }
9561da177e4SLinus Torvalds
957*87875c10SUwe Kleine-König static void __exit
snd_harmony_remove(struct parisc_device * padev)9581da177e4SLinus Torvalds snd_harmony_remove(struct parisc_device *padev)
9591da177e4SLinus Torvalds {
96003f9ae25STakashi Iwai snd_card_free(parisc_get_drvdata(padev));
9611da177e4SLinus Torvalds }
9621da177e4SLinus Torvalds
963c8dbaa22SHelge Deller static struct parisc_driver snd_harmony_driver __refdata = {
9641da177e4SLinus Torvalds .name = "harmony",
9651da177e4SLinus Torvalds .id_table = snd_harmony_devtable,
9661da177e4SLinus Torvalds .probe = snd_harmony_probe,
967c8dbaa22SHelge Deller .remove = __exit_p(snd_harmony_remove),
9681da177e4SLinus Torvalds };
9691da177e4SLinus Torvalds
9701da177e4SLinus Torvalds static int __init
alsa_harmony_init(void)9711da177e4SLinus Torvalds alsa_harmony_init(void)
9721da177e4SLinus Torvalds {
97303f9ae25STakashi Iwai return register_parisc_driver(&snd_harmony_driver);
9741da177e4SLinus Torvalds }
9751da177e4SLinus Torvalds
9761da177e4SLinus Torvalds static void __exit
alsa_harmony_fini(void)9771da177e4SLinus Torvalds alsa_harmony_fini(void)
9781da177e4SLinus Torvalds {
97967b1020dSTakashi Iwai unregister_parisc_driver(&snd_harmony_driver);
9801da177e4SLinus Torvalds }
9811da177e4SLinus Torvalds
9821da177e4SLinus Torvalds MODULE_LICENSE("GPL");
9831da177e4SLinus Torvalds MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
9841da177e4SLinus Torvalds MODULE_DESCRIPTION("Harmony sound driver");
9851da177e4SLinus Torvalds
9861da177e4SLinus Torvalds module_init(alsa_harmony_init);
9871da177e4SLinus Torvalds module_exit(alsa_harmony_fini);
988