Blame view

sound/parisc/harmony.c 25.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  /* Hewlett-Packard Harmony audio driver
   *
   *   This is a driver for the Harmony audio chipset found
   *   on the LASI ASIC of various early HP PA-RISC workstations.
   *
   *   Copyright (C) 2004, Kyle McMartin <kyle@{debian.org,parisc-linux.org}>
   *
   *     Based on the previous Harmony incarnations by,
   *       Copyright 2000 (c) Linuxcare Canada, Alex deVries
   *       Copyright 2000-2003 (c) Helge Deller
   *       Copyright 2001 (c) Matthieu Delahaye
   *       Copyright 2001 (c) Jean-Christophe Vaugeois
   *       Copyright 2003 (c) Laurent Canet
   *       Copyright 2004 (c) Stuart Brady
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License, version 2, as
   *   published by the Free Software Foundation.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   *
   * Notes:
   *   - graveyard and silence buffers last for lifetime of
   *     the driver. playback and capture buffers are allocated
   *     per _open()/_close().
   * 
   * TODO:
   *
   */
  
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/time.h>
  #include <linux/wait.h>
  #include <linux/delay.h>
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/spinlock.h>
  #include <linux/dma-mapping.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
55
56
57
58
  #include <sound/core.h>
  #include <sound/pcm.h>
  #include <sound/control.h>
  #include <sound/rawmidi.h>
  #include <sound/initval.h>
  #include <sound/info.h>
  
  #include <asm/io.h>
  #include <asm/hardware.h>
  #include <asm/parisc-device.h>
  
  #include "harmony.h"
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
59
60
61
62
63
64
  static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
  static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
  module_param(index, int, 0444);
  MODULE_PARM_DESC(index, "Index value for Harmony driver.");
  module_param(id, charp, 0444);
  MODULE_PARM_DESC(id, "ID string for Harmony driver.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  static struct parisc_device_id snd_harmony_devtable[] = {
  	/* bushmaster / flounder */
  	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, 
  	/* 712 / 715 */
  	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, 
  	/* pace */
  	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, 
  	/* outfield / coral II */
  	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F },
  	{ 0, }
  };
  
  MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);
  
  #define NAME "harmony"
  #define PFX  NAME ": "
  
  static unsigned int snd_harmony_rates[] = {
  	5512, 6615, 8000, 9600,
  	11025, 16000, 18900, 22050,
  	27428, 32000, 33075, 37800,
  	44100, 48000
  };
  
  static unsigned int rate_bits[14] = {
  	HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,
  	HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,
  	HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,
  	HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ,
  	HARMONY_SR_44KHZ, HARMONY_SR_48KHZ
  };
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
96
  static struct snd_pcm_hw_constraint_list hw_constraint_rates = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
  	.count = ARRAY_SIZE(snd_harmony_rates),
  	.list = snd_harmony_rates,
  	.mask = 0,
  };
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
101
102
  static inline unsigned long
  harmony_read(struct snd_harmony *h, unsigned r)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
  {
  	return __raw_readl(h->iobase + r);
  }
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
106
107
  static inline void
  harmony_write(struct snd_harmony *h, unsigned r, unsigned long v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
  {
  	__raw_writel(v, h->iobase + r);
  }
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
111
112
  static inline void
  harmony_wait_for_control(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
  {
  	while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ;
  }
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
116
117
  static inline void
  harmony_reset(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
  {
  	harmony_write(h, HARMONY_RESET, 1);
  	mdelay(50);
  	harmony_write(h, HARMONY_RESET, 0);
  }
  
  static void
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
125
  harmony_disable_interrupts(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
131
132
133
134
  {
  	u32 dstatus;
  	harmony_wait_for_control(h);
  	dstatus = harmony_read(h, HARMONY_DSTATUS);
  	dstatus &= ~HARMONY_DSTATUS_IE;
  	harmony_write(h, HARMONY_DSTATUS, dstatus);
  }
  
  static void
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
135
  harmony_enable_interrupts(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
142
143
144
  {
  	u32 dstatus;
  	harmony_wait_for_control(h);
  	dstatus = harmony_read(h, HARMONY_DSTATUS);
  	dstatus |= HARMONY_DSTATUS_IE;
  	harmony_write(h, HARMONY_DSTATUS, dstatus);
  }
  
  static void
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
145
  harmony_mute(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
150
151
152
153
154
155
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&h->mixer_lock, flags);
  	harmony_wait_for_control(h);
  	harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE);
  	spin_unlock_irqrestore(&h->mixer_lock, flags);
  }
  
  static void
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
156
  harmony_unmute(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
163
164
165
166
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&h->mixer_lock, flags);
  	harmony_wait_for_control(h);
  	harmony_write(h, HARMONY_GAINCTL, h->st.gain);
  	spin_unlock_irqrestore(&h->mixer_lock, flags);
  }
  
  static void
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
167
  harmony_set_control(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  {
  	u32 ctrl;
  	unsigned long flags;
  
  	spin_lock_irqsave(&h->lock, flags);
  
  	ctrl = (HARMONY_CNTL_C      |
  		(h->st.format << 6) |
  		(h->st.stereo << 5) |
  		(h->st.rate));
  
  	harmony_wait_for_control(h);
  	harmony_write(h, HARMONY_CNTL, ctrl);
  
  	spin_unlock_irqrestore(&h->lock, flags);
  }
  
  static irqreturn_t
7d12e780e   David Howells   IRQ: Maintain reg...
186
  snd_harmony_interrupt(int irq, void *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
  {
  	u32 dstatus;
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
189
  	struct snd_harmony *h = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
197
  
  	spin_lock(&h->lock);
  	harmony_disable_interrupts(h);
  	harmony_wait_for_control(h);
  	dstatus = harmony_read(h, HARMONY_DSTATUS);
  	spin_unlock(&h->lock);
  
  	if (dstatus & HARMONY_DSTATUS_PN) {
3a1656801   Stuart Brady   [PARISC] Update h...
198
  		if (h->psubs && h->st.playing) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  			spin_lock(&h->lock);
  			h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
  			h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
  
  			harmony_write(h, HARMONY_PNXTADD, 
  				      h->pbuf.addr + h->pbuf.buf);
  			h->stats.play_intr++;
  			spin_unlock(&h->lock);
                          snd_pcm_period_elapsed(h->psubs);
  		} else {
  			spin_lock(&h->lock);
  			harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
  			h->stats.silence_intr++;
  			spin_unlock(&h->lock);
  		}
  	}
  
  	if (dstatus & HARMONY_DSTATUS_RN) {
3a1656801   Stuart Brady   [PARISC] Update h...
217
  		if (h->csubs && h->st.capturing) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  			spin_lock(&h->lock);
  			h->cbuf.buf += h->cbuf.count;
  			h->cbuf.buf %= h->cbuf.size;
  
  			harmony_write(h, HARMONY_RNXTADD,
  				      h->cbuf.addr + h->cbuf.buf);
  			h->stats.rec_intr++;
  			spin_unlock(&h->lock);
                          snd_pcm_period_elapsed(h->csubs);
  		} else {
  			spin_lock(&h->lock);
  			harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
  			h->stats.graveyard_intr++;
  			spin_unlock(&h->lock);
  		}
  	}
  
  	spin_lock(&h->lock);
  	harmony_enable_interrupts(h);
  	spin_unlock(&h->lock);
  
  	return IRQ_HANDLED;
  }
  
  static unsigned int 
  snd_harmony_rate_bits(int rate)
  {
  	unsigned int i;
  	
  	for (i = 0; i < ARRAY_SIZE(snd_harmony_rates); i++)
  		if (snd_harmony_rates[i] == rate)
  			return rate_bits[i];
  
  	return HARMONY_SR_44KHZ;
  }
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
253
  static struct snd_pcm_hardware snd_harmony_playback =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  {
  	.info =	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 
  		 SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
  		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
  	.formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
  		    SNDRV_PCM_FMTBIT_A_LAW),
  	.rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
  		  SNDRV_PCM_RATE_KNOT),
  	.rate_min = 5512,
  	.rate_max = 48000,
  	.channels_min =	1,
  	.channels_max =	2,
  	.buffer_bytes_max = MAX_BUF_SIZE,
  	.period_bytes_min = BUF_SIZE,
  	.period_bytes_max = BUF_SIZE,
  	.periods_min = 1,
  	.periods_max = MAX_BUFS,
  	.fifo_size = 0,
  };
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
273
  static struct snd_pcm_hardware snd_harmony_capture =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  {
          .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                   SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
                   SNDRV_PCM_INFO_BLOCK_TRANSFER),
          .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |
                      SNDRV_PCM_FMTBIT_A_LAW),
          .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |
  		  SNDRV_PCM_RATE_KNOT),
          .rate_min = 5512,
          .rate_max = 48000,
          .channels_min = 1,
          .channels_max = 2,
          .buffer_bytes_max = MAX_BUF_SIZE,
          .period_bytes_min = BUF_SIZE,
          .period_bytes_max = BUF_SIZE,
          .periods_min = 1,
          .periods_max = MAX_BUFS,
          .fifo_size = 0,
  };
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
295
  snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
297
  	struct snd_harmony *h = snd_pcm_substream_chip(ss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
  
  	if (h->st.capturing)
  		return -EBUSY;
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
301
  	spin_lock(&h->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
311
312
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
  		h->st.playing = 1;
  		harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr);
  		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
  		harmony_unmute(h);
  		harmony_enable_interrupts(h);
  		break;
  	case SNDRV_PCM_TRIGGER_STOP:
  		h->st.playing = 0;
  		harmony_mute(h);
3a1656801   Stuart Brady   [PARISC] Update h...
313
  		harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
319
  		harmony_disable_interrupts(h);
  		break;
  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  	case SNDRV_PCM_TRIGGER_SUSPEND:
  	default:
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
320
  		spin_unlock(&h->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
  		snd_BUG();
  		return -EINVAL;
  	}
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
324
  	spin_unlock(&h->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
328
329
  	
  	return 0;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
330
  snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
332
          struct snd_harmony *h = snd_pcm_substream_chip(ss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
  
  	if (h->st.playing)
  		return -EBUSY;
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
336
  	spin_lock(&h->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
339
340
341
342
343
344
345
346
          switch (cmd) {
          case SNDRV_PCM_TRIGGER_START:
  		h->st.capturing = 1;
                  harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
                  harmony_write(h, HARMONY_RNXTADD, h->cbuf.addr);
  		harmony_unmute(h);
                  harmony_enable_interrupts(h);
  		break;
          case SNDRV_PCM_TRIGGER_STOP:
  		h->st.capturing = 0;
3a1656801   Stuart Brady   [PARISC] Update h...
347
348
349
  		harmony_mute(h);
  		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
  		harmony_disable_interrupts(h);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
354
  		break;
          case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
          case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
          case SNDRV_PCM_TRIGGER_SUSPEND:
  	default:
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
355
  		spin_unlock(&h->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
  		snd_BUG();
                  return -EINVAL;
          }
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
359
  	spin_unlock(&h->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
  		
          return 0;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
365
  snd_harmony_set_data_format(struct snd_harmony *h, int fmt, int force)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  {
  	int o = h->st.format;
  	int n;
  
  	switch(fmt) {
  	case SNDRV_PCM_FORMAT_S16_BE:
  		n = HARMONY_DF_16BIT_LINEAR;
  		break;
  	case SNDRV_PCM_FORMAT_A_LAW:
  		n = HARMONY_DF_8BIT_ALAW;
  		break;
  	case SNDRV_PCM_FORMAT_MU_LAW:
  		n = HARMONY_DF_8BIT_ULAW;
  		break;
  	default:
  		n = HARMONY_DF_16BIT_LINEAR;
  		break;
  	}
  
  	if (force || o != n) {
  		snd_pcm_format_set_silence(fmt, h->sdma.area, SILENCE_BUFSZ / 
  					   (snd_pcm_format_physical_width(fmt)
  					    / 8));
  	}
  
  	return n;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
395
  snd_harmony_playback_prepare(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
397
398
  	struct snd_harmony *h = snd_pcm_substream_chip(ss);
  	struct snd_pcm_runtime *rt = ss->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
400
401
402
403
404
  	
  	if (h->st.capturing)
  		return -EBUSY;
  	
  	h->pbuf.size = snd_pcm_lib_buffer_bytes(ss);
  	h->pbuf.count = snd_pcm_lib_period_bytes(ss);
3a1656801   Stuart Brady   [PARISC] Update h...
405
406
  	if (h->pbuf.buf >= h->pbuf.size)
  		h->pbuf.buf = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  	h->st.playing = 0;
  
  	h->st.rate = snd_harmony_rate_bits(rt->rate);
  	h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
  	
  	if (rt->channels == 2)
  		h->st.stereo = HARMONY_SS_STEREO;
  	else
  		h->st.stereo = HARMONY_SS_MONO;
  
  	harmony_set_control(h);
  
  	h->pbuf.addr = rt->dma_addr;
  
  	return 0;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
425
  snd_harmony_capture_prepare(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
427
428
          struct snd_harmony *h = snd_pcm_substream_chip(ss);
          struct snd_pcm_runtime *rt = ss->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
433
434
  
  	if (h->st.playing)
  		return -EBUSY;
  
          h->cbuf.size = snd_pcm_lib_buffer_bytes(ss);
          h->cbuf.count = snd_pcm_lib_period_bytes(ss);
3a1656801   Stuart Brady   [PARISC] Update h...
435
436
  	if (h->cbuf.buf >= h->cbuf.size)
  	        h->cbuf.buf = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
  	h->st.capturing = 0;
  
          h->st.rate = snd_harmony_rate_bits(rt->rate);
          h->st.format = snd_harmony_set_data_format(h, rt->format, 0);
  
          if (rt->channels == 2)
                  h->st.stereo = HARMONY_SS_STEREO;
          else
                  h->st.stereo = HARMONY_SS_MONO;
  
          harmony_set_control(h);
  
          h->cbuf.addr = rt->dma_addr;
  
          return 0;
  }
  
  static snd_pcm_uframes_t 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
455
  snd_harmony_playback_pointer(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
457
458
  	struct snd_pcm_runtime *rt = ss->runtime;
  	struct snd_harmony *h = snd_pcm_substream_chip(ss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
  	unsigned long pcuradd;
  	unsigned long played;
  
  	if (!(h->st.playing) || (h->psubs == NULL)) 
  		return 0;
  
  	if ((h->pbuf.addr == 0) || (h->pbuf.size == 0))
  		return 0;
  	
  	pcuradd = harmony_read(h, HARMONY_PCURADD);
  	played = pcuradd - h->pbuf.addr;
  
  #ifdef HARMONY_DEBUG
  	printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes
  ", 
  	       pcuradd, h->pbuf.addr, played);	
  #endif
  
  	if (pcuradd > h->pbuf.addr + h->pbuf.size) {
  		return 0;
  	}
  
  	return bytes_to_frames(rt, played);
  }
  
  static snd_pcm_uframes_t
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
485
  snd_harmony_capture_pointer(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
487
488
          struct snd_pcm_runtime *rt = ss->runtime;
          struct snd_harmony *h = snd_pcm_substream_chip(ss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
          unsigned long rcuradd;
          unsigned long caught;
  
          if (!(h->st.capturing) || (h->csubs == NULL))
                  return 0;
  
          if ((h->cbuf.addr == 0) || (h->cbuf.size == 0))
                  return 0;
  
          rcuradd = harmony_read(h, HARMONY_RCURADD);
          caught = rcuradd - h->cbuf.addr;
  
  #ifdef HARMONY_DEBUG
          printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes
  ",
                 rcuradd, h->cbuf.addr, caught);
  #endif
  
          if (rcuradd > h->cbuf.addr + h->cbuf.size) {
  		return 0;
  	}
  
          return bytes_to_frames(rt, caught);
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
515
  snd_harmony_playback_open(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
517
518
  	struct snd_harmony *h = snd_pcm_substream_chip(ss);
  	struct snd_pcm_runtime *rt = ss->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
  	int err;
  	
  	h->psubs = ss;
  	rt->hw = snd_harmony_playback;
  	snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, 
  				   &hw_constraint_rates);
  	
  	err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
  	if (err < 0)
  		return err;
  	
  	return 0;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
534
  snd_harmony_capture_open(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
536
537
          struct snd_harmony *h = snd_pcm_substream_chip(ss);
          struct snd_pcm_runtime *rt = ss->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
          int err;
  
          h->csubs = ss;
          rt->hw = snd_harmony_capture;
          snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE,
                                     &hw_constraint_rates);
  
          err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
          if (err < 0)
                  return err;
  
          return 0;
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
553
  snd_harmony_playback_close(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
555
  	struct snd_harmony *h = snd_pcm_substream_chip(ss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
559
560
  	h->psubs = NULL;
  	return 0;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
561
  snd_harmony_capture_close(struct snd_pcm_substream *ss)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
563
          struct snd_harmony *h = snd_pcm_substream_chip(ss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
          h->csubs = NULL;
          return 0;
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
569
570
  snd_harmony_hw_params(struct snd_pcm_substream *ss,
  		      struct snd_pcm_hw_params *hw)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
  {
  	int err;
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
573
  	struct snd_harmony *h = snd_pcm_substream_chip(ss);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
581
582
  	
  	err = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw));
  	if (err > 0 && h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS)
  		ss->runtime->dma_addr = __pa(ss->runtime->dma_area);
  	
  	return err;
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
583
  snd_harmony_hw_free(struct snd_pcm_substream *ss) 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
  {
  	return snd_pcm_lib_free_pages(ss);
  }
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
587
  static struct snd_pcm_ops snd_harmony_playback_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
591
592
593
594
595
596
  	.open =	snd_harmony_playback_open,
  	.close = snd_harmony_playback_close,
  	.ioctl = snd_pcm_lib_ioctl,
  	.hw_params = snd_harmony_hw_params,
  	.hw_free = snd_harmony_hw_free,
  	.prepare = snd_harmony_playback_prepare,
  	.trigger = snd_harmony_playback_trigger,
   	.pointer = snd_harmony_playback_pointer,
  };
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
597
  static struct snd_pcm_ops snd_harmony_capture_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
603
604
605
606
607
608
          .open = snd_harmony_capture_open,
          .close = snd_harmony_capture_close,
          .ioctl = snd_pcm_lib_ioctl,
          .hw_params = snd_harmony_hw_params,
          .hw_free = snd_harmony_hw_free,
          .prepare = snd_harmony_capture_prepare,
          .trigger = snd_harmony_capture_trigger,
          .pointer = snd_harmony_capture_pointer,
  };
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
609
  snd_harmony_pcm_init(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
611
  	struct snd_pcm *pcm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  	int err;
e8e0929d7   Julia Lawall   ALSA: sound/paris...
613
614
  	if (snd_BUG_ON(!h))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  	harmony_disable_interrupts(h);
  	
     	err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm);
  	if (err < 0)
  		return err;
  	
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 
  			&snd_harmony_playback_ops);
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
  			&snd_harmony_capture_ops);
  
  	pcm->private_data = h;
  	pcm->info_flags = 0;
  	strcpy(pcm->name, "harmony");
  	h->pcm = pcm;
  
  	h->psubs = NULL;
  	h->csubs = NULL;
  	
  	/* initialize graveyard buffer */
  	h->dma.type = SNDRV_DMA_TYPE_DEV;
  	h->dma.dev = &h->dev->dev;
  	err = snd_dma_alloc_pages(h->dma.type,
  				  h->dma.dev,
  				  BUF_SIZE*GRAVEYARD_BUFS,
  				  &h->gdma);
  	if (err < 0) {
  		printk(KERN_ERR PFX "cannot allocate graveyard buffer!
  ");
  		return err;
  	}
  	
  	/* initialize silence buffers */
  	err = snd_dma_alloc_pages(h->dma.type,
  				  h->dma.dev,
  				  BUF_SIZE*SILENCE_BUFS,
  				  &h->sdma);
  	if (err < 0) {
  		printk(KERN_ERR PFX "cannot allocate silence buffer!
  ");
  		return err;
  	}
  
  	/* pre-allocate space for DMA */
  	err = snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type,
  						    h->dma.dev,
  						    MAX_BUF_SIZE, 
  						    MAX_BUF_SIZE);
  	if (err < 0) {
  		printk(KERN_ERR PFX "buffer allocation error: %d
  ", err);
  		return err;
  	}
  
  	h->st.format = snd_harmony_set_data_format(h,
  		SNDRV_PCM_FORMAT_S16_BE, 1);
  
  	return 0;
  }
  
  static void 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
676
  snd_harmony_set_new_gain(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
680
681
682
  {
   	harmony_wait_for_control(h);
  	harmony_write(h, HARMONY_GAINCTL, h->st.gain);
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
683
684
  snd_harmony_mixercontrol_info(struct snd_kcontrol *kc, 
  			      struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
  {
  	int mask = (kc->private_value >> 16) & 0xff;
  	int left_shift = (kc->private_value) & 0xff;
  	int right_shift = (kc->private_value >> 8) & 0xff;
  	
  	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : 
  		       SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = left_shift == right_shift ? 1 : 2;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = mask;
  
  	return 0;
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
700
701
  snd_harmony_volume_get(struct snd_kcontrol *kc, 
  		       struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
703
  	struct snd_harmony *h = snd_kcontrol_chip(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
706
707
708
  	int shift_left = (kc->private_value) & 0xff;
  	int shift_right = (kc->private_value >> 8) & 0xff;
  	int mask = (kc->private_value >> 16) & 0xff;
  	int invert = (kc->private_value >> 24) & 0xff;
  	int left, right;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
  	
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
710
  	spin_lock_irq(&h->mixer_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
  
  	left = (h->st.gain >> shift_left) & mask;
  	right = (h->st.gain >> shift_right) & mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
716
717
  	if (invert) {
  		left = mask - left;
  		right = mask - right;
  	}
3a1656801   Stuart Brady   [PARISC] Update h...
718
  	
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
  	ucontrol->value.integer.value[0] = left;
3a1656801   Stuart Brady   [PARISC] Update h...
720
721
  	if (shift_left != shift_right)
  		ucontrol->value.integer.value[1] = right;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722

03f9ae250   Takashi Iwai   [ALSA] harmony - ...
723
  	spin_unlock_irq(&h->mixer_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
725
726
727
728
  
  	return 0;
  }  
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
729
730
  snd_harmony_volume_put(struct snd_kcontrol *kc, 
  		       struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
732
  	struct snd_harmony *h = snd_kcontrol_chip(kc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
736
737
738
  	int shift_left = (kc->private_value) & 0xff;
  	int shift_right = (kc->private_value >> 8) & 0xff;
  	int mask = (kc->private_value >> 16) & 0xff;
  	int invert = (kc->private_value >> 24) & 0xff;
  	int left, right;
  	int old_gain = h->st.gain;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
  	
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
740
  	spin_lock_irq(&h->mixer_lock);
3a1656801   Stuart Brady   [PARISC] Update h...
741

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
  	left = ucontrol->value.integer.value[0] & mask;
3a1656801   Stuart Brady   [PARISC] Update h...
743
  	if (invert)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
  		left = mask - left;
3a1656801   Stuart Brady   [PARISC] Update h...
745
746
747
748
749
750
751
752
753
  	h->st.gain &= ~( (mask << shift_left ) );
   	h->st.gain |= (left << shift_left);
  
  	if (shift_left != shift_right) {
  		right = ucontrol->value.integer.value[1] & mask;
  		if (invert)
  			right = mask - right;
  		h->st.gain &= ~( (mask << shift_right) );
  		h->st.gain |= (right << shift_right);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
  	}
3a1656801   Stuart Brady   [PARISC] Update h...
755
756
  
  	snd_harmony_set_new_gain(h);
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
757
  	spin_unlock_irq(&h->mixer_lock);
3a1656801   Stuart Brady   [PARISC] Update h...
758
759
760
761
762
  	
  	return h->st.gain != old_gain;
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
763
764
  snd_harmony_captureroute_info(struct snd_kcontrol *kc, 
  			      struct snd_ctl_elem_info *uinfo)
3a1656801   Stuart Brady   [PARISC] Update h...
765
766
767
768
769
770
771
772
773
774
775
776
777
  {
  	static char *texts[2] = { "Line", "Mic" };
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
  	uinfo->count = 1;
  	uinfo->value.enumerated.items = 2;
  	if (uinfo->value.enumerated.item > 1)
  		uinfo->value.enumerated.item = 1;
  	strcpy(uinfo->value.enumerated.name,
  	       texts[uinfo->value.enumerated.item]);
  	return 0;
  }
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
778
779
  snd_harmony_captureroute_get(struct snd_kcontrol *kc, 
  			     struct snd_ctl_elem_value *ucontrol)
3a1656801   Stuart Brady   [PARISC] Update h...
780
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
781
  	struct snd_harmony *h = snd_kcontrol_chip(kc);
3a1656801   Stuart Brady   [PARISC] Update h...
782
  	int value;
3a1656801   Stuart Brady   [PARISC] Update h...
783
  	
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
784
  	spin_lock_irq(&h->mixer_lock);
3a1656801   Stuart Brady   [PARISC] Update h...
785
786
787
  
  	value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1;
  	ucontrol->value.enumerated.item[0] = value;
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
788
  	spin_unlock_irq(&h->mixer_lock);
3a1656801   Stuart Brady   [PARISC] Update h...
789
790
791
792
793
  
  	return 0;
  }  
  
  static int 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
794
795
  snd_harmony_captureroute_put(struct snd_kcontrol *kc, 
  			     struct snd_ctl_elem_value *ucontrol)
3a1656801   Stuart Brady   [PARISC] Update h...
796
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
797
  	struct snd_harmony *h = snd_kcontrol_chip(kc);
3a1656801   Stuart Brady   [PARISC] Update h...
798
799
  	int value;
  	int old_gain = h->st.gain;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
  	
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
801
  	spin_lock_irq(&h->mixer_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802

3a1656801   Stuart Brady   [PARISC] Update h...
803
804
805
  	value = ucontrol->value.enumerated.item[0] & 1;
  	h->st.gain &= ~HARMONY_GAIN_IS_MASK;
   	h->st.gain |= value << HARMONY_GAIN_IS_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
  	snd_harmony_set_new_gain(h);
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
807
  	spin_unlock_irq(&h->mixer_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
  	
3a1656801   Stuart Brady   [PARISC] Update h...
809
  	return h->st.gain != old_gain;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  }
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
811
  #define HARMONY_CONTROLS	ARRAY_SIZE(snd_harmony_controls)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
815
816
817
818
  
  #define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \
  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,                \
    .info = snd_harmony_mixercontrol_info,                             \
    .get = snd_harmony_volume_get, .put = snd_harmony_volume_put,      \
    .private_value = ((left_shift) | ((right_shift) << 8) |            \
                     ((mask) << 16) | ((invert) << 24)) }
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
819
  static struct snd_kcontrol_new snd_harmony_controls[] = {
3a1656801   Stuart Brady   [PARISC] Update h...
820
  	HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
822
823
  		       HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1),
  	HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT,
  		       HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0),
3a1656801   Stuart Brady   [PARISC] Update h...
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
  	HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT,
  		       HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1),
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  		.name = "Input Route",
  		.info = snd_harmony_captureroute_info,
  		.get = snd_harmony_captureroute_get,
  		.put = snd_harmony_captureroute_put
  	},
  	HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT,
  		       HARMONY_GAIN_SE_SHIFT, 1, 0),
  	HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT,
  		       HARMONY_GAIN_LE_SHIFT, 1, 0),
  	HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT,
  		       HARMONY_GAIN_HE_SHIFT, 1, 0),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
  };
992378a06   Helge Deller   [PARISC] fix sect...
840
  static void __devinit
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
841
  snd_harmony_mixer_reset(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
844
845
846
847
  {
  	harmony_mute(h);
  	harmony_reset(h);
  	h->st.gain = HARMONY_GAIN_DEFAULT;
  	harmony_unmute(h);
  }
992378a06   Helge Deller   [PARISC] fix sect...
848
  static int __devinit
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
849
  snd_harmony_mixer_init(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
  {
e8e0929d7   Julia Lawall   ALSA: sound/paris...
851
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
  	int idx, err;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
853
  	if (snd_BUG_ON(!h))
c86a456b2   Takashi Iwai   ALSA: harmony - f...
854
  		return -EINVAL;
e8e0929d7   Julia Lawall   ALSA: sound/paris...
855
  	card = h->card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
  	strcpy(card->mixername, "Harmony Gain control interface");
  
  	for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
  		err = snd_ctl_add(card, 
  				  snd_ctl_new1(&snd_harmony_controls[idx], h));
  		if (err < 0)
  			return err;
  	}
  	
  	snd_harmony_mixer_reset(h);
  
  	return 0;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
871
  snd_harmony_free(struct snd_harmony *h)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
  {
          if (h->gdma.addr)
                  snd_dma_free_pages(&h->gdma);
          if (h->sdma.addr)
                  snd_dma_free_pages(&h->sdma);
  
  	if (h->irq >= 0)
  		free_irq(h->irq, h);
  
  	if (h->iobase)
  		iounmap(h->iobase);
  
  	parisc_set_drvdata(h->dev, NULL);
  
  	kfree(h);
  	return 0;
  }
  
  static int
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
891
  snd_harmony_dev_free(struct snd_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
893
  	struct snd_harmony *h = dev->device_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
896
897
  	return snd_harmony_free(h);
  }
  
  static int __devinit
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
898
  snd_harmony_create(struct snd_card *card, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
  		   struct parisc_device *padev, 
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
900
  		   struct snd_harmony **rchip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
902
  {
  	int err;
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
903
904
  	struct snd_harmony *h;
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
906
907
908
  		.dev_free = snd_harmony_dev_free,
  	};
  
  	*rchip = NULL;
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
909
  	h = kzalloc(sizeof(*h), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910
911
  	if (h == NULL)
  		return -ENOMEM;
53f01bba4   Matthew Wilcox   [PARISC] Convert ...
912
  	h->hpa = padev->hpa.start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913
914
  	h->card = card;
  	h->dev = padev;
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
915
  	h->irq = -1;
53f01bba4   Matthew Wilcox   [PARISC] Convert ...
916
  	h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
918
919
  	if (h->iobase == NULL) {
  		printk(KERN_ERR PFX "unable to remap hpa 0x%lx
  ",
b04b4f786   Alexander Beregalov   ALSA: parisc/harm...
920
  		       (unsigned long)padev->hpa.start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
922
923
924
  		err = -EBUSY;
  		goto free_and_ret;
  	}
  		
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
925
  	err = request_irq(padev->irq, snd_harmony_interrupt, 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
927
928
  			  "harmony", h);
  	if (err) {
  		printk(KERN_ERR PFX "could not obtain interrupt %d",
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
929
  		       padev->irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
931
  		goto free_and_ret;
  	}
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
932
  	h->irq = padev->irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
934
935
936
937
938
939
940
  
  	spin_lock_init(&h->mixer_lock);
  	spin_lock_init(&h->lock);
  
          if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
                                    h, &ops)) < 0) {
                  goto free_and_ret;
          }
a76af199d   Takashi Iwai   [ALSA] Add snd_ca...
941
  	snd_card_set_dev(card, &padev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
944
945
946
947
948
949
950
951
952
953
954
  	*rchip = h;
  
  	return 0;
  
  free_and_ret:
  	snd_harmony_free(h);
  	return err;
  }
  
  static int __devinit
  snd_harmony_probe(struct parisc_device *padev)
  {
  	int err;
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
955
956
  	struct snd_card *card;
  	struct snd_harmony *h;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957

bd7dd77c2   Takashi Iwai   ALSA: Convert to ...
958
959
960
  	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
962
  
  	err = snd_harmony_create(card, padev, &h);
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
963
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964
  		goto free_and_ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
966
  
  	err = snd_harmony_pcm_init(h);
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
967
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
  		goto free_and_ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
970
  
  	err = snd_harmony_mixer_init(h);
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
971
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
  		goto free_and_ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
973
974
975
976
977
978
979
  
  	strcpy(card->driver, "harmony");
  	strcpy(card->shortname, "Harmony");
  	sprintf(card->longname, "%s at 0x%lx, irq %i",
  		card->shortname, h->hpa, h->irq);
  
  	err = snd_card_register(card);
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
980
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
  		goto free_and_ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982

03f9ae250   Takashi Iwai   [ALSA] harmony - ...
983
  	parisc_set_drvdata(padev, card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
985
986
987
988
989
990
991
992
993
  	return 0;
  
  free_and_ret:
  	snd_card_free(card);
  	return err;
  }
  
  static int __devexit
  snd_harmony_remove(struct parisc_device *padev)
  {
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
994
995
  	snd_card_free(parisc_get_drvdata(padev));
  	parisc_set_drvdata(padev, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
996
997
998
999
1000
1001
1002
  	return 0;
  }
  
  static struct parisc_driver snd_harmony_driver = {
  	.name = "harmony",
  	.id_table = snd_harmony_devtable,
  	.probe = snd_harmony_probe,
82ced6fd2   Jean Delvare   ALSA: Add missing...
1003
  	.remove = __devexit_p(snd_harmony_remove),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004
1005
1006
1007
1008
  };
  
  static int __init 
  alsa_harmony_init(void)
  {
03f9ae250   Takashi Iwai   [ALSA] harmony - ...
1009
  	return register_parisc_driver(&snd_harmony_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1010
1011
1012
1013
1014
  }
  
  static void __exit
  alsa_harmony_fini(void)
  {
67b1020d8   Takashi Iwai   [ALSA] Remove xxx...
1015
  	unregister_parisc_driver(&snd_harmony_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
1017
1018
1019
1020
1021
1022
1023
  }
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");
  MODULE_DESCRIPTION("Harmony sound driver");
  
  module_init(alsa_harmony_init);
  module_exit(alsa_harmony_fini);