18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Hewlett-Packard Harmony audio driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This is a driver for the Harmony audio chipset found 58c2ecf20Sopenharmony_ci * on the LASI ASIC of various early HP PA-RISC workstations. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2004, Kyle McMartin <kyle@{debian.org,parisc-linux.org}> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on the previous Harmony incarnations by, 108c2ecf20Sopenharmony_ci * Copyright 2000 (c) Linuxcare Canada, Alex deVries 118c2ecf20Sopenharmony_ci * Copyright 2000-2003 (c) Helge Deller 128c2ecf20Sopenharmony_ci * Copyright 2001 (c) Matthieu Delahaye 138c2ecf20Sopenharmony_ci * Copyright 2001 (c) Jean-Christophe Vaugeois 148c2ecf20Sopenharmony_ci * Copyright 2003 (c) Laurent Canet 158c2ecf20Sopenharmony_ci * Copyright 2004 (c) Stuart Brady 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Notes: 188c2ecf20Sopenharmony_ci * - graveyard and silence buffers last for lifetime of 198c2ecf20Sopenharmony_ci * the driver. playback and capture buffers are allocated 208c2ecf20Sopenharmony_ci * per _open()/_close(). 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * TODO: 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/init.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/time.h> 288c2ecf20Sopenharmony_ci#include <linux/wait.h> 298c2ecf20Sopenharmony_ci#include <linux/delay.h> 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 328c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 338c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 348c2ecf20Sopenharmony_ci#include <linux/io.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <sound/core.h> 378c2ecf20Sopenharmony_ci#include <sound/pcm.h> 388c2ecf20Sopenharmony_ci#include <sound/control.h> 398c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 408c2ecf20Sopenharmony_ci#include <sound/initval.h> 418c2ecf20Sopenharmony_ci#include <sound/info.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include <asm/hardware.h> 448c2ecf20Sopenharmony_ci#include <asm/parisc-device.h> 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include "harmony.h" 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 498c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 508c2ecf20Sopenharmony_cimodule_param(index, int, 0444); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Harmony driver."); 528c2ecf20Sopenharmony_cimodule_param(id, charp, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Harmony driver."); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct parisc_device_id snd_harmony_devtable[] __initconst = { 578c2ecf20Sopenharmony_ci /* bushmaster / flounder */ 588c2ecf20Sopenharmony_ci { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, 598c2ecf20Sopenharmony_ci /* 712 / 715 */ 608c2ecf20Sopenharmony_ci { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, 618c2ecf20Sopenharmony_ci /* pace */ 628c2ecf20Sopenharmony_ci { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, 638c2ecf20Sopenharmony_ci /* outfield / coral II */ 648c2ecf20Sopenharmony_ci { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, 658c2ecf20Sopenharmony_ci { 0, } 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(parisc, snd_harmony_devtable); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define NAME "harmony" 718c2ecf20Sopenharmony_ci#define PFX NAME ": " 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const unsigned int snd_harmony_rates[] = { 748c2ecf20Sopenharmony_ci 5512, 6615, 8000, 9600, 758c2ecf20Sopenharmony_ci 11025, 16000, 18900, 22050, 768c2ecf20Sopenharmony_ci 27428, 32000, 33075, 37800, 778c2ecf20Sopenharmony_ci 44100, 48000 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const unsigned int rate_bits[14] = { 818c2ecf20Sopenharmony_ci HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ, 828c2ecf20Sopenharmony_ci HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ, 838c2ecf20Sopenharmony_ci HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ, 848c2ecf20Sopenharmony_ci HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ, 858c2ecf20Sopenharmony_ci HARMONY_SR_44KHZ, HARMONY_SR_48KHZ 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraint_rates = { 898c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(snd_harmony_rates), 908c2ecf20Sopenharmony_ci .list = snd_harmony_rates, 918c2ecf20Sopenharmony_ci .mask = 0, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline unsigned long 958c2ecf20Sopenharmony_ciharmony_read(struct snd_harmony *h, unsigned r) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return __raw_readl(h->iobase + r); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline void 1018c2ecf20Sopenharmony_ciharmony_write(struct snd_harmony *h, unsigned r, unsigned long v) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci __raw_writel(v, h->iobase + r); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic inline void 1078c2ecf20Sopenharmony_ciharmony_wait_for_control(struct snd_harmony *h) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic inline void 1138c2ecf20Sopenharmony_ciharmony_reset(struct snd_harmony *h) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_RESET, 1); 1168c2ecf20Sopenharmony_ci mdelay(50); 1178c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_RESET, 0); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void 1218c2ecf20Sopenharmony_ciharmony_disable_interrupts(struct snd_harmony *h) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u32 dstatus; 1248c2ecf20Sopenharmony_ci harmony_wait_for_control(h); 1258c2ecf20Sopenharmony_ci dstatus = harmony_read(h, HARMONY_DSTATUS); 1268c2ecf20Sopenharmony_ci dstatus &= ~HARMONY_DSTATUS_IE; 1278c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_DSTATUS, dstatus); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void 1318c2ecf20Sopenharmony_ciharmony_enable_interrupts(struct snd_harmony *h) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci u32 dstatus; 1348c2ecf20Sopenharmony_ci harmony_wait_for_control(h); 1358c2ecf20Sopenharmony_ci dstatus = harmony_read(h, HARMONY_DSTATUS); 1368c2ecf20Sopenharmony_ci dstatus |= HARMONY_DSTATUS_IE; 1378c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_DSTATUS, dstatus); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void 1418c2ecf20Sopenharmony_ciharmony_mute(struct snd_harmony *h) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned long flags; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_lock_irqsave(&h->mixer_lock, flags); 1468c2ecf20Sopenharmony_ci harmony_wait_for_control(h); 1478c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE); 1488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&h->mixer_lock, flags); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void 1528c2ecf20Sopenharmony_ciharmony_unmute(struct snd_harmony *h) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci unsigned long flags; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci spin_lock_irqsave(&h->mixer_lock, flags); 1578c2ecf20Sopenharmony_ci harmony_wait_for_control(h); 1588c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_GAINCTL, h->st.gain); 1598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&h->mixer_lock, flags); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void 1638c2ecf20Sopenharmony_ciharmony_set_control(struct snd_harmony *h) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci u32 ctrl; 1668c2ecf20Sopenharmony_ci unsigned long flags; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock_irqsave(&h->lock, flags); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ctrl = (HARMONY_CNTL_C | 1718c2ecf20Sopenharmony_ci (h->st.format << 6) | 1728c2ecf20Sopenharmony_ci (h->st.stereo << 5) | 1738c2ecf20Sopenharmony_ci (h->st.rate)); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci harmony_wait_for_control(h); 1768c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_CNTL, ctrl); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&h->lock, flags); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic irqreturn_t 1828c2ecf20Sopenharmony_cisnd_harmony_interrupt(int irq, void *dev) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u32 dstatus; 1858c2ecf20Sopenharmony_ci struct snd_harmony *h = dev; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci spin_lock(&h->lock); 1888c2ecf20Sopenharmony_ci harmony_disable_interrupts(h); 1898c2ecf20Sopenharmony_ci harmony_wait_for_control(h); 1908c2ecf20Sopenharmony_ci dstatus = harmony_read(h, HARMONY_DSTATUS); 1918c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (dstatus & HARMONY_DSTATUS_PN) { 1948c2ecf20Sopenharmony_ci if (h->psubs && h->st.playing) { 1958c2ecf20Sopenharmony_ci spin_lock(&h->lock); 1968c2ecf20Sopenharmony_ci h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */ 1978c2ecf20Sopenharmony_ci h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */ 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_PNXTADD, 2008c2ecf20Sopenharmony_ci h->pbuf.addr + h->pbuf.buf); 2018c2ecf20Sopenharmony_ci h->stats.play_intr++; 2028c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 2038c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(h->psubs); 2048c2ecf20Sopenharmony_ci } else { 2058c2ecf20Sopenharmony_ci spin_lock(&h->lock); 2068c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_PNXTADD, h->sdma.addr); 2078c2ecf20Sopenharmony_ci h->stats.silence_intr++; 2088c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (dstatus & HARMONY_DSTATUS_RN) { 2138c2ecf20Sopenharmony_ci if (h->csubs && h->st.capturing) { 2148c2ecf20Sopenharmony_ci spin_lock(&h->lock); 2158c2ecf20Sopenharmony_ci h->cbuf.buf += h->cbuf.count; 2168c2ecf20Sopenharmony_ci h->cbuf.buf %= h->cbuf.size; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_RNXTADD, 2198c2ecf20Sopenharmony_ci h->cbuf.addr + h->cbuf.buf); 2208c2ecf20Sopenharmony_ci h->stats.rec_intr++; 2218c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 2228c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(h->csubs); 2238c2ecf20Sopenharmony_ci } else { 2248c2ecf20Sopenharmony_ci spin_lock(&h->lock); 2258c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_RNXTADD, h->gdma.addr); 2268c2ecf20Sopenharmony_ci h->stats.graveyard_intr++; 2278c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci spin_lock(&h->lock); 2328c2ecf20Sopenharmony_ci harmony_enable_interrupts(h); 2338c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic unsigned int 2398c2ecf20Sopenharmony_cisnd_harmony_rate_bits(int rate) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci unsigned int i; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_harmony_rates); i++) 2448c2ecf20Sopenharmony_ci if (snd_harmony_rates[i] == rate) 2458c2ecf20Sopenharmony_ci return rate_bits[i]; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return HARMONY_SR_44KHZ; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_harmony_playback = 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 2538c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID | 2548c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER), 2558c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW | 2568c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_A_LAW), 2578c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 | 2588c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_KNOT), 2598c2ecf20Sopenharmony_ci .rate_min = 5512, 2608c2ecf20Sopenharmony_ci .rate_max = 48000, 2618c2ecf20Sopenharmony_ci .channels_min = 1, 2628c2ecf20Sopenharmony_ci .channels_max = 2, 2638c2ecf20Sopenharmony_ci .buffer_bytes_max = MAX_BUF_SIZE, 2648c2ecf20Sopenharmony_ci .period_bytes_min = BUF_SIZE, 2658c2ecf20Sopenharmony_ci .period_bytes_max = BUF_SIZE, 2668c2ecf20Sopenharmony_ci .periods_min = 1, 2678c2ecf20Sopenharmony_ci .periods_max = MAX_BUFS, 2688c2ecf20Sopenharmony_ci .fifo_size = 0, 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_harmony_capture = 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 2748c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID | 2758c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER), 2768c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW | 2778c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_A_LAW), 2788c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 | 2798c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_KNOT), 2808c2ecf20Sopenharmony_ci .rate_min = 5512, 2818c2ecf20Sopenharmony_ci .rate_max = 48000, 2828c2ecf20Sopenharmony_ci .channels_min = 1, 2838c2ecf20Sopenharmony_ci .channels_max = 2, 2848c2ecf20Sopenharmony_ci .buffer_bytes_max = MAX_BUF_SIZE, 2858c2ecf20Sopenharmony_ci .period_bytes_min = BUF_SIZE, 2868c2ecf20Sopenharmony_ci .period_bytes_max = BUF_SIZE, 2878c2ecf20Sopenharmony_ci .periods_min = 1, 2888c2ecf20Sopenharmony_ci .periods_max = MAX_BUFS, 2898c2ecf20Sopenharmony_ci .fifo_size = 0, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int 2938c2ecf20Sopenharmony_cisnd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (h->st.capturing) 2988c2ecf20Sopenharmony_ci return -EBUSY; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci spin_lock(&h->lock); 3018c2ecf20Sopenharmony_ci switch (cmd) { 3028c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3038c2ecf20Sopenharmony_ci h->st.playing = 1; 3048c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr); 3058c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_RNXTADD, h->gdma.addr); 3068c2ecf20Sopenharmony_ci harmony_unmute(h); 3078c2ecf20Sopenharmony_ci harmony_enable_interrupts(h); 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3108c2ecf20Sopenharmony_ci h->st.playing = 0; 3118c2ecf20Sopenharmony_ci harmony_mute(h); 3128c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_PNXTADD, h->sdma.addr); 3138c2ecf20Sopenharmony_ci harmony_disable_interrupts(h); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3168c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3178c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3188c2ecf20Sopenharmony_ci default: 3198c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 3208c2ecf20Sopenharmony_ci snd_BUG(); 3218c2ecf20Sopenharmony_ci return -EINVAL; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int 3298c2ecf20Sopenharmony_cisnd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (h->st.playing) 3348c2ecf20Sopenharmony_ci return -EBUSY; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci spin_lock(&h->lock); 3378c2ecf20Sopenharmony_ci switch (cmd) { 3388c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3398c2ecf20Sopenharmony_ci h->st.capturing = 1; 3408c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_PNXTADD, h->sdma.addr); 3418c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_RNXTADD, h->cbuf.addr); 3428c2ecf20Sopenharmony_ci harmony_unmute(h); 3438c2ecf20Sopenharmony_ci harmony_enable_interrupts(h); 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3468c2ecf20Sopenharmony_ci h->st.capturing = 0; 3478c2ecf20Sopenharmony_ci harmony_mute(h); 3488c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_RNXTADD, h->gdma.addr); 3498c2ecf20Sopenharmony_ci harmony_disable_interrupts(h); 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3528c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3548c2ecf20Sopenharmony_ci default: 3558c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 3568c2ecf20Sopenharmony_ci snd_BUG(); 3578c2ecf20Sopenharmony_ci return -EINVAL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci spin_unlock(&h->lock); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int 3658c2ecf20Sopenharmony_cisnd_harmony_set_data_format(struct snd_harmony *h, int fmt, int force) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci int o = h->st.format; 3688c2ecf20Sopenharmony_ci int n; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci switch(fmt) { 3718c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 3728c2ecf20Sopenharmony_ci n = HARMONY_DF_16BIT_LINEAR; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_A_LAW: 3758c2ecf20Sopenharmony_ci n = HARMONY_DF_8BIT_ALAW; 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_MU_LAW: 3788c2ecf20Sopenharmony_ci n = HARMONY_DF_8BIT_ULAW; 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci default: 3818c2ecf20Sopenharmony_ci n = HARMONY_DF_16BIT_LINEAR; 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (force || o != n) { 3868c2ecf20Sopenharmony_ci snd_pcm_format_set_silence(fmt, h->sdma.area, SILENCE_BUFSZ / 3878c2ecf20Sopenharmony_ci (snd_pcm_format_physical_width(fmt) 3888c2ecf20Sopenharmony_ci / 8)); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return n; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int 3958c2ecf20Sopenharmony_cisnd_harmony_playback_prepare(struct snd_pcm_substream *ss) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 3988c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (h->st.capturing) 4018c2ecf20Sopenharmony_ci return -EBUSY; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci h->pbuf.size = snd_pcm_lib_buffer_bytes(ss); 4048c2ecf20Sopenharmony_ci h->pbuf.count = snd_pcm_lib_period_bytes(ss); 4058c2ecf20Sopenharmony_ci if (h->pbuf.buf >= h->pbuf.size) 4068c2ecf20Sopenharmony_ci h->pbuf.buf = 0; 4078c2ecf20Sopenharmony_ci h->st.playing = 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci h->st.rate = snd_harmony_rate_bits(rt->rate); 4108c2ecf20Sopenharmony_ci h->st.format = snd_harmony_set_data_format(h, rt->format, 0); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (rt->channels == 2) 4138c2ecf20Sopenharmony_ci h->st.stereo = HARMONY_SS_STEREO; 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci h->st.stereo = HARMONY_SS_MONO; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci harmony_set_control(h); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci h->pbuf.addr = rt->dma_addr; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int 4258c2ecf20Sopenharmony_cisnd_harmony_capture_prepare(struct snd_pcm_substream *ss) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 4288c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (h->st.playing) 4318c2ecf20Sopenharmony_ci return -EBUSY; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci h->cbuf.size = snd_pcm_lib_buffer_bytes(ss); 4348c2ecf20Sopenharmony_ci h->cbuf.count = snd_pcm_lib_period_bytes(ss); 4358c2ecf20Sopenharmony_ci if (h->cbuf.buf >= h->cbuf.size) 4368c2ecf20Sopenharmony_ci h->cbuf.buf = 0; 4378c2ecf20Sopenharmony_ci h->st.capturing = 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci h->st.rate = snd_harmony_rate_bits(rt->rate); 4408c2ecf20Sopenharmony_ci h->st.format = snd_harmony_set_data_format(h, rt->format, 0); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (rt->channels == 2) 4438c2ecf20Sopenharmony_ci h->st.stereo = HARMONY_SS_STEREO; 4448c2ecf20Sopenharmony_ci else 4458c2ecf20Sopenharmony_ci h->st.stereo = HARMONY_SS_MONO; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci harmony_set_control(h); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci h->cbuf.addr = rt->dma_addr; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 4558c2ecf20Sopenharmony_cisnd_harmony_playback_pointer(struct snd_pcm_substream *ss) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 4588c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 4598c2ecf20Sopenharmony_ci unsigned long pcuradd; 4608c2ecf20Sopenharmony_ci unsigned long played; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (!(h->st.playing) || (h->psubs == NULL)) 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if ((h->pbuf.addr == 0) || (h->pbuf.size == 0)) 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci pcuradd = harmony_read(h, HARMONY_PCURADD); 4698c2ecf20Sopenharmony_ci played = pcuradd - h->pbuf.addr; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci#ifdef HARMONY_DEBUG 4728c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes\n", 4738c2ecf20Sopenharmony_ci pcuradd, h->pbuf.addr, played); 4748c2ecf20Sopenharmony_ci#endif 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (pcuradd > h->pbuf.addr + h->pbuf.size) { 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return bytes_to_frames(rt, played); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 4848c2ecf20Sopenharmony_cisnd_harmony_capture_pointer(struct snd_pcm_substream *ss) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 4878c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 4888c2ecf20Sopenharmony_ci unsigned long rcuradd; 4898c2ecf20Sopenharmony_ci unsigned long caught; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (!(h->st.capturing) || (h->csubs == NULL)) 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if ((h->cbuf.addr == 0) || (h->cbuf.size == 0)) 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci rcuradd = harmony_read(h, HARMONY_RCURADD); 4988c2ecf20Sopenharmony_ci caught = rcuradd - h->cbuf.addr; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci#ifdef HARMONY_DEBUG 5018c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes\n", 5028c2ecf20Sopenharmony_ci rcuradd, h->cbuf.addr, caught); 5038c2ecf20Sopenharmony_ci#endif 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (rcuradd > h->cbuf.addr + h->cbuf.size) { 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return bytes_to_frames(rt, caught); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int 5138c2ecf20Sopenharmony_cisnd_harmony_playback_open(struct snd_pcm_substream *ss) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 5168c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 5178c2ecf20Sopenharmony_ci int err; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci h->psubs = ss; 5208c2ecf20Sopenharmony_ci rt->hw = snd_harmony_playback; 5218c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, 5228c2ecf20Sopenharmony_ci &hw_constraint_rates); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 5258c2ecf20Sopenharmony_ci if (err < 0) 5268c2ecf20Sopenharmony_ci return err; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int 5328c2ecf20Sopenharmony_cisnd_harmony_capture_open(struct snd_pcm_substream *ss) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 5358c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 5368c2ecf20Sopenharmony_ci int err; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci h->csubs = ss; 5398c2ecf20Sopenharmony_ci rt->hw = snd_harmony_capture; 5408c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, 5418c2ecf20Sopenharmony_ci &hw_constraint_rates); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 5448c2ecf20Sopenharmony_ci if (err < 0) 5458c2ecf20Sopenharmony_ci return err; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int 5518c2ecf20Sopenharmony_cisnd_harmony_playback_close(struct snd_pcm_substream *ss) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 5548c2ecf20Sopenharmony_ci h->psubs = NULL; 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int 5598c2ecf20Sopenharmony_cisnd_harmony_capture_close(struct snd_pcm_substream *ss) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 5628c2ecf20Sopenharmony_ci h->csubs = NULL; 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int 5678c2ecf20Sopenharmony_cisnd_harmony_hw_params(struct snd_pcm_substream *ss, 5688c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_pcm_substream_chip(ss); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS) 5738c2ecf20Sopenharmony_ci ss->runtime->dma_addr = __pa(ss->runtime->dma_area); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_harmony_playback_ops = { 5798c2ecf20Sopenharmony_ci .open = snd_harmony_playback_open, 5808c2ecf20Sopenharmony_ci .close = snd_harmony_playback_close, 5818c2ecf20Sopenharmony_ci .hw_params = snd_harmony_hw_params, 5828c2ecf20Sopenharmony_ci .prepare = snd_harmony_playback_prepare, 5838c2ecf20Sopenharmony_ci .trigger = snd_harmony_playback_trigger, 5848c2ecf20Sopenharmony_ci .pointer = snd_harmony_playback_pointer, 5858c2ecf20Sopenharmony_ci}; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_harmony_capture_ops = { 5888c2ecf20Sopenharmony_ci .open = snd_harmony_capture_open, 5898c2ecf20Sopenharmony_ci .close = snd_harmony_capture_close, 5908c2ecf20Sopenharmony_ci .hw_params = snd_harmony_hw_params, 5918c2ecf20Sopenharmony_ci .prepare = snd_harmony_capture_prepare, 5928c2ecf20Sopenharmony_ci .trigger = snd_harmony_capture_trigger, 5938c2ecf20Sopenharmony_ci .pointer = snd_harmony_capture_pointer, 5948c2ecf20Sopenharmony_ci}; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int 5978c2ecf20Sopenharmony_cisnd_harmony_pcm_init(struct snd_harmony *h) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 6008c2ecf20Sopenharmony_ci int err; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (snd_BUG_ON(!h)) 6038c2ecf20Sopenharmony_ci return -EINVAL; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci harmony_disable_interrupts(h); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm); 6088c2ecf20Sopenharmony_ci if (err < 0) 6098c2ecf20Sopenharmony_ci return err; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 6128c2ecf20Sopenharmony_ci &snd_harmony_playback_ops); 6138c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 6148c2ecf20Sopenharmony_ci &snd_harmony_capture_ops); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci pcm->private_data = h; 6178c2ecf20Sopenharmony_ci pcm->info_flags = 0; 6188c2ecf20Sopenharmony_ci strcpy(pcm->name, "harmony"); 6198c2ecf20Sopenharmony_ci h->pcm = pcm; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci h->psubs = NULL; 6228c2ecf20Sopenharmony_ci h->csubs = NULL; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* initialize graveyard buffer */ 6258c2ecf20Sopenharmony_ci h->dma.type = SNDRV_DMA_TYPE_DEV; 6268c2ecf20Sopenharmony_ci h->dma.dev = &h->dev->dev; 6278c2ecf20Sopenharmony_ci err = snd_dma_alloc_pages(h->dma.type, 6288c2ecf20Sopenharmony_ci h->dma.dev, 6298c2ecf20Sopenharmony_ci BUF_SIZE*GRAVEYARD_BUFS, 6308c2ecf20Sopenharmony_ci &h->gdma); 6318c2ecf20Sopenharmony_ci if (err < 0) { 6328c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "cannot allocate graveyard buffer!\n"); 6338c2ecf20Sopenharmony_ci return err; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* initialize silence buffers */ 6378c2ecf20Sopenharmony_ci err = snd_dma_alloc_pages(h->dma.type, 6388c2ecf20Sopenharmony_ci h->dma.dev, 6398c2ecf20Sopenharmony_ci BUF_SIZE*SILENCE_BUFS, 6408c2ecf20Sopenharmony_ci &h->sdma); 6418c2ecf20Sopenharmony_ci if (err < 0) { 6428c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "cannot allocate silence buffer!\n"); 6438c2ecf20Sopenharmony_ci return err; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* pre-allocate space for DMA */ 6478c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, h->dma.type, h->dma.dev, 6488c2ecf20Sopenharmony_ci MAX_BUF_SIZE, MAX_BUF_SIZE); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci h->st.format = snd_harmony_set_data_format(h, 6518c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_S16_BE, 1); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic void 6578c2ecf20Sopenharmony_cisnd_harmony_set_new_gain(struct snd_harmony *h) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci harmony_wait_for_control(h); 6608c2ecf20Sopenharmony_ci harmony_write(h, HARMONY_GAINCTL, h->st.gain); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int 6648c2ecf20Sopenharmony_cisnd_harmony_mixercontrol_info(struct snd_kcontrol *kc, 6658c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci int mask = (kc->private_value >> 16) & 0xff; 6688c2ecf20Sopenharmony_ci int left_shift = (kc->private_value) & 0xff; 6698c2ecf20Sopenharmony_ci int right_shift = (kc->private_value >> 8) & 0xff; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : 6728c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_TYPE_INTEGER; 6738c2ecf20Sopenharmony_ci uinfo->count = left_shift == right_shift ? 1 : 2; 6748c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 6758c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic int 6818c2ecf20Sopenharmony_cisnd_harmony_volume_get(struct snd_kcontrol *kc, 6828c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_kcontrol_chip(kc); 6858c2ecf20Sopenharmony_ci int shift_left = (kc->private_value) & 0xff; 6868c2ecf20Sopenharmony_ci int shift_right = (kc->private_value >> 8) & 0xff; 6878c2ecf20Sopenharmony_ci int mask = (kc->private_value >> 16) & 0xff; 6888c2ecf20Sopenharmony_ci int invert = (kc->private_value >> 24) & 0xff; 6898c2ecf20Sopenharmony_ci int left, right; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci spin_lock_irq(&h->mixer_lock); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci left = (h->st.gain >> shift_left) & mask; 6948c2ecf20Sopenharmony_ci right = (h->st.gain >> shift_right) & mask; 6958c2ecf20Sopenharmony_ci if (invert) { 6968c2ecf20Sopenharmony_ci left = mask - left; 6978c2ecf20Sopenharmony_ci right = mask - right; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = left; 7018c2ecf20Sopenharmony_ci if (shift_left != shift_right) 7028c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = right; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci spin_unlock_irq(&h->mixer_lock); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int 7108c2ecf20Sopenharmony_cisnd_harmony_volume_put(struct snd_kcontrol *kc, 7118c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_kcontrol_chip(kc); 7148c2ecf20Sopenharmony_ci int shift_left = (kc->private_value) & 0xff; 7158c2ecf20Sopenharmony_ci int shift_right = (kc->private_value >> 8) & 0xff; 7168c2ecf20Sopenharmony_ci int mask = (kc->private_value >> 16) & 0xff; 7178c2ecf20Sopenharmony_ci int invert = (kc->private_value >> 24) & 0xff; 7188c2ecf20Sopenharmony_ci int left, right; 7198c2ecf20Sopenharmony_ci int old_gain = h->st.gain; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci spin_lock_irq(&h->mixer_lock); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci left = ucontrol->value.integer.value[0] & mask; 7248c2ecf20Sopenharmony_ci if (invert) 7258c2ecf20Sopenharmony_ci left = mask - left; 7268c2ecf20Sopenharmony_ci h->st.gain &= ~( (mask << shift_left ) ); 7278c2ecf20Sopenharmony_ci h->st.gain |= (left << shift_left); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (shift_left != shift_right) { 7308c2ecf20Sopenharmony_ci right = ucontrol->value.integer.value[1] & mask; 7318c2ecf20Sopenharmony_ci if (invert) 7328c2ecf20Sopenharmony_ci right = mask - right; 7338c2ecf20Sopenharmony_ci h->st.gain &= ~( (mask << shift_right) ); 7348c2ecf20Sopenharmony_ci h->st.gain |= (right << shift_right); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci snd_harmony_set_new_gain(h); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci spin_unlock_irq(&h->mixer_lock); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return h->st.gain != old_gain; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic int 7458c2ecf20Sopenharmony_cisnd_harmony_captureroute_info(struct snd_kcontrol *kc, 7468c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci static const char * const texts[2] = { "Line", "Mic" }; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, texts); 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic int 7548c2ecf20Sopenharmony_cisnd_harmony_captureroute_get(struct snd_kcontrol *kc, 7558c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_kcontrol_chip(kc); 7588c2ecf20Sopenharmony_ci int value; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci spin_lock_irq(&h->mixer_lock); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1; 7638c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = value; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci spin_unlock_irq(&h->mixer_lock); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int 7718c2ecf20Sopenharmony_cisnd_harmony_captureroute_put(struct snd_kcontrol *kc, 7728c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct snd_harmony *h = snd_kcontrol_chip(kc); 7758c2ecf20Sopenharmony_ci int value; 7768c2ecf20Sopenharmony_ci int old_gain = h->st.gain; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci spin_lock_irq(&h->mixer_lock); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci value = ucontrol->value.enumerated.item[0] & 1; 7818c2ecf20Sopenharmony_ci h->st.gain &= ~HARMONY_GAIN_IS_MASK; 7828c2ecf20Sopenharmony_ci h->st.gain |= value << HARMONY_GAIN_IS_SHIFT; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci snd_harmony_set_new_gain(h); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci spin_unlock_irq(&h->mixer_lock); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return h->st.gain != old_gain; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci#define HARMONY_CONTROLS ARRAY_SIZE(snd_harmony_controls) 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci#define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \ 7948c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 7958c2ecf20Sopenharmony_ci .info = snd_harmony_mixercontrol_info, \ 7968c2ecf20Sopenharmony_ci .get = snd_harmony_volume_get, .put = snd_harmony_volume_put, \ 7978c2ecf20Sopenharmony_ci .private_value = ((left_shift) | ((right_shift) << 8) | \ 7988c2ecf20Sopenharmony_ci ((mask) << 16) | ((invert) << 24)) } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_harmony_controls[] = { 8018c2ecf20Sopenharmony_ci HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT, 8028c2ecf20Sopenharmony_ci HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1), 8038c2ecf20Sopenharmony_ci HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT, 8048c2ecf20Sopenharmony_ci HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0), 8058c2ecf20Sopenharmony_ci HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT, 8068c2ecf20Sopenharmony_ci HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1), 8078c2ecf20Sopenharmony_ci { 8088c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8098c2ecf20Sopenharmony_ci .name = "Input Route", 8108c2ecf20Sopenharmony_ci .info = snd_harmony_captureroute_info, 8118c2ecf20Sopenharmony_ci .get = snd_harmony_captureroute_get, 8128c2ecf20Sopenharmony_ci .put = snd_harmony_captureroute_put 8138c2ecf20Sopenharmony_ci }, 8148c2ecf20Sopenharmony_ci HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT, 8158c2ecf20Sopenharmony_ci HARMONY_GAIN_SE_SHIFT, 1, 0), 8168c2ecf20Sopenharmony_ci HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT, 8178c2ecf20Sopenharmony_ci HARMONY_GAIN_LE_SHIFT, 1, 0), 8188c2ecf20Sopenharmony_ci HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT, 8198c2ecf20Sopenharmony_ci HARMONY_GAIN_HE_SHIFT, 1, 0), 8208c2ecf20Sopenharmony_ci}; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic void 8238c2ecf20Sopenharmony_cisnd_harmony_mixer_reset(struct snd_harmony *h) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci harmony_mute(h); 8268c2ecf20Sopenharmony_ci harmony_reset(h); 8278c2ecf20Sopenharmony_ci h->st.gain = HARMONY_GAIN_DEFAULT; 8288c2ecf20Sopenharmony_ci harmony_unmute(h); 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic int 8328c2ecf20Sopenharmony_cisnd_harmony_mixer_init(struct snd_harmony *h) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci struct snd_card *card; 8358c2ecf20Sopenharmony_ci int idx, err; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (snd_BUG_ON(!h)) 8388c2ecf20Sopenharmony_ci return -EINVAL; 8398c2ecf20Sopenharmony_ci card = h->card; 8408c2ecf20Sopenharmony_ci strcpy(card->mixername, "Harmony Gain control interface"); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci for (idx = 0; idx < HARMONY_CONTROLS; idx++) { 8438c2ecf20Sopenharmony_ci err = snd_ctl_add(card, 8448c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_harmony_controls[idx], h)); 8458c2ecf20Sopenharmony_ci if (err < 0) 8468c2ecf20Sopenharmony_ci return err; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci snd_harmony_mixer_reset(h); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return 0; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int 8558c2ecf20Sopenharmony_cisnd_harmony_free(struct snd_harmony *h) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci if (h->gdma.addr) 8588c2ecf20Sopenharmony_ci snd_dma_free_pages(&h->gdma); 8598c2ecf20Sopenharmony_ci if (h->sdma.addr) 8608c2ecf20Sopenharmony_ci snd_dma_free_pages(&h->sdma); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (h->irq >= 0) 8638c2ecf20Sopenharmony_ci free_irq(h->irq, h); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci iounmap(h->iobase); 8668c2ecf20Sopenharmony_ci kfree(h); 8678c2ecf20Sopenharmony_ci return 0; 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic int 8718c2ecf20Sopenharmony_cisnd_harmony_dev_free(struct snd_device *dev) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct snd_harmony *h = dev->device_data; 8748c2ecf20Sopenharmony_ci return snd_harmony_free(h); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic int 8788c2ecf20Sopenharmony_cisnd_harmony_create(struct snd_card *card, 8798c2ecf20Sopenharmony_ci struct parisc_device *padev, 8808c2ecf20Sopenharmony_ci struct snd_harmony **rchip) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci int err; 8838c2ecf20Sopenharmony_ci struct snd_harmony *h; 8848c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 8858c2ecf20Sopenharmony_ci .dev_free = snd_harmony_dev_free, 8868c2ecf20Sopenharmony_ci }; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci *rchip = NULL; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci h = kzalloc(sizeof(*h), GFP_KERNEL); 8918c2ecf20Sopenharmony_ci if (h == NULL) 8928c2ecf20Sopenharmony_ci return -ENOMEM; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci h->hpa = padev->hpa.start; 8958c2ecf20Sopenharmony_ci h->card = card; 8968c2ecf20Sopenharmony_ci h->dev = padev; 8978c2ecf20Sopenharmony_ci h->irq = -1; 8988c2ecf20Sopenharmony_ci h->iobase = ioremap(padev->hpa.start, HARMONY_SIZE); 8998c2ecf20Sopenharmony_ci if (h->iobase == NULL) { 9008c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n", 9018c2ecf20Sopenharmony_ci (unsigned long)padev->hpa.start); 9028c2ecf20Sopenharmony_ci err = -EBUSY; 9038c2ecf20Sopenharmony_ci goto free_and_ret; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci err = request_irq(padev->irq, snd_harmony_interrupt, 0, 9078c2ecf20Sopenharmony_ci "harmony", h); 9088c2ecf20Sopenharmony_ci if (err) { 9098c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "could not obtain interrupt %d", 9108c2ecf20Sopenharmony_ci padev->irq); 9118c2ecf20Sopenharmony_ci goto free_and_ret; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci h->irq = padev->irq; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci spin_lock_init(&h->mixer_lock); 9168c2ecf20Sopenharmony_ci spin_lock_init(&h->lock); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, 9198c2ecf20Sopenharmony_ci h, &ops)) < 0) { 9208c2ecf20Sopenharmony_ci goto free_and_ret; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci *rchip = h; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci return 0; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cifree_and_ret: 9288c2ecf20Sopenharmony_ci snd_harmony_free(h); 9298c2ecf20Sopenharmony_ci return err; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic int __init 9338c2ecf20Sopenharmony_cisnd_harmony_probe(struct parisc_device *padev) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci int err; 9368c2ecf20Sopenharmony_ci struct snd_card *card; 9378c2ecf20Sopenharmony_ci struct snd_harmony *h; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci err = snd_card_new(&padev->dev, index, id, THIS_MODULE, 0, &card); 9408c2ecf20Sopenharmony_ci if (err < 0) 9418c2ecf20Sopenharmony_ci return err; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci err = snd_harmony_create(card, padev, &h); 9448c2ecf20Sopenharmony_ci if (err < 0) 9458c2ecf20Sopenharmony_ci goto free_and_ret; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci err = snd_harmony_pcm_init(h); 9488c2ecf20Sopenharmony_ci if (err < 0) 9498c2ecf20Sopenharmony_ci goto free_and_ret; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci err = snd_harmony_mixer_init(h); 9528c2ecf20Sopenharmony_ci if (err < 0) 9538c2ecf20Sopenharmony_ci goto free_and_ret; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci strcpy(card->driver, "harmony"); 9568c2ecf20Sopenharmony_ci strcpy(card->shortname, "Harmony"); 9578c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %i", 9588c2ecf20Sopenharmony_ci card->shortname, h->hpa, h->irq); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci err = snd_card_register(card); 9618c2ecf20Sopenharmony_ci if (err < 0) 9628c2ecf20Sopenharmony_ci goto free_and_ret; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci parisc_set_drvdata(padev, card); 9658c2ecf20Sopenharmony_ci return 0; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cifree_and_ret: 9688c2ecf20Sopenharmony_ci snd_card_free(card); 9698c2ecf20Sopenharmony_ci return err; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic int __exit 9738c2ecf20Sopenharmony_cisnd_harmony_remove(struct parisc_device *padev) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci snd_card_free(parisc_get_drvdata(padev)); 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic struct parisc_driver snd_harmony_driver __refdata = { 9808c2ecf20Sopenharmony_ci .name = "harmony", 9818c2ecf20Sopenharmony_ci .id_table = snd_harmony_devtable, 9828c2ecf20Sopenharmony_ci .probe = snd_harmony_probe, 9838c2ecf20Sopenharmony_ci .remove = __exit_p(snd_harmony_remove), 9848c2ecf20Sopenharmony_ci}; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic int __init 9878c2ecf20Sopenharmony_cialsa_harmony_init(void) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci return register_parisc_driver(&snd_harmony_driver); 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic void __exit 9938c2ecf20Sopenharmony_cialsa_harmony_fini(void) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci unregister_parisc_driver(&snd_harmony_driver); 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 9998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>"); 10008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Harmony sound driver"); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cimodule_init(alsa_harmony_init); 10038c2ecf20Sopenharmony_cimodule_exit(alsa_harmony_fini); 1004