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