xref: /OK3568_Linux_fs/kernel/sound/parisc/harmony.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /* Hewlett-Packard Harmony audio driver
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *   This is a driver for the Harmony audio chipset found
5*4882a593Smuzhiyun  *   on the LASI ASIC of various early HP PA-RISC workstations.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *   Copyright (C) 2004, Kyle McMartin <kyle@{debian.org,parisc-linux.org}>
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  *     Based on the previous Harmony incarnations by,
10*4882a593Smuzhiyun  *       Copyright 2000 (c) Linuxcare Canada, Alex deVries
11*4882a593Smuzhiyun  *       Copyright 2000-2003 (c) Helge Deller
12*4882a593Smuzhiyun  *       Copyright 2001 (c) Matthieu Delahaye
13*4882a593Smuzhiyun  *       Copyright 2001 (c) Jean-Christophe Vaugeois
14*4882a593Smuzhiyun  *       Copyright 2003 (c) Laurent Canet
15*4882a593Smuzhiyun  *       Copyright 2004 (c) Stuart Brady
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * Notes:
18*4882a593Smuzhiyun  *   - graveyard and silence buffers last for lifetime of
19*4882a593Smuzhiyun  *     the driver. playback and capture buffers are allocated
20*4882a593Smuzhiyun  *     per _open()/_close().
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * TODO:
23*4882a593Smuzhiyun  */
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include <linux/init.h>
26*4882a593Smuzhiyun #include <linux/slab.h>
27*4882a593Smuzhiyun #include <linux/time.h>
28*4882a593Smuzhiyun #include <linux/wait.h>
29*4882a593Smuzhiyun #include <linux/delay.h>
30*4882a593Smuzhiyun #include <linux/module.h>
31*4882a593Smuzhiyun #include <linux/interrupt.h>
32*4882a593Smuzhiyun #include <linux/spinlock.h>
33*4882a593Smuzhiyun #include <linux/dma-mapping.h>
34*4882a593Smuzhiyun #include <linux/io.h>
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #include <sound/core.h>
37*4882a593Smuzhiyun #include <sound/pcm.h>
38*4882a593Smuzhiyun #include <sound/control.h>
39*4882a593Smuzhiyun #include <sound/rawmidi.h>
40*4882a593Smuzhiyun #include <sound/initval.h>
41*4882a593Smuzhiyun #include <sound/info.h>
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #include <asm/hardware.h>
44*4882a593Smuzhiyun #include <asm/parisc-device.h>
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun #include "harmony.h"
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
49*4882a593Smuzhiyun static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
50*4882a593Smuzhiyun module_param(index, int, 0444);
51*4882a593Smuzhiyun MODULE_PARM_DESC(index, "Index value for Harmony driver.");
52*4882a593Smuzhiyun module_param(id, charp, 0444);
53*4882a593Smuzhiyun MODULE_PARM_DESC(id, "ID string for Harmony driver.");
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun static const struct parisc_device_id snd_harmony_devtable[] __initconst = {
57*4882a593Smuzhiyun 	/* bushmaster / flounder */
58*4882a593Smuzhiyun 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A },
59*4882a593Smuzhiyun 	/* 712 / 715 */
60*4882a593Smuzhiyun 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B },
61*4882a593Smuzhiyun 	/* pace */
62*4882a593Smuzhiyun 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E },
63*4882a593Smuzhiyun 	/* outfield / coral II */
64*4882a593Smuzhiyun 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F },
65*4882a593Smuzhiyun 	{ 0, }
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun #define NAME "harmony"
71*4882a593Smuzhiyun #define PFX  NAME ": "
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun static const unsigned int snd_harmony_rates[] = {
74*4882a593Smuzhiyun 	5512, 6615, 8000, 9600,
75*4882a593Smuzhiyun 	11025, 16000, 18900, 22050,
76*4882a593Smuzhiyun 	27428, 32000, 33075, 37800,
77*4882a593Smuzhiyun 	44100, 48000
78*4882a593Smuzhiyun };
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun static const unsigned int rate_bits[14] = {
81*4882a593Smuzhiyun 	HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,
82*4882a593Smuzhiyun 	HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,
83*4882a593Smuzhiyun 	HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,
84*4882a593Smuzhiyun 	HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ,
85*4882a593Smuzhiyun 	HARMONY_SR_44KHZ, HARMONY_SR_48KHZ
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun static const struct snd_pcm_hw_constraint_list hw_constraint_rates = {
89*4882a593Smuzhiyun 	.count = ARRAY_SIZE(snd_harmony_rates),
90*4882a593Smuzhiyun 	.list = snd_harmony_rates,
91*4882a593Smuzhiyun 	.mask = 0,
92*4882a593Smuzhiyun };
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun static inline unsigned long
harmony_read(struct snd_harmony * h,unsigned r)95*4882a593Smuzhiyun harmony_read(struct snd_harmony *h, unsigned r)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	return __raw_readl(h->iobase + r);
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun static inline void
harmony_write(struct snd_harmony * h,unsigned r,unsigned long v)101*4882a593Smuzhiyun harmony_write(struct snd_harmony *h, unsigned r, unsigned long v)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	__raw_writel(v, h->iobase + r);
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun static inline void
harmony_wait_for_control(struct snd_harmony * h)107*4882a593Smuzhiyun harmony_wait_for_control(struct snd_harmony *h)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun static inline void
harmony_reset(struct snd_harmony * h)113*4882a593Smuzhiyun harmony_reset(struct snd_harmony *h)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	harmony_write(h, HARMONY_RESET, 1);
116*4882a593Smuzhiyun 	mdelay(50);
117*4882a593Smuzhiyun 	harmony_write(h, HARMONY_RESET, 0);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun static void
harmony_disable_interrupts(struct snd_harmony * h)121*4882a593Smuzhiyun harmony_disable_interrupts(struct snd_harmony *h)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	u32 dstatus;
124*4882a593Smuzhiyun 	harmony_wait_for_control(h);
125*4882a593Smuzhiyun 	dstatus = harmony_read(h, HARMONY_DSTATUS);
126*4882a593Smuzhiyun 	dstatus &= ~HARMONY_DSTATUS_IE;
127*4882a593Smuzhiyun 	harmony_write(h, HARMONY_DSTATUS, dstatus);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun static void
harmony_enable_interrupts(struct snd_harmony * h)131*4882a593Smuzhiyun harmony_enable_interrupts(struct snd_harmony *h)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	u32 dstatus;
134*4882a593Smuzhiyun 	harmony_wait_for_control(h);
135*4882a593Smuzhiyun 	dstatus = harmony_read(h, HARMONY_DSTATUS);
136*4882a593Smuzhiyun 	dstatus |= HARMONY_DSTATUS_IE;
137*4882a593Smuzhiyun 	harmony_write(h, HARMONY_DSTATUS, dstatus);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun static void
harmony_mute(struct snd_harmony * h)141*4882a593Smuzhiyun harmony_mute(struct snd_harmony *h)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun 	unsigned long flags;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	spin_lock_irqsave(&h->mixer_lock, flags);
146*4882a593Smuzhiyun 	harmony_wait_for_control(h);
147*4882a593Smuzhiyun 	harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE);
148*4882a593Smuzhiyun 	spin_unlock_irqrestore(&h->mixer_lock, flags);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun static void
harmony_unmute(struct snd_harmony * h)152*4882a593Smuzhiyun harmony_unmute(struct snd_harmony *h)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	unsigned long flags;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	spin_lock_irqsave(&h->mixer_lock, flags);
157*4882a593Smuzhiyun 	harmony_wait_for_control(h);
158*4882a593Smuzhiyun 	harmony_write(h, HARMONY_GAINCTL, h->st.gain);
159*4882a593Smuzhiyun 	spin_unlock_irqrestore(&h->mixer_lock, flags);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun static void
harmony_set_control(struct snd_harmony * h)163*4882a593Smuzhiyun harmony_set_control(struct snd_harmony *h)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun 	u32 ctrl;
166*4882a593Smuzhiyun 	unsigned long flags;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	spin_lock_irqsave(&h->lock, flags);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	ctrl = (HARMONY_CNTL_C      |
171*4882a593Smuzhiyun 		(h->st.format << 6) |
172*4882a593Smuzhiyun 		(h->st.stereo << 5) |
173*4882a593Smuzhiyun 		(h->st.rate));
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	harmony_wait_for_control(h);
176*4882a593Smuzhiyun 	harmony_write(h, HARMONY_CNTL, ctrl);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	spin_unlock_irqrestore(&h->lock, flags);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun static irqreturn_t
snd_harmony_interrupt(int irq,void * dev)182*4882a593Smuzhiyun snd_harmony_interrupt(int irq, void *dev)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	u32 dstatus;
185*4882a593Smuzhiyun 	struct snd_harmony *h = dev;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	spin_lock(&h->lock);
188*4882a593Smuzhiyun 	harmony_disable_interrupts(h);
189*4882a593Smuzhiyun 	harmony_wait_for_control(h);
190*4882a593Smuzhiyun 	dstatus = harmony_read(h, HARMONY_DSTATUS);
191*4882a593Smuzhiyun 	spin_unlock(&h->lock);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	if (dstatus & HARMONY_DSTATUS_PN) {
194*4882a593Smuzhiyun 		if (h->psubs && h->st.playing) {
195*4882a593Smuzhiyun 			spin_lock(&h->lock);
196*4882a593Smuzhiyun 			h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
197*4882a593Smuzhiyun 			h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 			harmony_write(h, HARMONY_PNXTADD,
200*4882a593Smuzhiyun 				      h->pbuf.addr + h->pbuf.buf);
201*4882a593Smuzhiyun 			h->stats.play_intr++;
202*4882a593Smuzhiyun 			spin_unlock(&h->lock);
203*4882a593Smuzhiyun                         snd_pcm_period_elapsed(h->psubs);
204*4882a593Smuzhiyun 		} else {
205*4882a593Smuzhiyun 			spin_lock(&h->lock);
206*4882a593Smuzhiyun 			harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
207*4882a593Smuzhiyun 			h->stats.silence_intr++;
208*4882a593Smuzhiyun 			spin_unlock(&h->lock);
209*4882a593Smuzhiyun 		}
210*4882a593Smuzhiyun 	}
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	if (dstatus & HARMONY_DSTATUS_RN) {
213*4882a593Smuzhiyun 		if (h->csubs && h->st.capturing) {
214*4882a593Smuzhiyun 			spin_lock(&h->lock);
215*4882a593Smuzhiyun 			h->cbuf.buf += h->cbuf.count;
216*4882a593Smuzhiyun 			h->cbuf.buf %= h->cbuf.size;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 			harmony_write(h, HARMONY_RNXTADD,
219*4882a593Smuzhiyun 				      h->cbuf.addr + h->cbuf.buf);
220*4882a593Smuzhiyun 			h->stats.rec_intr++;
221*4882a593Smuzhiyun 			spin_unlock(&h->lock);
222*4882a593Smuzhiyun                         snd_pcm_period_elapsed(h->csubs);
223*4882a593Smuzhiyun 		} else {
224*4882a593Smuzhiyun 			spin_lock(&h->lock);
225*4882a593Smuzhiyun 			harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
226*4882a593Smuzhiyun 			h->stats.graveyard_intr++;
227*4882a593Smuzhiyun 			spin_unlock(&h->lock);
228*4882a593Smuzhiyun 		}
229*4882a593Smuzhiyun 	}
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	spin_lock(&h->lock);
232*4882a593Smuzhiyun 	harmony_enable_interrupts(h);
233*4882a593Smuzhiyun 	spin_unlock(&h->lock);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	return IRQ_HANDLED;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun static unsigned int
snd_harmony_rate_bits(int rate)239*4882a593Smuzhiyun snd_harmony_rate_bits(int rate)
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun 	unsigned int i;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(snd_harmony_rates); i++)
244*4882a593Smuzhiyun 		if (snd_harmony_rates[i] == rate)
245*4882a593Smuzhiyun 			return rate_bits[i];
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	return HARMONY_SR_44KHZ;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_harmony_playback =
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	.info =	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
253*4882a593Smuzhiyun 		 SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
254*4882a593Smuzhiyun 		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
255*4882a593Smuzhiyun 	.formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
256*4882a593Smuzhiyun 		    SNDRV_PCM_FMTBIT_A_LAW),
257*4882a593Smuzhiyun 	.rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
258*4882a593Smuzhiyun 		  SNDRV_PCM_RATE_KNOT),
259*4882a593Smuzhiyun 	.rate_min = 5512,
260*4882a593Smuzhiyun 	.rate_max = 48000,
261*4882a593Smuzhiyun 	.channels_min =	1,
262*4882a593Smuzhiyun 	.channels_max =	2,
263*4882a593Smuzhiyun 	.buffer_bytes_max = MAX_BUF_SIZE,
264*4882a593Smuzhiyun 	.period_bytes_min = BUF_SIZE,
265*4882a593Smuzhiyun 	.period_bytes_max = BUF_SIZE,
266*4882a593Smuzhiyun 	.periods_min = 1,
267*4882a593Smuzhiyun 	.periods_max = MAX_BUFS,
268*4882a593Smuzhiyun 	.fifo_size = 0,
269*4882a593Smuzhiyun };
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_harmony_capture =
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun         .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
274*4882a593Smuzhiyun                  SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
275*4882a593Smuzhiyun                  SNDRV_PCM_INFO_BLOCK_TRANSFER),
276*4882a593Smuzhiyun         .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
277*4882a593Smuzhiyun                     SNDRV_PCM_FMTBIT_A_LAW),
278*4882a593Smuzhiyun         .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
279*4882a593Smuzhiyun 		  SNDRV_PCM_RATE_KNOT),
280*4882a593Smuzhiyun         .rate_min = 5512,
281*4882a593Smuzhiyun         .rate_max = 48000,
282*4882a593Smuzhiyun         .channels_min = 1,
283*4882a593Smuzhiyun         .channels_max = 2,
284*4882a593Smuzhiyun         .buffer_bytes_max = MAX_BUF_SIZE,
285*4882a593Smuzhiyun         .period_bytes_min = BUF_SIZE,
286*4882a593Smuzhiyun         .period_bytes_max = BUF_SIZE,
287*4882a593Smuzhiyun         .periods_min = 1,
288*4882a593Smuzhiyun         .periods_max = MAX_BUFS,
289*4882a593Smuzhiyun         .fifo_size = 0,
290*4882a593Smuzhiyun };
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun static int
snd_harmony_playback_trigger(struct snd_pcm_substream * ss,int cmd)293*4882a593Smuzhiyun snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	if (h->st.capturing)
298*4882a593Smuzhiyun 		return -EBUSY;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	spin_lock(&h->lock);
301*4882a593Smuzhiyun 	switch (cmd) {
302*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
303*4882a593Smuzhiyun 		h->st.playing = 1;
304*4882a593Smuzhiyun 		harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr);
305*4882a593Smuzhiyun 		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
306*4882a593Smuzhiyun 		harmony_unmute(h);
307*4882a593Smuzhiyun 		harmony_enable_interrupts(h);
308*4882a593Smuzhiyun 		break;
309*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
310*4882a593Smuzhiyun 		h->st.playing = 0;
311*4882a593Smuzhiyun 		harmony_mute(h);
312*4882a593Smuzhiyun 		harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
313*4882a593Smuzhiyun 		harmony_disable_interrupts(h);
314*4882a593Smuzhiyun 		break;
315*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
316*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
317*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
318*4882a593Smuzhiyun 	default:
319*4882a593Smuzhiyun 		spin_unlock(&h->lock);
320*4882a593Smuzhiyun 		snd_BUG();
321*4882a593Smuzhiyun 		return -EINVAL;
322*4882a593Smuzhiyun 	}
323*4882a593Smuzhiyun 	spin_unlock(&h->lock);
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	return 0;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun static int
snd_harmony_capture_trigger(struct snd_pcm_substream * ss,int cmd)329*4882a593Smuzhiyun snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun         struct snd_harmony *h = snd_pcm_substream_chip(ss);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	if (h->st.playing)
334*4882a593Smuzhiyun 		return -EBUSY;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	spin_lock(&h->lock);
337*4882a593Smuzhiyun         switch (cmd) {
338*4882a593Smuzhiyun         case SNDRV_PCM_TRIGGER_START:
339*4882a593Smuzhiyun 		h->st.capturing = 1;
340*4882a593Smuzhiyun                 harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
341*4882a593Smuzhiyun                 harmony_write(h, HARMONY_RNXTADD, h->cbuf.addr);
342*4882a593Smuzhiyun 		harmony_unmute(h);
343*4882a593Smuzhiyun                 harmony_enable_interrupts(h);
344*4882a593Smuzhiyun 		break;
345*4882a593Smuzhiyun         case SNDRV_PCM_TRIGGER_STOP:
346*4882a593Smuzhiyun 		h->st.capturing = 0;
347*4882a593Smuzhiyun 		harmony_mute(h);
348*4882a593Smuzhiyun 		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
349*4882a593Smuzhiyun 		harmony_disable_interrupts(h);
350*4882a593Smuzhiyun 		break;
351*4882a593Smuzhiyun         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
352*4882a593Smuzhiyun         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
353*4882a593Smuzhiyun         case SNDRV_PCM_TRIGGER_SUSPEND:
354*4882a593Smuzhiyun 	default:
355*4882a593Smuzhiyun 		spin_unlock(&h->lock);
356*4882a593Smuzhiyun 		snd_BUG();
357*4882a593Smuzhiyun                 return -EINVAL;
358*4882a593Smuzhiyun         }
359*4882a593Smuzhiyun 	spin_unlock(&h->lock);
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun         return 0;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun static int
snd_harmony_set_data_format(struct snd_harmony * h,int fmt,int force)365*4882a593Smuzhiyun snd_harmony_set_data_format(struct snd_harmony *h, int fmt, int force)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun 	int o = h->st.format;
368*4882a593Smuzhiyun 	int n;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	switch(fmt) {
371*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S16_BE:
372*4882a593Smuzhiyun 		n = HARMONY_DF_16BIT_LINEAR;
373*4882a593Smuzhiyun 		break;
374*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_A_LAW:
375*4882a593Smuzhiyun 		n = HARMONY_DF_8BIT_ALAW;
376*4882a593Smuzhiyun 		break;
377*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_MU_LAW:
378*4882a593Smuzhiyun 		n = HARMONY_DF_8BIT_ULAW;
379*4882a593Smuzhiyun 		break;
380*4882a593Smuzhiyun 	default:
381*4882a593Smuzhiyun 		n = HARMONY_DF_16BIT_LINEAR;
382*4882a593Smuzhiyun 		break;
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	if (force || o != n) {
386*4882a593Smuzhiyun 		snd_pcm_format_set_silence(fmt, h->sdma.area, SILENCE_BUFSZ /
387*4882a593Smuzhiyun 					   (snd_pcm_format_physical_width(fmt)
388*4882a593Smuzhiyun 					    / 8));
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	return n;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun static int
snd_harmony_playback_prepare(struct snd_pcm_substream * ss)395*4882a593Smuzhiyun snd_harmony_playback_prepare(struct snd_pcm_substream *ss)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
398*4882a593Smuzhiyun 	struct snd_pcm_runtime *rt = ss->runtime;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	if (h->st.capturing)
401*4882a593Smuzhiyun 		return -EBUSY;
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	h->pbuf.size = snd_pcm_lib_buffer_bytes(ss);
404*4882a593Smuzhiyun 	h->pbuf.count = snd_pcm_lib_period_bytes(ss);
405*4882a593Smuzhiyun 	if (h->pbuf.buf >= h->pbuf.size)
406*4882a593Smuzhiyun 		h->pbuf.buf = 0;
407*4882a593Smuzhiyun 	h->st.playing = 0;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	h->st.rate = snd_harmony_rate_bits(rt->rate);
410*4882a593Smuzhiyun 	h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	if (rt->channels == 2)
413*4882a593Smuzhiyun 		h->st.stereo = HARMONY_SS_STEREO;
414*4882a593Smuzhiyun 	else
415*4882a593Smuzhiyun 		h->st.stereo = HARMONY_SS_MONO;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	harmony_set_control(h);
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	h->pbuf.addr = rt->dma_addr;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	return 0;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun static int
snd_harmony_capture_prepare(struct snd_pcm_substream * ss)425*4882a593Smuzhiyun snd_harmony_capture_prepare(struct snd_pcm_substream *ss)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun         struct snd_harmony *h = snd_pcm_substream_chip(ss);
428*4882a593Smuzhiyun         struct snd_pcm_runtime *rt = ss->runtime;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	if (h->st.playing)
431*4882a593Smuzhiyun 		return -EBUSY;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun         h->cbuf.size = snd_pcm_lib_buffer_bytes(ss);
434*4882a593Smuzhiyun         h->cbuf.count = snd_pcm_lib_period_bytes(ss);
435*4882a593Smuzhiyun 	if (h->cbuf.buf >= h->cbuf.size)
436*4882a593Smuzhiyun 	        h->cbuf.buf = 0;
437*4882a593Smuzhiyun 	h->st.capturing = 0;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun         h->st.rate = snd_harmony_rate_bits(rt->rate);
440*4882a593Smuzhiyun         h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun         if (rt->channels == 2)
443*4882a593Smuzhiyun                 h->st.stereo = HARMONY_SS_STEREO;
444*4882a593Smuzhiyun         else
445*4882a593Smuzhiyun                 h->st.stereo = HARMONY_SS_MONO;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun         harmony_set_control(h);
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun         h->cbuf.addr = rt->dma_addr;
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun         return 0;
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun static snd_pcm_uframes_t
snd_harmony_playback_pointer(struct snd_pcm_substream * ss)455*4882a593Smuzhiyun snd_harmony_playback_pointer(struct snd_pcm_substream *ss)
456*4882a593Smuzhiyun {
457*4882a593Smuzhiyun 	struct snd_pcm_runtime *rt = ss->runtime;
458*4882a593Smuzhiyun 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
459*4882a593Smuzhiyun 	unsigned long pcuradd;
460*4882a593Smuzhiyun 	unsigned long played;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	if (!(h->st.playing) || (h->psubs == NULL))
463*4882a593Smuzhiyun 		return 0;
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	if ((h->pbuf.addr == 0) || (h->pbuf.size == 0))
466*4882a593Smuzhiyun 		return 0;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	pcuradd = harmony_read(h, HARMONY_PCURADD);
469*4882a593Smuzhiyun 	played = pcuradd - h->pbuf.addr;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun #ifdef HARMONY_DEBUG
472*4882a593Smuzhiyun 	printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes\n",
473*4882a593Smuzhiyun 	       pcuradd, h->pbuf.addr, played);
474*4882a593Smuzhiyun #endif
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	if (pcuradd > h->pbuf.addr + h->pbuf.size) {
477*4882a593Smuzhiyun 		return 0;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	return bytes_to_frames(rt, played);
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun static snd_pcm_uframes_t
snd_harmony_capture_pointer(struct snd_pcm_substream * ss)484*4882a593Smuzhiyun snd_harmony_capture_pointer(struct snd_pcm_substream *ss)
485*4882a593Smuzhiyun {
486*4882a593Smuzhiyun         struct snd_pcm_runtime *rt = ss->runtime;
487*4882a593Smuzhiyun         struct snd_harmony *h = snd_pcm_substream_chip(ss);
488*4882a593Smuzhiyun         unsigned long rcuradd;
489*4882a593Smuzhiyun         unsigned long caught;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun         if (!(h->st.capturing) || (h->csubs == NULL))
492*4882a593Smuzhiyun                 return 0;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun         if ((h->cbuf.addr == 0) || (h->cbuf.size == 0))
495*4882a593Smuzhiyun                 return 0;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun         rcuradd = harmony_read(h, HARMONY_RCURADD);
498*4882a593Smuzhiyun         caught = rcuradd - h->cbuf.addr;
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun #ifdef HARMONY_DEBUG
501*4882a593Smuzhiyun         printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes\n",
502*4882a593Smuzhiyun                rcuradd, h->cbuf.addr, caught);
503*4882a593Smuzhiyun #endif
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun         if (rcuradd > h->cbuf.addr + h->cbuf.size) {
506*4882a593Smuzhiyun 		return 0;
507*4882a593Smuzhiyun 	}
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun         return bytes_to_frames(rt, caught);
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun static int
snd_harmony_playback_open(struct snd_pcm_substream * ss)513*4882a593Smuzhiyun snd_harmony_playback_open(struct snd_pcm_substream *ss)
514*4882a593Smuzhiyun {
515*4882a593Smuzhiyun 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
516*4882a593Smuzhiyun 	struct snd_pcm_runtime *rt = ss->runtime;
517*4882a593Smuzhiyun 	int err;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	h->psubs = ss;
520*4882a593Smuzhiyun 	rt->hw = snd_harmony_playback;
521*4882a593Smuzhiyun 	snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
522*4882a593Smuzhiyun 				   &hw_constraint_rates);
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
525*4882a593Smuzhiyun 	if (err < 0)
526*4882a593Smuzhiyun 		return err;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	return 0;
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun static int
snd_harmony_capture_open(struct snd_pcm_substream * ss)532*4882a593Smuzhiyun snd_harmony_capture_open(struct snd_pcm_substream *ss)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun         struct snd_harmony *h = snd_pcm_substream_chip(ss);
535*4882a593Smuzhiyun         struct snd_pcm_runtime *rt = ss->runtime;
536*4882a593Smuzhiyun         int err;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun         h->csubs = ss;
539*4882a593Smuzhiyun         rt->hw = snd_harmony_capture;
540*4882a593Smuzhiyun         snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
541*4882a593Smuzhiyun                                    &hw_constraint_rates);
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun         err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
544*4882a593Smuzhiyun         if (err < 0)
545*4882a593Smuzhiyun                 return err;
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun         return 0;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun static int
snd_harmony_playback_close(struct snd_pcm_substream * ss)551*4882a593Smuzhiyun snd_harmony_playback_close(struct snd_pcm_substream *ss)
552*4882a593Smuzhiyun {
553*4882a593Smuzhiyun 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
554*4882a593Smuzhiyun 	h->psubs = NULL;
555*4882a593Smuzhiyun 	return 0;
556*4882a593Smuzhiyun }
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun static int
snd_harmony_capture_close(struct snd_pcm_substream * ss)559*4882a593Smuzhiyun snd_harmony_capture_close(struct snd_pcm_substream *ss)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun         struct snd_harmony *h = snd_pcm_substream_chip(ss);
562*4882a593Smuzhiyun         h->csubs = NULL;
563*4882a593Smuzhiyun         return 0;
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun static int
snd_harmony_hw_params(struct snd_pcm_substream * ss,struct snd_pcm_hw_params * hw)567*4882a593Smuzhiyun snd_harmony_hw_params(struct snd_pcm_substream *ss,
568*4882a593Smuzhiyun 		      struct snd_pcm_hw_params *hw)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun 	struct snd_harmony *h = snd_pcm_substream_chip(ss);
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	if (h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS)
573*4882a593Smuzhiyun 		ss->runtime->dma_addr = __pa(ss->runtime->dma_area);
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	return 0;
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun static const struct snd_pcm_ops snd_harmony_playback_ops = {
579*4882a593Smuzhiyun 	.open =	snd_harmony_playback_open,
580*4882a593Smuzhiyun 	.close = snd_harmony_playback_close,
581*4882a593Smuzhiyun 	.hw_params = snd_harmony_hw_params,
582*4882a593Smuzhiyun 	.prepare = snd_harmony_playback_prepare,
583*4882a593Smuzhiyun 	.trigger = snd_harmony_playback_trigger,
584*4882a593Smuzhiyun  	.pointer = snd_harmony_playback_pointer,
585*4882a593Smuzhiyun };
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun static const struct snd_pcm_ops snd_harmony_capture_ops = {
588*4882a593Smuzhiyun         .open = snd_harmony_capture_open,
589*4882a593Smuzhiyun         .close = snd_harmony_capture_close,
590*4882a593Smuzhiyun         .hw_params = snd_harmony_hw_params,
591*4882a593Smuzhiyun         .prepare = snd_harmony_capture_prepare,
592*4882a593Smuzhiyun         .trigger = snd_harmony_capture_trigger,
593*4882a593Smuzhiyun         .pointer = snd_harmony_capture_pointer,
594*4882a593Smuzhiyun };
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun static int
snd_harmony_pcm_init(struct snd_harmony * h)597*4882a593Smuzhiyun snd_harmony_pcm_init(struct snd_harmony *h)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun 	struct snd_pcm *pcm;
600*4882a593Smuzhiyun 	int err;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	if (snd_BUG_ON(!h))
603*4882a593Smuzhiyun 		return -EINVAL;
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	harmony_disable_interrupts(h);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun    	err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm);
608*4882a593Smuzhiyun 	if (err < 0)
609*4882a593Smuzhiyun 		return err;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
612*4882a593Smuzhiyun 			&snd_harmony_playback_ops);
613*4882a593Smuzhiyun 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
614*4882a593Smuzhiyun 			&snd_harmony_capture_ops);
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	pcm->private_data = h;
617*4882a593Smuzhiyun 	pcm->info_flags = 0;
618*4882a593Smuzhiyun 	strcpy(pcm->name, "harmony");
619*4882a593Smuzhiyun 	h->pcm = pcm;
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun 	h->psubs = NULL;
622*4882a593Smuzhiyun 	h->csubs = NULL;
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	/* initialize graveyard buffer */
625*4882a593Smuzhiyun 	h->dma.type = SNDRV_DMA_TYPE_DEV;
626*4882a593Smuzhiyun 	h->dma.dev = &h->dev->dev;
627*4882a593Smuzhiyun 	err = snd_dma_alloc_pages(h->dma.type,
628*4882a593Smuzhiyun 				  h->dma.dev,
629*4882a593Smuzhiyun 				  BUF_SIZE*GRAVEYARD_BUFS,
630*4882a593Smuzhiyun 				  &h->gdma);
631*4882a593Smuzhiyun 	if (err < 0) {
632*4882a593Smuzhiyun 		printk(KERN_ERR PFX "cannot allocate graveyard buffer!\n");
633*4882a593Smuzhiyun 		return err;
634*4882a593Smuzhiyun 	}
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	/* initialize silence buffers */
637*4882a593Smuzhiyun 	err = snd_dma_alloc_pages(h->dma.type,
638*4882a593Smuzhiyun 				  h->dma.dev,
639*4882a593Smuzhiyun 				  BUF_SIZE*SILENCE_BUFS,
640*4882a593Smuzhiyun 				  &h->sdma);
641*4882a593Smuzhiyun 	if (err < 0) {
642*4882a593Smuzhiyun 		printk(KERN_ERR PFX "cannot allocate silence buffer!\n");
643*4882a593Smuzhiyun 		return err;
644*4882a593Smuzhiyun 	}
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	/* pre-allocate space for DMA */
647*4882a593Smuzhiyun 	snd_pcm_set_managed_buffer_all(pcm, h->dma.type, h->dma.dev,
648*4882a593Smuzhiyun 				       MAX_BUF_SIZE, MAX_BUF_SIZE);
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	h->st.format = snd_harmony_set_data_format(h,
651*4882a593Smuzhiyun 		SNDRV_PCM_FORMAT_S16_BE, 1);
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	return 0;
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun static void
snd_harmony_set_new_gain(struct snd_harmony * h)657*4882a593Smuzhiyun snd_harmony_set_new_gain(struct snd_harmony *h)
658*4882a593Smuzhiyun {
659*4882a593Smuzhiyun  	harmony_wait_for_control(h);
660*4882a593Smuzhiyun 	harmony_write(h, HARMONY_GAINCTL, h->st.gain);
661*4882a593Smuzhiyun }
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun static int
snd_harmony_mixercontrol_info(struct snd_kcontrol * kc,struct snd_ctl_elem_info * uinfo)664*4882a593Smuzhiyun snd_harmony_mixercontrol_info(struct snd_kcontrol *kc,
665*4882a593Smuzhiyun 			      struct snd_ctl_elem_info *uinfo)
666*4882a593Smuzhiyun {
667*4882a593Smuzhiyun 	int mask = (kc->private_value >> 16) & 0xff;
668*4882a593Smuzhiyun 	int left_shift = (kc->private_value) & 0xff;
669*4882a593Smuzhiyun 	int right_shift = (kc->private_value >> 8) & 0xff;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :
672*4882a593Smuzhiyun 		       SNDRV_CTL_ELEM_TYPE_INTEGER;
673*4882a593Smuzhiyun 	uinfo->count = left_shift == right_shift ? 1 : 2;
674*4882a593Smuzhiyun 	uinfo->value.integer.min = 0;
675*4882a593Smuzhiyun 	uinfo->value.integer.max = mask;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	return 0;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun static int
snd_harmony_volume_get(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)681*4882a593Smuzhiyun snd_harmony_volume_get(struct snd_kcontrol *kc,
682*4882a593Smuzhiyun 		       struct snd_ctl_elem_value *ucontrol)
683*4882a593Smuzhiyun {
684*4882a593Smuzhiyun 	struct snd_harmony *h = snd_kcontrol_chip(kc);
685*4882a593Smuzhiyun 	int shift_left = (kc->private_value) & 0xff;
686*4882a593Smuzhiyun 	int shift_right = (kc->private_value >> 8) & 0xff;
687*4882a593Smuzhiyun 	int mask = (kc->private_value >> 16) & 0xff;
688*4882a593Smuzhiyun 	int invert = (kc->private_value >> 24) & 0xff;
689*4882a593Smuzhiyun 	int left, right;
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	spin_lock_irq(&h->mixer_lock);
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	left = (h->st.gain >> shift_left) & mask;
694*4882a593Smuzhiyun 	right = (h->st.gain >> shift_right) & mask;
695*4882a593Smuzhiyun 	if (invert) {
696*4882a593Smuzhiyun 		left = mask - left;
697*4882a593Smuzhiyun 		right = mask - right;
698*4882a593Smuzhiyun 	}
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	ucontrol->value.integer.value[0] = left;
701*4882a593Smuzhiyun 	if (shift_left != shift_right)
702*4882a593Smuzhiyun 		ucontrol->value.integer.value[1] = right;
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	spin_unlock_irq(&h->mixer_lock);
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun 	return 0;
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun static int
snd_harmony_volume_put(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)710*4882a593Smuzhiyun snd_harmony_volume_put(struct snd_kcontrol *kc,
711*4882a593Smuzhiyun 		       struct snd_ctl_elem_value *ucontrol)
712*4882a593Smuzhiyun {
713*4882a593Smuzhiyun 	struct snd_harmony *h = snd_kcontrol_chip(kc);
714*4882a593Smuzhiyun 	int shift_left = (kc->private_value) & 0xff;
715*4882a593Smuzhiyun 	int shift_right = (kc->private_value >> 8) & 0xff;
716*4882a593Smuzhiyun 	int mask = (kc->private_value >> 16) & 0xff;
717*4882a593Smuzhiyun 	int invert = (kc->private_value >> 24) & 0xff;
718*4882a593Smuzhiyun 	int left, right;
719*4882a593Smuzhiyun 	int old_gain = h->st.gain;
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	spin_lock_irq(&h->mixer_lock);
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	left = ucontrol->value.integer.value[0] & mask;
724*4882a593Smuzhiyun 	if (invert)
725*4882a593Smuzhiyun 		left = mask - left;
726*4882a593Smuzhiyun 	h->st.gain &= ~( (mask << shift_left ) );
727*4882a593Smuzhiyun  	h->st.gain |= (left << shift_left);
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	if (shift_left != shift_right) {
730*4882a593Smuzhiyun 		right = ucontrol->value.integer.value[1] & mask;
731*4882a593Smuzhiyun 		if (invert)
732*4882a593Smuzhiyun 			right = mask - right;
733*4882a593Smuzhiyun 		h->st.gain &= ~( (mask << shift_right) );
734*4882a593Smuzhiyun 		h->st.gain |= (right << shift_right);
735*4882a593Smuzhiyun 	}
736*4882a593Smuzhiyun 
737*4882a593Smuzhiyun 	snd_harmony_set_new_gain(h);
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	spin_unlock_irq(&h->mixer_lock);
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 	return h->st.gain != old_gain;
742*4882a593Smuzhiyun }
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun static int
snd_harmony_captureroute_info(struct snd_kcontrol * kc,struct snd_ctl_elem_info * uinfo)745*4882a593Smuzhiyun snd_harmony_captureroute_info(struct snd_kcontrol *kc,
746*4882a593Smuzhiyun 			      struct snd_ctl_elem_info *uinfo)
747*4882a593Smuzhiyun {
748*4882a593Smuzhiyun 	static const char * const texts[2] = { "Line", "Mic" };
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 	return snd_ctl_enum_info(uinfo, 1, 2, texts);
751*4882a593Smuzhiyun }
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun static int
snd_harmony_captureroute_get(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)754*4882a593Smuzhiyun snd_harmony_captureroute_get(struct snd_kcontrol *kc,
755*4882a593Smuzhiyun 			     struct snd_ctl_elem_value *ucontrol)
756*4882a593Smuzhiyun {
757*4882a593Smuzhiyun 	struct snd_harmony *h = snd_kcontrol_chip(kc);
758*4882a593Smuzhiyun 	int value;
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 	spin_lock_irq(&h->mixer_lock);
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1;
763*4882a593Smuzhiyun 	ucontrol->value.enumerated.item[0] = value;
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	spin_unlock_irq(&h->mixer_lock);
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	return 0;
768*4882a593Smuzhiyun }
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun static int
snd_harmony_captureroute_put(struct snd_kcontrol * kc,struct snd_ctl_elem_value * ucontrol)771*4882a593Smuzhiyun snd_harmony_captureroute_put(struct snd_kcontrol *kc,
772*4882a593Smuzhiyun 			     struct snd_ctl_elem_value *ucontrol)
773*4882a593Smuzhiyun {
774*4882a593Smuzhiyun 	struct snd_harmony *h = snd_kcontrol_chip(kc);
775*4882a593Smuzhiyun 	int value;
776*4882a593Smuzhiyun 	int old_gain = h->st.gain;
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun 	spin_lock_irq(&h->mixer_lock);
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	value = ucontrol->value.enumerated.item[0] & 1;
781*4882a593Smuzhiyun 	h->st.gain &= ~HARMONY_GAIN_IS_MASK;
782*4882a593Smuzhiyun  	h->st.gain |= value << HARMONY_GAIN_IS_SHIFT;
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	snd_harmony_set_new_gain(h);
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	spin_unlock_irq(&h->mixer_lock);
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun 	return h->st.gain != old_gain;
789*4882a593Smuzhiyun }
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun #define HARMONY_CONTROLS	ARRAY_SIZE(snd_harmony_controls)
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun #define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \
794*4882a593Smuzhiyun { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,                \
795*4882a593Smuzhiyun   .info = snd_harmony_mixercontrol_info,                             \
796*4882a593Smuzhiyun   .get = snd_harmony_volume_get, .put = snd_harmony_volume_put,      \
797*4882a593Smuzhiyun   .private_value = ((left_shift) | ((right_shift) << 8) |            \
798*4882a593Smuzhiyun                    ((mask) << 16) | ((invert) << 24)) }
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_harmony_controls[] = {
801*4882a593Smuzhiyun 	HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT,
802*4882a593Smuzhiyun 		       HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1),
803*4882a593Smuzhiyun 	HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT,
804*4882a593Smuzhiyun 		       HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0),
805*4882a593Smuzhiyun 	HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT,
806*4882a593Smuzhiyun 		       HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1),
807*4882a593Smuzhiyun 	{
808*4882a593Smuzhiyun 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
809*4882a593Smuzhiyun 		.name = "Input Route",
810*4882a593Smuzhiyun 		.info = snd_harmony_captureroute_info,
811*4882a593Smuzhiyun 		.get = snd_harmony_captureroute_get,
812*4882a593Smuzhiyun 		.put = snd_harmony_captureroute_put
813*4882a593Smuzhiyun 	},
814*4882a593Smuzhiyun 	HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT,
815*4882a593Smuzhiyun 		       HARMONY_GAIN_SE_SHIFT, 1, 0),
816*4882a593Smuzhiyun 	HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT,
817*4882a593Smuzhiyun 		       HARMONY_GAIN_LE_SHIFT, 1, 0),
818*4882a593Smuzhiyun 	HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT,
819*4882a593Smuzhiyun 		       HARMONY_GAIN_HE_SHIFT, 1, 0),
820*4882a593Smuzhiyun };
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun static void
snd_harmony_mixer_reset(struct snd_harmony * h)823*4882a593Smuzhiyun snd_harmony_mixer_reset(struct snd_harmony *h)
824*4882a593Smuzhiyun {
825*4882a593Smuzhiyun 	harmony_mute(h);
826*4882a593Smuzhiyun 	harmony_reset(h);
827*4882a593Smuzhiyun 	h->st.gain = HARMONY_GAIN_DEFAULT;
828*4882a593Smuzhiyun 	harmony_unmute(h);
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun static int
snd_harmony_mixer_init(struct snd_harmony * h)832*4882a593Smuzhiyun snd_harmony_mixer_init(struct snd_harmony *h)
833*4882a593Smuzhiyun {
834*4882a593Smuzhiyun 	struct snd_card *card;
835*4882a593Smuzhiyun 	int idx, err;
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	if (snd_BUG_ON(!h))
838*4882a593Smuzhiyun 		return -EINVAL;
839*4882a593Smuzhiyun 	card = h->card;
840*4882a593Smuzhiyun 	strcpy(card->mixername, "Harmony Gain control interface");
841*4882a593Smuzhiyun 
842*4882a593Smuzhiyun 	for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
843*4882a593Smuzhiyun 		err = snd_ctl_add(card,
844*4882a593Smuzhiyun 				  snd_ctl_new1(&snd_harmony_controls[idx], h));
845*4882a593Smuzhiyun 		if (err < 0)
846*4882a593Smuzhiyun 			return err;
847*4882a593Smuzhiyun 	}
848*4882a593Smuzhiyun 
849*4882a593Smuzhiyun 	snd_harmony_mixer_reset(h);
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	return 0;
852*4882a593Smuzhiyun }
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun static int
snd_harmony_free(struct snd_harmony * h)855*4882a593Smuzhiyun snd_harmony_free(struct snd_harmony *h)
856*4882a593Smuzhiyun {
857*4882a593Smuzhiyun         if (h->gdma.addr)
858*4882a593Smuzhiyun                 snd_dma_free_pages(&h->gdma);
859*4882a593Smuzhiyun         if (h->sdma.addr)
860*4882a593Smuzhiyun                 snd_dma_free_pages(&h->sdma);
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun 	if (h->irq >= 0)
863*4882a593Smuzhiyun 		free_irq(h->irq, h);
864*4882a593Smuzhiyun 
865*4882a593Smuzhiyun 	iounmap(h->iobase);
866*4882a593Smuzhiyun 	kfree(h);
867*4882a593Smuzhiyun 	return 0;
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun 
870*4882a593Smuzhiyun static int
snd_harmony_dev_free(struct snd_device * dev)871*4882a593Smuzhiyun snd_harmony_dev_free(struct snd_device *dev)
872*4882a593Smuzhiyun {
873*4882a593Smuzhiyun 	struct snd_harmony *h = dev->device_data;
874*4882a593Smuzhiyun 	return snd_harmony_free(h);
875*4882a593Smuzhiyun }
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun static int
snd_harmony_create(struct snd_card * card,struct parisc_device * padev,struct snd_harmony ** rchip)878*4882a593Smuzhiyun snd_harmony_create(struct snd_card *card,
879*4882a593Smuzhiyun 		   struct parisc_device *padev,
880*4882a593Smuzhiyun 		   struct snd_harmony **rchip)
881*4882a593Smuzhiyun {
882*4882a593Smuzhiyun 	int err;
883*4882a593Smuzhiyun 	struct snd_harmony *h;
884*4882a593Smuzhiyun 	static const struct snd_device_ops ops = {
885*4882a593Smuzhiyun 		.dev_free = snd_harmony_dev_free,
886*4882a593Smuzhiyun 	};
887*4882a593Smuzhiyun 
888*4882a593Smuzhiyun 	*rchip = NULL;
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	h = kzalloc(sizeof(*h), GFP_KERNEL);
891*4882a593Smuzhiyun 	if (h == NULL)
892*4882a593Smuzhiyun 		return -ENOMEM;
893*4882a593Smuzhiyun 
894*4882a593Smuzhiyun 	h->hpa = padev->hpa.start;
895*4882a593Smuzhiyun 	h->card = card;
896*4882a593Smuzhiyun 	h->dev = padev;
897*4882a593Smuzhiyun 	h->irq = -1;
898*4882a593Smuzhiyun 	h->iobase = ioremap(padev->hpa.start, HARMONY_SIZE);
899*4882a593Smuzhiyun 	if (h->iobase == NULL) {
900*4882a593Smuzhiyun 		printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
901*4882a593Smuzhiyun 		       (unsigned long)padev->hpa.start);
902*4882a593Smuzhiyun 		err = -EBUSY;
903*4882a593Smuzhiyun 		goto free_and_ret;
904*4882a593Smuzhiyun 	}
905*4882a593Smuzhiyun 
906*4882a593Smuzhiyun 	err = request_irq(padev->irq, snd_harmony_interrupt, 0,
907*4882a593Smuzhiyun 			  "harmony", h);
908*4882a593Smuzhiyun 	if (err) {
909*4882a593Smuzhiyun 		printk(KERN_ERR PFX "could not obtain interrupt %d",
910*4882a593Smuzhiyun 		       padev->irq);
911*4882a593Smuzhiyun 		goto free_and_ret;
912*4882a593Smuzhiyun 	}
913*4882a593Smuzhiyun 	h->irq = padev->irq;
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun 	spin_lock_init(&h->mixer_lock);
916*4882a593Smuzhiyun 	spin_lock_init(&h->lock);
917*4882a593Smuzhiyun 
918*4882a593Smuzhiyun         if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
919*4882a593Smuzhiyun                                   h, &ops)) < 0) {
920*4882a593Smuzhiyun                 goto free_and_ret;
921*4882a593Smuzhiyun         }
922*4882a593Smuzhiyun 
923*4882a593Smuzhiyun 	*rchip = h;
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun 	return 0;
926*4882a593Smuzhiyun 
927*4882a593Smuzhiyun free_and_ret:
928*4882a593Smuzhiyun 	snd_harmony_free(h);
929*4882a593Smuzhiyun 	return err;
930*4882a593Smuzhiyun }
931*4882a593Smuzhiyun 
932*4882a593Smuzhiyun static int __init
snd_harmony_probe(struct parisc_device * padev)933*4882a593Smuzhiyun snd_harmony_probe(struct parisc_device *padev)
934*4882a593Smuzhiyun {
935*4882a593Smuzhiyun 	int err;
936*4882a593Smuzhiyun 	struct snd_card *card;
937*4882a593Smuzhiyun 	struct snd_harmony *h;
938*4882a593Smuzhiyun 
939*4882a593Smuzhiyun 	err = snd_card_new(&padev->dev, index, id, THIS_MODULE, 0, &card);
940*4882a593Smuzhiyun 	if (err < 0)
941*4882a593Smuzhiyun 		return err;
942*4882a593Smuzhiyun 
943*4882a593Smuzhiyun 	err = snd_harmony_create(card, padev, &h);
944*4882a593Smuzhiyun 	if (err < 0)
945*4882a593Smuzhiyun 		goto free_and_ret;
946*4882a593Smuzhiyun 
947*4882a593Smuzhiyun 	err = snd_harmony_pcm_init(h);
948*4882a593Smuzhiyun 	if (err < 0)
949*4882a593Smuzhiyun 		goto free_and_ret;
950*4882a593Smuzhiyun 
951*4882a593Smuzhiyun 	err = snd_harmony_mixer_init(h);
952*4882a593Smuzhiyun 	if (err < 0)
953*4882a593Smuzhiyun 		goto free_and_ret;
954*4882a593Smuzhiyun 
955*4882a593Smuzhiyun 	strcpy(card->driver, "harmony");
956*4882a593Smuzhiyun 	strcpy(card->shortname, "Harmony");
957*4882a593Smuzhiyun 	sprintf(card->longname, "%s at 0x%lx, irq %i",
958*4882a593Smuzhiyun 		card->shortname, h->hpa, h->irq);
959*4882a593Smuzhiyun 
960*4882a593Smuzhiyun 	err = snd_card_register(card);
961*4882a593Smuzhiyun 	if (err < 0)
962*4882a593Smuzhiyun 		goto free_and_ret;
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 	parisc_set_drvdata(padev, card);
965*4882a593Smuzhiyun 	return 0;
966*4882a593Smuzhiyun 
967*4882a593Smuzhiyun free_and_ret:
968*4882a593Smuzhiyun 	snd_card_free(card);
969*4882a593Smuzhiyun 	return err;
970*4882a593Smuzhiyun }
971*4882a593Smuzhiyun 
972*4882a593Smuzhiyun static int __exit
snd_harmony_remove(struct parisc_device * padev)973*4882a593Smuzhiyun snd_harmony_remove(struct parisc_device *padev)
974*4882a593Smuzhiyun {
975*4882a593Smuzhiyun 	snd_card_free(parisc_get_drvdata(padev));
976*4882a593Smuzhiyun 	return 0;
977*4882a593Smuzhiyun }
978*4882a593Smuzhiyun 
979*4882a593Smuzhiyun static struct parisc_driver snd_harmony_driver __refdata = {
980*4882a593Smuzhiyun 	.name = "harmony",
981*4882a593Smuzhiyun 	.id_table = snd_harmony_devtable,
982*4882a593Smuzhiyun 	.probe = snd_harmony_probe,
983*4882a593Smuzhiyun 	.remove = __exit_p(snd_harmony_remove),
984*4882a593Smuzhiyun };
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun static int __init
alsa_harmony_init(void)987*4882a593Smuzhiyun alsa_harmony_init(void)
988*4882a593Smuzhiyun {
989*4882a593Smuzhiyun 	return register_parisc_driver(&snd_harmony_driver);
990*4882a593Smuzhiyun }
991*4882a593Smuzhiyun 
992*4882a593Smuzhiyun static void __exit
alsa_harmony_fini(void)993*4882a593Smuzhiyun alsa_harmony_fini(void)
994*4882a593Smuzhiyun {
995*4882a593Smuzhiyun 	unregister_parisc_driver(&snd_harmony_driver);
996*4882a593Smuzhiyun }
997*4882a593Smuzhiyun 
998*4882a593Smuzhiyun MODULE_LICENSE("GPL");
999*4882a593Smuzhiyun MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
1000*4882a593Smuzhiyun MODULE_DESCRIPTION("Harmony sound driver");
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun module_init(alsa_harmony_init);
1003*4882a593Smuzhiyun module_exit(alsa_harmony_fini);
1004