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/driver.h> 491da177e4SLinus Torvalds #include <sound/core.h> 501da177e4SLinus Torvalds #include <sound/pcm.h> 511da177e4SLinus Torvalds #include <sound/control.h> 521da177e4SLinus Torvalds #include <sound/rawmidi.h> 531da177e4SLinus Torvalds #include <sound/initval.h> 541da177e4SLinus Torvalds #include <sound/info.h> 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds #include <asm/io.h> 571da177e4SLinus Torvalds #include <asm/hardware.h> 581da177e4SLinus Torvalds #include <asm/parisc-device.h> 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds #include "harmony.h" 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds static struct parisc_device_id snd_harmony_devtable[] = { 631da177e4SLinus Torvalds /* bushmaster / flounder */ 641da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, 651da177e4SLinus Torvalds /* 712 / 715 */ 661da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, 671da177e4SLinus Torvalds /* pace */ 681da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, 691da177e4SLinus Torvalds /* outfield / coral II */ 701da177e4SLinus Torvalds { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, 711da177e4SLinus Torvalds { 0, } 721da177e4SLinus Torvalds }; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable); 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds #define NAME "harmony" 771da177e4SLinus Torvalds #define PFX NAME ": " 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static unsigned int snd_harmony_rates[] = { 801da177e4SLinus Torvalds 5512, 6615, 8000, 9600, 811da177e4SLinus Torvalds 11025, 16000, 18900, 22050, 821da177e4SLinus Torvalds 27428, 32000, 33075, 37800, 831da177e4SLinus Torvalds 44100, 48000 841da177e4SLinus Torvalds }; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static unsigned int rate_bits[14] = { 871da177e4SLinus Torvalds HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ, 881da177e4SLinus Torvalds HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ, 891da177e4SLinus Torvalds HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ, 901da177e4SLinus Torvalds HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ, 911da177e4SLinus Torvalds HARMONY_SR_44KHZ, HARMONY_SR_48KHZ 921da177e4SLinus Torvalds }; 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds static snd_pcm_hw_constraint_list_t hw_constraint_rates = { 951da177e4SLinus Torvalds .count = ARRAY_SIZE(snd_harmony_rates), 961da177e4SLinus Torvalds .list = snd_harmony_rates, 971da177e4SLinus Torvalds .mask = 0, 981da177e4SLinus Torvalds }; 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds inline unsigned long 1011da177e4SLinus Torvalds harmony_read(harmony_t *h, unsigned r) 1021da177e4SLinus Torvalds { 1031da177e4SLinus Torvalds return __raw_readl(h->iobase + r); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds inline void 1071da177e4SLinus Torvalds harmony_write(harmony_t *h, unsigned r, unsigned long v) 1081da177e4SLinus Torvalds { 1091da177e4SLinus Torvalds __raw_writel(v, h->iobase + r); 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds static void 1131da177e4SLinus Torvalds harmony_wait_for_control(harmony_t *h) 1141da177e4SLinus Torvalds { 1151da177e4SLinus Torvalds while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ; 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds inline void 1191da177e4SLinus Torvalds harmony_reset(harmony_t *h) 1201da177e4SLinus Torvalds { 1211da177e4SLinus Torvalds harmony_write(h, HARMONY_RESET, 1); 1221da177e4SLinus Torvalds mdelay(50); 1231da177e4SLinus Torvalds harmony_write(h, HARMONY_RESET, 0); 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds static void 1271da177e4SLinus Torvalds harmony_disable_interrupts(harmony_t *h) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds u32 dstatus; 1301da177e4SLinus Torvalds harmony_wait_for_control(h); 1311da177e4SLinus Torvalds dstatus = harmony_read(h, HARMONY_DSTATUS); 1321da177e4SLinus Torvalds dstatus &= ~HARMONY_DSTATUS_IE; 1331da177e4SLinus Torvalds harmony_write(h, HARMONY_DSTATUS, dstatus); 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds static void 1371da177e4SLinus Torvalds harmony_enable_interrupts(harmony_t *h) 1381da177e4SLinus Torvalds { 1391da177e4SLinus Torvalds u32 dstatus; 1401da177e4SLinus Torvalds harmony_wait_for_control(h); 1411da177e4SLinus Torvalds dstatus = harmony_read(h, HARMONY_DSTATUS); 1421da177e4SLinus Torvalds dstatus |= HARMONY_DSTATUS_IE; 1431da177e4SLinus Torvalds harmony_write(h, HARMONY_DSTATUS, dstatus); 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static void 1471da177e4SLinus Torvalds harmony_mute(harmony_t *h) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds unsigned long flags; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds spin_lock_irqsave(&h->mixer_lock, flags); 1521da177e4SLinus Torvalds harmony_wait_for_control(h); 1531da177e4SLinus Torvalds harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE); 1541da177e4SLinus Torvalds spin_unlock_irqrestore(&h->mixer_lock, flags); 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds static void 1581da177e4SLinus Torvalds harmony_unmute(harmony_t *h) 1591da177e4SLinus Torvalds { 1601da177e4SLinus Torvalds unsigned long flags; 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds spin_lock_irqsave(&h->mixer_lock, flags); 1631da177e4SLinus Torvalds harmony_wait_for_control(h); 1641da177e4SLinus Torvalds harmony_write(h, HARMONY_GAINCTL, h->st.gain); 1651da177e4SLinus Torvalds spin_unlock_irqrestore(&h->mixer_lock, flags); 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds static void 1691da177e4SLinus Torvalds harmony_set_control(harmony_t *h) 1701da177e4SLinus Torvalds { 1711da177e4SLinus Torvalds u32 ctrl; 1721da177e4SLinus Torvalds unsigned long flags; 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds spin_lock_irqsave(&h->lock, flags); 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds ctrl = (HARMONY_CNTL_C | 1771da177e4SLinus Torvalds (h->st.format << 6) | 1781da177e4SLinus Torvalds (h->st.stereo << 5) | 1791da177e4SLinus Torvalds (h->st.rate)); 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds harmony_wait_for_control(h); 1821da177e4SLinus Torvalds harmony_write(h, HARMONY_CNTL, ctrl); 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds spin_unlock_irqrestore(&h->lock, flags); 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static irqreturn_t 1881da177e4SLinus Torvalds snd_harmony_interrupt(int irq, void *dev, struct pt_regs *regs) 1891da177e4SLinus Torvalds { 1901da177e4SLinus Torvalds u32 dstatus; 1911da177e4SLinus Torvalds harmony_t *h = dev; 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds spin_lock(&h->lock); 1941da177e4SLinus Torvalds harmony_disable_interrupts(h); 1951da177e4SLinus Torvalds harmony_wait_for_control(h); 1961da177e4SLinus Torvalds dstatus = harmony_read(h, HARMONY_DSTATUS); 1971da177e4SLinus Torvalds spin_unlock(&h->lock); 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds if (dstatus & HARMONY_DSTATUS_PN) { 200*3a165680SStuart Brady if (h->psubs && h->st.playing) { 2011da177e4SLinus Torvalds spin_lock(&h->lock); 2021da177e4SLinus Torvalds h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */ 2031da177e4SLinus Torvalds h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */ 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD, 2061da177e4SLinus Torvalds h->pbuf.addr + h->pbuf.buf); 2071da177e4SLinus Torvalds h->stats.play_intr++; 2081da177e4SLinus Torvalds spin_unlock(&h->lock); 2091da177e4SLinus Torvalds snd_pcm_period_elapsed(h->psubs); 2101da177e4SLinus Torvalds } else { 2111da177e4SLinus Torvalds spin_lock(&h->lock); 2121da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD, h->sdma.addr); 2131da177e4SLinus Torvalds h->stats.silence_intr++; 2141da177e4SLinus Torvalds spin_unlock(&h->lock); 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds if (dstatus & HARMONY_DSTATUS_RN) { 219*3a165680SStuart Brady if (h->csubs && h->st.capturing) { 2201da177e4SLinus Torvalds spin_lock(&h->lock); 2211da177e4SLinus Torvalds h->cbuf.buf += h->cbuf.count; 2221da177e4SLinus Torvalds h->cbuf.buf %= h->cbuf.size; 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD, 2251da177e4SLinus Torvalds h->cbuf.addr + h->cbuf.buf); 2261da177e4SLinus Torvalds h->stats.rec_intr++; 2271da177e4SLinus Torvalds spin_unlock(&h->lock); 2281da177e4SLinus Torvalds snd_pcm_period_elapsed(h->csubs); 2291da177e4SLinus Torvalds } else { 2301da177e4SLinus Torvalds spin_lock(&h->lock); 2311da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD, h->gdma.addr); 2321da177e4SLinus Torvalds h->stats.graveyard_intr++; 2331da177e4SLinus Torvalds spin_unlock(&h->lock); 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds spin_lock(&h->lock); 2381da177e4SLinus Torvalds harmony_enable_interrupts(h); 2391da177e4SLinus Torvalds spin_unlock(&h->lock); 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds return IRQ_HANDLED; 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds static unsigned int 2451da177e4SLinus Torvalds snd_harmony_rate_bits(int rate) 2461da177e4SLinus Torvalds { 2471da177e4SLinus Torvalds unsigned int i; 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(snd_harmony_rates); i++) 2501da177e4SLinus Torvalds if (snd_harmony_rates[i] == rate) 2511da177e4SLinus Torvalds return rate_bits[i]; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds return HARMONY_SR_44KHZ; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds static snd_pcm_hardware_t snd_harmony_playback = 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 2591da177e4SLinus Torvalds SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID | 2601da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER), 2611da177e4SLinus Torvalds .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW | 2621da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_A_LAW), 2631da177e4SLinus Torvalds .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 | 2641da177e4SLinus Torvalds SNDRV_PCM_RATE_KNOT), 2651da177e4SLinus Torvalds .rate_min = 5512, 2661da177e4SLinus Torvalds .rate_max = 48000, 2671da177e4SLinus Torvalds .channels_min = 1, 2681da177e4SLinus Torvalds .channels_max = 2, 2691da177e4SLinus Torvalds .buffer_bytes_max = MAX_BUF_SIZE, 2701da177e4SLinus Torvalds .period_bytes_min = BUF_SIZE, 2711da177e4SLinus Torvalds .period_bytes_max = BUF_SIZE, 2721da177e4SLinus Torvalds .periods_min = 1, 2731da177e4SLinus Torvalds .periods_max = MAX_BUFS, 2741da177e4SLinus Torvalds .fifo_size = 0, 2751da177e4SLinus Torvalds }; 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds static snd_pcm_hardware_t snd_harmony_capture = 2781da177e4SLinus Torvalds { 2791da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 2801da177e4SLinus Torvalds SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID | 2811da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER), 2821da177e4SLinus Torvalds .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW | 2831da177e4SLinus Torvalds SNDRV_PCM_FMTBIT_A_LAW), 2841da177e4SLinus Torvalds .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 | 2851da177e4SLinus Torvalds SNDRV_PCM_RATE_KNOT), 2861da177e4SLinus Torvalds .rate_min = 5512, 2871da177e4SLinus Torvalds .rate_max = 48000, 2881da177e4SLinus Torvalds .channels_min = 1, 2891da177e4SLinus Torvalds .channels_max = 2, 2901da177e4SLinus Torvalds .buffer_bytes_max = MAX_BUF_SIZE, 2911da177e4SLinus Torvalds .period_bytes_min = BUF_SIZE, 2921da177e4SLinus Torvalds .period_bytes_max = BUF_SIZE, 2931da177e4SLinus Torvalds .periods_min = 1, 2941da177e4SLinus Torvalds .periods_max = MAX_BUFS, 2951da177e4SLinus Torvalds .fifo_size = 0, 2961da177e4SLinus Torvalds }; 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds static int 2991da177e4SLinus Torvalds snd_harmony_playback_trigger(snd_pcm_substream_t *ss, int cmd) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 3021da177e4SLinus Torvalds unsigned long flags; 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds if (h->st.capturing) 3051da177e4SLinus Torvalds return -EBUSY; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds spin_lock_irqsave(&h->lock, flags); 3081da177e4SLinus Torvalds switch (cmd) { 3091da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START: 3101da177e4SLinus Torvalds h->st.playing = 1; 3111da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr); 3121da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD, h->gdma.addr); 3131da177e4SLinus Torvalds harmony_unmute(h); 3141da177e4SLinus Torvalds harmony_enable_interrupts(h); 3151da177e4SLinus Torvalds break; 3161da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP: 3171da177e4SLinus Torvalds h->st.playing = 0; 3181da177e4SLinus Torvalds harmony_mute(h); 319*3a165680SStuart Brady harmony_write(h, HARMONY_PNXTADD, h->sdma.addr); 3201da177e4SLinus Torvalds harmony_disable_interrupts(h); 3211da177e4SLinus Torvalds break; 3221da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3231da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3241da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND: 3251da177e4SLinus Torvalds default: 3261da177e4SLinus Torvalds spin_unlock_irqrestore(&h->lock, flags); 3271da177e4SLinus Torvalds snd_BUG(); 3281da177e4SLinus Torvalds return -EINVAL; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds spin_unlock_irqrestore(&h->lock, flags); 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds return 0; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds static int 3361da177e4SLinus Torvalds snd_harmony_capture_trigger(snd_pcm_substream_t *ss, int cmd) 3371da177e4SLinus Torvalds { 3381da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 3391da177e4SLinus Torvalds unsigned long flags; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds if (h->st.playing) 3421da177e4SLinus Torvalds return -EBUSY; 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds spin_lock_irqsave(&h->lock, flags); 3451da177e4SLinus Torvalds switch (cmd) { 3461da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START: 3471da177e4SLinus Torvalds h->st.capturing = 1; 3481da177e4SLinus Torvalds harmony_write(h, HARMONY_PNXTADD, h->sdma.addr); 3491da177e4SLinus Torvalds harmony_write(h, HARMONY_RNXTADD, h->cbuf.addr); 3501da177e4SLinus Torvalds harmony_unmute(h); 3511da177e4SLinus Torvalds harmony_enable_interrupts(h); 3521da177e4SLinus Torvalds break; 3531da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP: 3541da177e4SLinus Torvalds h->st.capturing = 0; 3551da177e4SLinus Torvalds harmony_mute(h); 356*3a165680SStuart Brady harmony_write(h, HARMONY_RNXTADD, h->gdma.addr); 3571da177e4SLinus Torvalds harmony_disable_interrupts(h); 3581da177e4SLinus Torvalds break; 3591da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3601da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3611da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND: 3621da177e4SLinus Torvalds default: 3631da177e4SLinus Torvalds spin_unlock_irqrestore(&h->lock, flags); 3641da177e4SLinus Torvalds snd_BUG(); 3651da177e4SLinus Torvalds return -EINVAL; 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds spin_unlock_irqrestore(&h->lock, flags); 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds return 0; 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds static int 3731da177e4SLinus Torvalds snd_harmony_set_data_format(harmony_t *h, int fmt, int force) 3741da177e4SLinus Torvalds { 3751da177e4SLinus Torvalds int o = h->st.format; 3761da177e4SLinus Torvalds int n; 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds switch(fmt) { 3791da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_S16_BE: 3801da177e4SLinus Torvalds n = HARMONY_DF_16BIT_LINEAR; 3811da177e4SLinus Torvalds break; 3821da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_A_LAW: 3831da177e4SLinus Torvalds n = HARMONY_DF_8BIT_ALAW; 3841da177e4SLinus Torvalds break; 3851da177e4SLinus Torvalds case SNDRV_PCM_FORMAT_MU_LAW: 3861da177e4SLinus Torvalds n = HARMONY_DF_8BIT_ULAW; 3871da177e4SLinus Torvalds break; 3881da177e4SLinus Torvalds default: 3891da177e4SLinus Torvalds n = HARMONY_DF_16BIT_LINEAR; 3901da177e4SLinus Torvalds break; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds if (force || o != n) { 3941da177e4SLinus Torvalds snd_pcm_format_set_silence(fmt, h->sdma.area, SILENCE_BUFSZ / 3951da177e4SLinus Torvalds (snd_pcm_format_physical_width(fmt) 3961da177e4SLinus Torvalds / 8)); 3971da177e4SLinus Torvalds } 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds return n; 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds static int 4031da177e4SLinus Torvalds snd_harmony_playback_prepare(snd_pcm_substream_t *ss) 4041da177e4SLinus Torvalds { 4051da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 4061da177e4SLinus Torvalds snd_pcm_runtime_t *rt = ss->runtime; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds if (h->st.capturing) 4091da177e4SLinus Torvalds return -EBUSY; 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds h->pbuf.size = snd_pcm_lib_buffer_bytes(ss); 4121da177e4SLinus Torvalds h->pbuf.count = snd_pcm_lib_period_bytes(ss); 413*3a165680SStuart Brady if (h->pbuf.buf >= h->pbuf.size) 4141da177e4SLinus Torvalds h->pbuf.buf = 0; 4151da177e4SLinus Torvalds h->st.playing = 0; 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds h->st.rate = snd_harmony_rate_bits(rt->rate); 4181da177e4SLinus Torvalds h->st.format = snd_harmony_set_data_format(h, rt->format, 0); 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds if (rt->channels == 2) 4211da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_STEREO; 4221da177e4SLinus Torvalds else 4231da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_MONO; 4241da177e4SLinus Torvalds 4251da177e4SLinus Torvalds harmony_set_control(h); 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds h->pbuf.addr = rt->dma_addr; 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds return 0; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds static int 4331da177e4SLinus Torvalds snd_harmony_capture_prepare(snd_pcm_substream_t *ss) 4341da177e4SLinus Torvalds { 4351da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 4361da177e4SLinus Torvalds snd_pcm_runtime_t *rt = ss->runtime; 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds if (h->st.playing) 4391da177e4SLinus Torvalds return -EBUSY; 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds h->cbuf.size = snd_pcm_lib_buffer_bytes(ss); 4421da177e4SLinus Torvalds h->cbuf.count = snd_pcm_lib_period_bytes(ss); 443*3a165680SStuart Brady if (h->cbuf.buf >= h->cbuf.size) 4441da177e4SLinus Torvalds h->cbuf.buf = 0; 4451da177e4SLinus Torvalds h->st.capturing = 0; 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds h->st.rate = snd_harmony_rate_bits(rt->rate); 4481da177e4SLinus Torvalds h->st.format = snd_harmony_set_data_format(h, rt->format, 0); 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds if (rt->channels == 2) 4511da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_STEREO; 4521da177e4SLinus Torvalds else 4531da177e4SLinus Torvalds h->st.stereo = HARMONY_SS_MONO; 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds harmony_set_control(h); 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds h->cbuf.addr = rt->dma_addr; 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds return 0; 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds 4621da177e4SLinus Torvalds static snd_pcm_uframes_t 4631da177e4SLinus Torvalds snd_harmony_playback_pointer(snd_pcm_substream_t *ss) 4641da177e4SLinus Torvalds { 4651da177e4SLinus Torvalds snd_pcm_runtime_t *rt = ss->runtime; 4661da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 4671da177e4SLinus Torvalds unsigned long pcuradd; 4681da177e4SLinus Torvalds unsigned long played; 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds if (!(h->st.playing) || (h->psubs == NULL)) 4711da177e4SLinus Torvalds return 0; 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds if ((h->pbuf.addr == 0) || (h->pbuf.size == 0)) 4741da177e4SLinus Torvalds return 0; 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds pcuradd = harmony_read(h, HARMONY_PCURADD); 4771da177e4SLinus Torvalds played = pcuradd - h->pbuf.addr; 4781da177e4SLinus Torvalds 4791da177e4SLinus Torvalds #ifdef HARMONY_DEBUG 4801da177e4SLinus Torvalds printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes\n", 4811da177e4SLinus Torvalds pcuradd, h->pbuf.addr, played); 4821da177e4SLinus Torvalds #endif 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds if (pcuradd > h->pbuf.addr + h->pbuf.size) { 4851da177e4SLinus Torvalds return 0; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds return bytes_to_frames(rt, played); 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds static snd_pcm_uframes_t 4921da177e4SLinus Torvalds snd_harmony_capture_pointer(snd_pcm_substream_t *ss) 4931da177e4SLinus Torvalds { 4941da177e4SLinus Torvalds snd_pcm_runtime_t *rt = ss->runtime; 4951da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 4961da177e4SLinus Torvalds unsigned long rcuradd; 4971da177e4SLinus Torvalds unsigned long caught; 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds if (!(h->st.capturing) || (h->csubs == NULL)) 5001da177e4SLinus Torvalds return 0; 5011da177e4SLinus Torvalds 5021da177e4SLinus Torvalds if ((h->cbuf.addr == 0) || (h->cbuf.size == 0)) 5031da177e4SLinus Torvalds return 0; 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds rcuradd = harmony_read(h, HARMONY_RCURADD); 5061da177e4SLinus Torvalds caught = rcuradd - h->cbuf.addr; 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds #ifdef HARMONY_DEBUG 5091da177e4SLinus Torvalds printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes\n", 5101da177e4SLinus Torvalds rcuradd, h->cbuf.addr, caught); 5111da177e4SLinus Torvalds #endif 5121da177e4SLinus Torvalds 5131da177e4SLinus Torvalds if (rcuradd > h->cbuf.addr + h->cbuf.size) { 5141da177e4SLinus Torvalds return 0; 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds return bytes_to_frames(rt, caught); 5181da177e4SLinus Torvalds } 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds static int 5211da177e4SLinus Torvalds snd_harmony_playback_open(snd_pcm_substream_t *ss) 5221da177e4SLinus Torvalds { 5231da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 5241da177e4SLinus Torvalds snd_pcm_runtime_t *rt = ss->runtime; 5251da177e4SLinus Torvalds int err; 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds h->psubs = ss; 5281da177e4SLinus Torvalds rt->hw = snd_harmony_playback; 5291da177e4SLinus Torvalds snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, 5301da177e4SLinus Torvalds &hw_constraint_rates); 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 5331da177e4SLinus Torvalds if (err < 0) 5341da177e4SLinus Torvalds return err; 5351da177e4SLinus Torvalds 5361da177e4SLinus Torvalds return 0; 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds static int 5401da177e4SLinus Torvalds snd_harmony_capture_open(snd_pcm_substream_t *ss) 5411da177e4SLinus Torvalds { 5421da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 5431da177e4SLinus Torvalds snd_pcm_runtime_t *rt = ss->runtime; 5441da177e4SLinus Torvalds int err; 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds h->csubs = ss; 5471da177e4SLinus Torvalds rt->hw = snd_harmony_capture; 5481da177e4SLinus Torvalds snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, 5491da177e4SLinus Torvalds &hw_constraint_rates); 5501da177e4SLinus Torvalds 5511da177e4SLinus Torvalds err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 5521da177e4SLinus Torvalds if (err < 0) 5531da177e4SLinus Torvalds return err; 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds return 0; 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds static int 5591da177e4SLinus Torvalds snd_harmony_playback_close(snd_pcm_substream_t *ss) 5601da177e4SLinus Torvalds { 5611da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 5621da177e4SLinus Torvalds h->psubs = NULL; 5631da177e4SLinus Torvalds return 0; 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds static int 5671da177e4SLinus Torvalds snd_harmony_capture_close(snd_pcm_substream_t *ss) 5681da177e4SLinus Torvalds { 5691da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 5701da177e4SLinus Torvalds h->csubs = NULL; 5711da177e4SLinus Torvalds return 0; 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds 5741da177e4SLinus Torvalds static int 5751da177e4SLinus Torvalds snd_harmony_hw_params(snd_pcm_substream_t *ss, 5761da177e4SLinus Torvalds snd_pcm_hw_params_t *hw) 5771da177e4SLinus Torvalds { 5781da177e4SLinus Torvalds int err; 5791da177e4SLinus Torvalds harmony_t *h = snd_pcm_substream_chip(ss); 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds err = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw)); 5821da177e4SLinus Torvalds if (err > 0 && h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS) 5831da177e4SLinus Torvalds ss->runtime->dma_addr = __pa(ss->runtime->dma_area); 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds return err; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds 5881da177e4SLinus Torvalds static int 5891da177e4SLinus Torvalds snd_harmony_hw_free(snd_pcm_substream_t *ss) 5901da177e4SLinus Torvalds { 5911da177e4SLinus Torvalds return snd_pcm_lib_free_pages(ss); 5921da177e4SLinus Torvalds } 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds static snd_pcm_ops_t snd_harmony_playback_ops = { 5951da177e4SLinus Torvalds .open = snd_harmony_playback_open, 5961da177e4SLinus Torvalds .close = snd_harmony_playback_close, 5971da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 5981da177e4SLinus Torvalds .hw_params = snd_harmony_hw_params, 5991da177e4SLinus Torvalds .hw_free = snd_harmony_hw_free, 6001da177e4SLinus Torvalds .prepare = snd_harmony_playback_prepare, 6011da177e4SLinus Torvalds .trigger = snd_harmony_playback_trigger, 6021da177e4SLinus Torvalds .pointer = snd_harmony_playback_pointer, 6031da177e4SLinus Torvalds }; 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds static snd_pcm_ops_t snd_harmony_capture_ops = { 6061da177e4SLinus Torvalds .open = snd_harmony_capture_open, 6071da177e4SLinus Torvalds .close = snd_harmony_capture_close, 6081da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 6091da177e4SLinus Torvalds .hw_params = snd_harmony_hw_params, 6101da177e4SLinus Torvalds .hw_free = snd_harmony_hw_free, 6111da177e4SLinus Torvalds .prepare = snd_harmony_capture_prepare, 6121da177e4SLinus Torvalds .trigger = snd_harmony_capture_trigger, 6131da177e4SLinus Torvalds .pointer = snd_harmony_capture_pointer, 6141da177e4SLinus Torvalds }; 6151da177e4SLinus Torvalds 6161da177e4SLinus Torvalds static int 6171da177e4SLinus Torvalds snd_harmony_pcm_init(harmony_t *h) 6181da177e4SLinus Torvalds { 6191da177e4SLinus Torvalds snd_pcm_t *pcm; 6201da177e4SLinus Torvalds int err; 6211da177e4SLinus Torvalds 6221da177e4SLinus Torvalds harmony_disable_interrupts(h); 6231da177e4SLinus Torvalds 6241da177e4SLinus Torvalds err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm); 6251da177e4SLinus Torvalds if (err < 0) 6261da177e4SLinus Torvalds return err; 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 6291da177e4SLinus Torvalds &snd_harmony_playback_ops); 6301da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 6311da177e4SLinus Torvalds &snd_harmony_capture_ops); 6321da177e4SLinus Torvalds 6331da177e4SLinus Torvalds pcm->private_data = h; 6341da177e4SLinus Torvalds pcm->info_flags = 0; 6351da177e4SLinus Torvalds strcpy(pcm->name, "harmony"); 6361da177e4SLinus Torvalds h->pcm = pcm; 6371da177e4SLinus Torvalds 6381da177e4SLinus Torvalds h->psubs = NULL; 6391da177e4SLinus Torvalds h->csubs = NULL; 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds /* initialize graveyard buffer */ 6421da177e4SLinus Torvalds h->dma.type = SNDRV_DMA_TYPE_DEV; 6431da177e4SLinus Torvalds h->dma.dev = &h->dev->dev; 6441da177e4SLinus Torvalds err = snd_dma_alloc_pages(h->dma.type, 6451da177e4SLinus Torvalds h->dma.dev, 6461da177e4SLinus Torvalds BUF_SIZE*GRAVEYARD_BUFS, 6471da177e4SLinus Torvalds &h->gdma); 6481da177e4SLinus Torvalds if (err < 0) { 6491da177e4SLinus Torvalds printk(KERN_ERR PFX "cannot allocate graveyard buffer!\n"); 6501da177e4SLinus Torvalds return err; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds /* initialize silence buffers */ 6541da177e4SLinus Torvalds err = snd_dma_alloc_pages(h->dma.type, 6551da177e4SLinus Torvalds h->dma.dev, 6561da177e4SLinus Torvalds BUF_SIZE*SILENCE_BUFS, 6571da177e4SLinus Torvalds &h->sdma); 6581da177e4SLinus Torvalds if (err < 0) { 6591da177e4SLinus Torvalds printk(KERN_ERR PFX "cannot allocate silence buffer!\n"); 6601da177e4SLinus Torvalds return err; 6611da177e4SLinus Torvalds } 6621da177e4SLinus Torvalds 6631da177e4SLinus Torvalds /* pre-allocate space for DMA */ 6641da177e4SLinus Torvalds err = snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type, 6651da177e4SLinus Torvalds h->dma.dev, 6661da177e4SLinus Torvalds MAX_BUF_SIZE, 6671da177e4SLinus Torvalds MAX_BUF_SIZE); 6681da177e4SLinus Torvalds if (err < 0) { 6691da177e4SLinus Torvalds printk(KERN_ERR PFX "buffer allocation error: %d\n", err); 6701da177e4SLinus Torvalds return err; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds h->st.format = snd_harmony_set_data_format(h, 6741da177e4SLinus Torvalds SNDRV_PCM_FORMAT_S16_BE, 1); 6751da177e4SLinus Torvalds 6761da177e4SLinus Torvalds return 0; 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds static void 6801da177e4SLinus Torvalds snd_harmony_set_new_gain(harmony_t *h) 6811da177e4SLinus Torvalds { 6821da177e4SLinus Torvalds harmony_wait_for_control(h); 6831da177e4SLinus Torvalds harmony_write(h, HARMONY_GAINCTL, h->st.gain); 6841da177e4SLinus Torvalds } 6851da177e4SLinus Torvalds 6861da177e4SLinus Torvalds static int 6871da177e4SLinus Torvalds snd_harmony_mixercontrol_info(snd_kcontrol_t *kc, 6881da177e4SLinus Torvalds snd_ctl_elem_info_t *uinfo) 6891da177e4SLinus Torvalds { 6901da177e4SLinus Torvalds int mask = (kc->private_value >> 16) & 0xff; 6911da177e4SLinus Torvalds int left_shift = (kc->private_value) & 0xff; 6921da177e4SLinus Torvalds int right_shift = (kc->private_value >> 8) & 0xff; 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : 6951da177e4SLinus Torvalds SNDRV_CTL_ELEM_TYPE_INTEGER; 6961da177e4SLinus Torvalds uinfo->count = left_shift == right_shift ? 1 : 2; 6971da177e4SLinus Torvalds uinfo->value.integer.min = 0; 6981da177e4SLinus Torvalds uinfo->value.integer.max = mask; 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds return 0; 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds static int 7041da177e4SLinus Torvalds snd_harmony_volume_get(snd_kcontrol_t *kc, 7051da177e4SLinus Torvalds snd_ctl_elem_value_t *ucontrol) 7061da177e4SLinus Torvalds { 7071da177e4SLinus Torvalds harmony_t *h = snd_kcontrol_chip(kc); 7081da177e4SLinus Torvalds int shift_left = (kc->private_value) & 0xff; 7091da177e4SLinus Torvalds int shift_right = (kc->private_value >> 8) & 0xff; 7101da177e4SLinus Torvalds int mask = (kc->private_value >> 16) & 0xff; 7111da177e4SLinus Torvalds int invert = (kc->private_value >> 24) & 0xff; 7121da177e4SLinus Torvalds int left, right; 7131da177e4SLinus Torvalds unsigned long flags; 7141da177e4SLinus Torvalds 7151da177e4SLinus Torvalds spin_lock_irqsave(&h->mixer_lock, flags); 7161da177e4SLinus Torvalds 7171da177e4SLinus Torvalds left = (h->st.gain >> shift_left) & mask; 7181da177e4SLinus Torvalds right = (h->st.gain >> shift_right) & mask; 7191da177e4SLinus Torvalds if (invert) { 7201da177e4SLinus Torvalds left = mask - left; 7211da177e4SLinus Torvalds right = mask - right; 7221da177e4SLinus Torvalds } 723*3a165680SStuart Brady 7241da177e4SLinus Torvalds ucontrol->value.integer.value[0] = left; 725*3a165680SStuart Brady if (shift_left != shift_right) 7261da177e4SLinus Torvalds ucontrol->value.integer.value[1] = right; 7271da177e4SLinus Torvalds 7281da177e4SLinus Torvalds spin_unlock_irqrestore(&h->mixer_lock, flags); 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds return 0; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds static int 7341da177e4SLinus Torvalds snd_harmony_volume_put(snd_kcontrol_t *kc, 7351da177e4SLinus Torvalds snd_ctl_elem_value_t *ucontrol) 7361da177e4SLinus Torvalds { 7371da177e4SLinus Torvalds harmony_t *h = snd_kcontrol_chip(kc); 7381da177e4SLinus Torvalds int shift_left = (kc->private_value) & 0xff; 7391da177e4SLinus Torvalds int shift_right = (kc->private_value >> 8) & 0xff; 7401da177e4SLinus Torvalds int mask = (kc->private_value >> 16) & 0xff; 7411da177e4SLinus Torvalds int invert = (kc->private_value >> 24) & 0xff; 7421da177e4SLinus Torvalds int left, right; 7431da177e4SLinus Torvalds int old_gain = h->st.gain; 7441da177e4SLinus Torvalds unsigned long flags; 7451da177e4SLinus Torvalds 7461da177e4SLinus Torvalds spin_lock_irqsave(&h->mixer_lock, flags); 7471da177e4SLinus Torvalds 748*3a165680SStuart Brady left = ucontrol->value.integer.value[0] & mask; 749*3a165680SStuart Brady if (invert) 750*3a165680SStuart Brady left = mask - left; 751*3a165680SStuart Brady h->st.gain &= ~( (mask << shift_left ) ); 752*3a165680SStuart Brady h->st.gain |= (left << shift_left); 753*3a165680SStuart Brady 754*3a165680SStuart Brady if (shift_left != shift_right) { 755*3a165680SStuart Brady right = ucontrol->value.integer.value[1] & mask; 756*3a165680SStuart Brady if (invert) 757*3a165680SStuart Brady right = mask - right; 758*3a165680SStuart Brady h->st.gain &= ~( (mask << shift_right) ); 759*3a165680SStuart Brady h->st.gain |= (right << shift_right); 760*3a165680SStuart Brady } 761*3a165680SStuart Brady 7621da177e4SLinus Torvalds snd_harmony_set_new_gain(h); 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds spin_unlock_irqrestore(&h->mixer_lock, flags); 7651da177e4SLinus Torvalds 766*3a165680SStuart Brady return h->st.gain != old_gain; 767*3a165680SStuart Brady } 768*3a165680SStuart Brady 769*3a165680SStuart Brady static int 770*3a165680SStuart Brady snd_harmony_captureroute_info(snd_kcontrol_t *kc, 771*3a165680SStuart Brady snd_ctl_elem_info_t *uinfo) 772*3a165680SStuart Brady { 773*3a165680SStuart Brady static char *texts[2] = { "Line", "Mic" }; 774*3a165680SStuart Brady uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 775*3a165680SStuart Brady uinfo->count = 1; 776*3a165680SStuart Brady uinfo->value.enumerated.items = 2; 777*3a165680SStuart Brady if (uinfo->value.enumerated.item > 1) 778*3a165680SStuart Brady uinfo->value.enumerated.item = 1; 779*3a165680SStuart Brady strcpy(uinfo->value.enumerated.name, 780*3a165680SStuart Brady texts[uinfo->value.enumerated.item]); 781*3a165680SStuart Brady return 0; 782*3a165680SStuart Brady } 783*3a165680SStuart Brady 784*3a165680SStuart Brady static int 785*3a165680SStuart Brady snd_harmony_captureroute_get(snd_kcontrol_t *kc, 786*3a165680SStuart Brady snd_ctl_elem_value_t *ucontrol) 787*3a165680SStuart Brady { 788*3a165680SStuart Brady harmony_t *h = snd_kcontrol_chip(kc); 789*3a165680SStuart Brady int value; 790*3a165680SStuart Brady unsigned long flags; 791*3a165680SStuart Brady 792*3a165680SStuart Brady spin_lock_irqsave(&h->mixer_lock, flags); 793*3a165680SStuart Brady 794*3a165680SStuart Brady value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1; 795*3a165680SStuart Brady ucontrol->value.enumerated.item[0] = value; 796*3a165680SStuart Brady 797*3a165680SStuart Brady spin_unlock_irqrestore(&h->mixer_lock, flags); 798*3a165680SStuart Brady 799*3a165680SStuart Brady return 0; 800*3a165680SStuart Brady } 801*3a165680SStuart Brady 802*3a165680SStuart Brady static int 803*3a165680SStuart Brady snd_harmony_captureroute_put(snd_kcontrol_t *kc, 804*3a165680SStuart Brady snd_ctl_elem_value_t *ucontrol) 805*3a165680SStuart Brady { 806*3a165680SStuart Brady harmony_t *h = snd_kcontrol_chip(kc); 807*3a165680SStuart Brady int value; 808*3a165680SStuart Brady int old_gain = h->st.gain; 809*3a165680SStuart Brady unsigned long flags; 810*3a165680SStuart Brady 811*3a165680SStuart Brady spin_lock_irqsave(&h->mixer_lock, flags); 812*3a165680SStuart Brady 813*3a165680SStuart Brady value = ucontrol->value.enumerated.item[0] & 1; 814*3a165680SStuart Brady h->st.gain &= ~HARMONY_GAIN_IS_MASK; 815*3a165680SStuart Brady h->st.gain |= value << HARMONY_GAIN_IS_SHIFT; 816*3a165680SStuart Brady 817*3a165680SStuart Brady snd_harmony_set_new_gain(h); 818*3a165680SStuart Brady 819*3a165680SStuart Brady spin_unlock_irqrestore(&h->mixer_lock, flags); 820*3a165680SStuart Brady 821*3a165680SStuart Brady return h->st.gain != old_gain; 8221da177e4SLinus Torvalds } 8231da177e4SLinus Torvalds 8241da177e4SLinus Torvalds #define HARMONY_CONTROLS (sizeof(snd_harmony_controls)/ \ 8251da177e4SLinus Torvalds sizeof(snd_kcontrol_new_t)) 8261da177e4SLinus Torvalds 8271da177e4SLinus Torvalds #define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \ 8281da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 8291da177e4SLinus Torvalds .info = snd_harmony_mixercontrol_info, \ 8301da177e4SLinus Torvalds .get = snd_harmony_volume_get, .put = snd_harmony_volume_put, \ 8311da177e4SLinus Torvalds .private_value = ((left_shift) | ((right_shift) << 8) | \ 8321da177e4SLinus Torvalds ((mask) << 16) | ((invert) << 24)) } 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds static snd_kcontrol_new_t snd_harmony_controls[] = { 835*3a165680SStuart Brady HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT, 8361da177e4SLinus Torvalds HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1), 8371da177e4SLinus Torvalds HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT, 8381da177e4SLinus Torvalds HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0), 839*3a165680SStuart Brady HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT, 840*3a165680SStuart Brady HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1), 841*3a165680SStuart Brady { 842*3a165680SStuart Brady .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 843*3a165680SStuart Brady .name = "Input Route", 844*3a165680SStuart Brady .info = snd_harmony_captureroute_info, 845*3a165680SStuart Brady .get = snd_harmony_captureroute_get, 846*3a165680SStuart Brady .put = snd_harmony_captureroute_put 847*3a165680SStuart Brady }, 848*3a165680SStuart Brady HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT, 849*3a165680SStuart Brady HARMONY_GAIN_SE_SHIFT, 1, 0), 850*3a165680SStuart Brady HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT, 851*3a165680SStuart Brady HARMONY_GAIN_LE_SHIFT, 1, 0), 852*3a165680SStuart Brady HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT, 853*3a165680SStuart Brady HARMONY_GAIN_HE_SHIFT, 1, 0), 8541da177e4SLinus Torvalds }; 8551da177e4SLinus Torvalds 8561da177e4SLinus Torvalds static void __init 8571da177e4SLinus Torvalds snd_harmony_mixer_reset(harmony_t *h) 8581da177e4SLinus Torvalds { 8591da177e4SLinus Torvalds harmony_mute(h); 8601da177e4SLinus Torvalds harmony_reset(h); 8611da177e4SLinus Torvalds h->st.gain = HARMONY_GAIN_DEFAULT; 8621da177e4SLinus Torvalds harmony_unmute(h); 8631da177e4SLinus Torvalds } 8641da177e4SLinus Torvalds 8651da177e4SLinus Torvalds static int __init 8661da177e4SLinus Torvalds snd_harmony_mixer_init(harmony_t *h) 8671da177e4SLinus Torvalds { 8681da177e4SLinus Torvalds snd_card_t *card = h->card; 8691da177e4SLinus Torvalds int idx, err; 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds snd_assert(h != NULL, return -EINVAL); 8721da177e4SLinus Torvalds strcpy(card->mixername, "Harmony Gain control interface"); 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds for (idx = 0; idx < HARMONY_CONTROLS; idx++) { 8751da177e4SLinus Torvalds err = snd_ctl_add(card, 8761da177e4SLinus Torvalds snd_ctl_new1(&snd_harmony_controls[idx], h)); 8771da177e4SLinus Torvalds if (err < 0) 8781da177e4SLinus Torvalds return err; 8791da177e4SLinus Torvalds } 8801da177e4SLinus Torvalds 8811da177e4SLinus Torvalds snd_harmony_mixer_reset(h); 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds return 0; 8841da177e4SLinus Torvalds } 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds static int 8871da177e4SLinus Torvalds snd_harmony_free(harmony_t *h) 8881da177e4SLinus Torvalds { 8891da177e4SLinus Torvalds if (h->gdma.addr) 8901da177e4SLinus Torvalds snd_dma_free_pages(&h->gdma); 8911da177e4SLinus Torvalds if (h->sdma.addr) 8921da177e4SLinus Torvalds snd_dma_free_pages(&h->sdma); 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds if (h->irq >= 0) 8951da177e4SLinus Torvalds free_irq(h->irq, h); 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds if (h->iobase) 8981da177e4SLinus Torvalds iounmap(h->iobase); 8991da177e4SLinus Torvalds 9001da177e4SLinus Torvalds parisc_set_drvdata(h->dev, NULL); 9011da177e4SLinus Torvalds 9021da177e4SLinus Torvalds kfree(h); 9031da177e4SLinus Torvalds return 0; 9041da177e4SLinus Torvalds } 9051da177e4SLinus Torvalds 9061da177e4SLinus Torvalds static int 9071da177e4SLinus Torvalds snd_harmony_dev_free(snd_device_t *dev) 9081da177e4SLinus Torvalds { 9091da177e4SLinus Torvalds harmony_t *h = dev->device_data; 9101da177e4SLinus Torvalds return snd_harmony_free(h); 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds static int __devinit 9141da177e4SLinus Torvalds snd_harmony_create(snd_card_t *card, 9151da177e4SLinus Torvalds struct parisc_device *padev, 9161da177e4SLinus Torvalds harmony_t **rchip) 9171da177e4SLinus Torvalds { 9181da177e4SLinus Torvalds int err; 9191da177e4SLinus Torvalds harmony_t *h; 9201da177e4SLinus Torvalds static snd_device_ops_t ops = { 9211da177e4SLinus Torvalds .dev_free = snd_harmony_dev_free, 9221da177e4SLinus Torvalds }; 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds *rchip = NULL; 9251da177e4SLinus Torvalds 9261da177e4SLinus Torvalds h = kmalloc(sizeof(*h), GFP_KERNEL); 9271da177e4SLinus Torvalds if (h == NULL) 9281da177e4SLinus Torvalds return -ENOMEM; 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds memset(&h->st, 0, sizeof(h->st)); 9311da177e4SLinus Torvalds memset(&h->stats, 0, sizeof(h->stats)); 9321da177e4SLinus Torvalds memset(&h->pbuf, 0, sizeof(h->pbuf)); 9331da177e4SLinus Torvalds memset(&h->cbuf, 0, sizeof(h->cbuf)); 9341da177e4SLinus Torvalds 93553f01bbaSMatthew Wilcox h->hpa = padev->hpa.start; 9361da177e4SLinus Torvalds h->card = card; 9371da177e4SLinus Torvalds h->dev = padev; 9381da177e4SLinus Torvalds h->irq = padev->irq; 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", 94253f01bbaSMatthew Wilcox padev->hpa.start); 9431da177e4SLinus Torvalds err = -EBUSY; 9441da177e4SLinus Torvalds goto free_and_ret; 9451da177e4SLinus Torvalds } 9461da177e4SLinus Torvalds 9471da177e4SLinus Torvalds err = request_irq(h->irq, snd_harmony_interrupt, 0, 9481da177e4SLinus Torvalds "harmony", h); 9491da177e4SLinus Torvalds if (err) { 9501da177e4SLinus Torvalds printk(KERN_ERR PFX "could not obtain interrupt %d", 9511da177e4SLinus Torvalds h->irq); 9521da177e4SLinus Torvalds goto free_and_ret; 9531da177e4SLinus Torvalds } 9541da177e4SLinus Torvalds 9551da177e4SLinus Torvalds spin_lock_init(&h->mixer_lock); 9561da177e4SLinus Torvalds spin_lock_init(&h->lock); 9571da177e4SLinus Torvalds 9581da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, 9591da177e4SLinus Torvalds h, &ops)) < 0) { 9601da177e4SLinus Torvalds goto free_and_ret; 9611da177e4SLinus Torvalds } 9621da177e4SLinus Torvalds 963a76af199STakashi Iwai snd_card_set_dev(card, &padev->dev); 964a76af199STakashi Iwai 9651da177e4SLinus Torvalds *rchip = h; 9661da177e4SLinus Torvalds 9671da177e4SLinus Torvalds return 0; 9681da177e4SLinus Torvalds 9691da177e4SLinus Torvalds free_and_ret: 9701da177e4SLinus Torvalds snd_harmony_free(h); 9711da177e4SLinus Torvalds return err; 9721da177e4SLinus Torvalds } 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds static int __devinit 9751da177e4SLinus Torvalds snd_harmony_probe(struct parisc_device *padev) 9761da177e4SLinus Torvalds { 9771da177e4SLinus Torvalds int err; 9781da177e4SLinus Torvalds static int dev; 9791da177e4SLinus Torvalds snd_card_t *card; 9801da177e4SLinus Torvalds harmony_t *h; 9811da177e4SLinus Torvalds static int index = SNDRV_DEFAULT_IDX1; 9821da177e4SLinus Torvalds static char *id = SNDRV_DEFAULT_STR1; 9831da177e4SLinus Torvalds 9841da177e4SLinus Torvalds h = parisc_get_drvdata(padev); 9851da177e4SLinus Torvalds if (h != NULL) { 9861da177e4SLinus Torvalds return -ENODEV; 9871da177e4SLinus Torvalds } 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds card = snd_card_new(index, id, THIS_MODULE, 0); 9901da177e4SLinus Torvalds if (card == NULL) 9911da177e4SLinus Torvalds return -ENOMEM; 9921da177e4SLinus Torvalds 9931da177e4SLinus Torvalds err = snd_harmony_create(card, padev, &h); 9941da177e4SLinus Torvalds if (err < 0) { 9951da177e4SLinus Torvalds goto free_and_ret; 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds 9981da177e4SLinus Torvalds err = snd_harmony_pcm_init(h); 9991da177e4SLinus Torvalds if (err < 0) { 10001da177e4SLinus Torvalds goto free_and_ret; 10011da177e4SLinus Torvalds } 10021da177e4SLinus Torvalds 10031da177e4SLinus Torvalds err = snd_harmony_mixer_init(h); 10041da177e4SLinus Torvalds if (err < 0) { 10051da177e4SLinus Torvalds goto free_and_ret; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds strcpy(card->driver, "harmony"); 10091da177e4SLinus Torvalds strcpy(card->shortname, "Harmony"); 10101da177e4SLinus Torvalds sprintf(card->longname, "%s at 0x%lx, irq %i", 10111da177e4SLinus Torvalds card->shortname, h->hpa, h->irq); 10121da177e4SLinus Torvalds 10131da177e4SLinus Torvalds err = snd_card_register(card); 10141da177e4SLinus Torvalds if (err < 0) { 10151da177e4SLinus Torvalds goto free_and_ret; 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds 10181da177e4SLinus Torvalds dev++; 10191da177e4SLinus Torvalds parisc_set_drvdata(padev, h); 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds return 0; 10221da177e4SLinus Torvalds 10231da177e4SLinus Torvalds free_and_ret: 10241da177e4SLinus Torvalds snd_card_free(card); 10251da177e4SLinus Torvalds return err; 10261da177e4SLinus Torvalds } 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds static int __devexit 10291da177e4SLinus Torvalds snd_harmony_remove(struct parisc_device *padev) 10301da177e4SLinus Torvalds { 10311da177e4SLinus Torvalds harmony_t *h = parisc_get_drvdata(padev); 10321da177e4SLinus Torvalds snd_card_free(h->card); 10331da177e4SLinus Torvalds return 0; 10341da177e4SLinus Torvalds } 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds static struct parisc_driver snd_harmony_driver = { 10371da177e4SLinus Torvalds .name = "harmony", 10381da177e4SLinus Torvalds .id_table = snd_harmony_devtable, 10391da177e4SLinus Torvalds .probe = snd_harmony_probe, 10401da177e4SLinus Torvalds .remove = snd_harmony_remove, 10411da177e4SLinus Torvalds }; 10421da177e4SLinus Torvalds 10431da177e4SLinus Torvalds static int __init 10441da177e4SLinus Torvalds alsa_harmony_init(void) 10451da177e4SLinus Torvalds { 10461da177e4SLinus Torvalds int err; 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds err = register_parisc_driver(&snd_harmony_driver); 10491da177e4SLinus Torvalds if (err < 0) { 10501da177e4SLinus Torvalds printk(KERN_ERR PFX "device not found\n"); 10511da177e4SLinus Torvalds return err; 10521da177e4SLinus Torvalds } 10531da177e4SLinus Torvalds 10541da177e4SLinus Torvalds return 0; 10551da177e4SLinus Torvalds } 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds static void __exit 10581da177e4SLinus Torvalds alsa_harmony_fini(void) 10591da177e4SLinus Torvalds { 10601da177e4SLinus Torvalds int err; 10611da177e4SLinus Torvalds 10621da177e4SLinus Torvalds err = unregister_parisc_driver(&snd_harmony_driver); 10631da177e4SLinus Torvalds if (err < 0) { 10641da177e4SLinus Torvalds printk(KERN_ERR PFX "failed to unregister\n"); 10651da177e4SLinus Torvalds } 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds return; 10681da177e4SLinus Torvalds } 10691da177e4SLinus Torvalds 10701da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 10711da177e4SLinus Torvalds MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>"); 10721da177e4SLinus Torvalds MODULE_DESCRIPTION("Harmony sound driver"); 10731da177e4SLinus Torvalds 10741da177e4SLinus Torvalds module_init(alsa_harmony_init); 10751da177e4SLinus Torvalds module_exit(alsa_harmony_fini); 1076