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