988 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Hewlett-Packard Harmony audio driver3 *4 * This is a driver for the Harmony audio chipset found5 * 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 deVries11 * Copyright 2000-2003 (c) Helge Deller12 * Copyright 2001 (c) Matthieu Delahaye13 * Copyright 2001 (c) Jean-Christophe Vaugeois14 * Copyright 2003 (c) Laurent Canet15 * Copyright 2004 (c) Stuart Brady16 *17 * Notes:18 * - graveyard and silence buffers last for lifetime of19 * the driver. playback and capture buffers are allocated20 * 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 48static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */49static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */50module_param(index, int, 0444);51MODULE_PARM_DESC(index, "Index value for Harmony driver.");52module_param(id, charp, 0444);53MODULE_PARM_DESC(id, "ID string for Harmony driver.");54 55 56static 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 68MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);69 70#define NAME "harmony"71#define PFX NAME ": "72 73static const unsigned int snd_harmony_rates[] = {74 5512, 6615, 8000, 9600,75 11025, 16000, 18900, 22050,76 27428, 32000, 33075, 37800,77 44100, 4800078};79 80static 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_48KHZ86};87 88static 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 94static inline unsigned long95harmony_read(struct snd_harmony *h, unsigned r)96{97 return __raw_readl(h->iobase + r);98}99 100static inline void101harmony_write(struct snd_harmony *h, unsigned r, unsigned long v)102{103 __raw_writel(v, h->iobase + r);104}105 106static inline void107harmony_wait_for_control(struct snd_harmony *h)108{109 while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ;110}111 112static inline void113harmony_reset(struct snd_harmony *h)114{115 harmony_write(h, HARMONY_RESET, 1);116 mdelay(50);117 harmony_write(h, HARMONY_RESET, 0);118}119 120static void121harmony_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, dstatus);128}129 130static void131harmony_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, dstatus);138}139 140static void141harmony_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(&h->mixer_lock, flags);149}150 151static void152harmony_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, h->st.gain);159 spin_unlock_irqrestore(&h->mixer_lock, flags);160}161 162static void163harmony_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, ctrl);177 178 spin_unlock_irqrestore(&h->lock, flags);179}180 181static irqreturn_t182snd_harmony_interrupt(int irq, void *dev)183{184 u32 dstatus;185 struct snd_harmony *h = dev;186 187 spin_lock(&h->lock);188 harmony_disable_interrupts(h);189 harmony_wait_for_control(h);190 dstatus = harmony_read(h, HARMONY_DSTATUS);191 spin_unlock(&h->lock);192 193 if (dstatus & HARMONY_DSTATUS_PN) {194 if (h->psubs && h->st.playing) {195 spin_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 h->pbuf.addr + h->pbuf.buf);201 h->stats.play_intr++;202 spin_unlock(&h->lock);203 snd_pcm_period_elapsed(h->psubs);204 } else {205 spin_lock(&h->lock);206 harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);207 h->stats.silence_intr++;208 spin_unlock(&h->lock);209 }210 }211 212 if (dstatus & HARMONY_DSTATUS_RN) {213 if (h->csubs && h->st.capturing) {214 spin_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 h->cbuf.addr + h->cbuf.buf);220 h->stats.rec_intr++;221 spin_unlock(&h->lock);222 snd_pcm_period_elapsed(h->csubs);223 } else {224 spin_lock(&h->lock);225 harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);226 h->stats.graveyard_intr++;227 spin_unlock(&h->lock);228 }229 }230 231 spin_lock(&h->lock);232 harmony_enable_interrupts(h);233 spin_unlock(&h->lock);234 235 return IRQ_HANDLED;236}237 238static unsigned int 239snd_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 250static 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 271static 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 292static int293snd_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(&h->lock);301 switch (cmd) {302 case SNDRV_PCM_TRIGGER_START:303 h->st.playing = 1;304 harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr);305 harmony_write(h, HARMONY_RNXTADD, 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, 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(&h->lock);320 snd_BUG();321 return -EINVAL;322 }323 spin_unlock(&h->lock);324 325 return 0;326}327 328static int329snd_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(&h->lock);337 switch (cmd) {338 case SNDRV_PCM_TRIGGER_START:339 h->st.capturing = 1;340 harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);341 harmony_write(h, HARMONY_RNXTADD, 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, 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(&h->lock);356 snd_BUG();357 return -EINVAL;358 }359 spin_unlock(&h->lock);360 361 return 0;362}363 364static int365snd_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(fmt, h->sdma.area, SILENCE_BUFSZ / 387 (snd_pcm_format_physical_width(fmt)388 / 8));389 }390 391 return n;392}393 394static int395snd_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(ss);404 h->pbuf.count = snd_pcm_lib_period_bytes(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(rt->rate);410 h->st.format = snd_harmony_set_data_format(h, rt->format, 0);411 412 if (rt->channels == 2)413 h->st.stereo = HARMONY_SS_STEREO;414 else415 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 424static int425snd_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(ss);434 h->cbuf.count = snd_pcm_lib_period_bytes(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(rt->rate);440 h->st.format = snd_harmony_set_data_format(h, rt->format, 0);441 442 if (rt->channels == 2)443 h->st.stereo = HARMONY_SS_STEREO;444 else445 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 454static snd_pcm_uframes_t 455snd_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_DEBUG472 printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes\n", 473 pcuradd, h->pbuf.addr, played); 474#endif475 476 if (pcuradd > h->pbuf.addr + h->pbuf.size) {477 return 0;478 }479 480 return bytes_to_frames(rt, played);481}482 483static snd_pcm_uframes_t484snd_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_DEBUG501 printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes\n",502 rcuradd, h->cbuf.addr, caught);503#endif504 505 if (rcuradd > h->cbuf.addr + h->cbuf.size) {506 return 0;507 }508 509 return bytes_to_frames(rt, caught);510}511 512static int 513snd_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(rt, 0, SNDRV_PCM_HW_PARAM_RATE, 522 &hw_constraint_rates);523 524 err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);525 if (err < 0)526 return err;527 528 return 0;529}530 531static int532snd_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(rt, 0, SNDRV_PCM_HW_PARAM_RATE,541 &hw_constraint_rates);542 543 err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);544 if (err < 0)545 return err;546 547 return 0;548}549 550static int 551snd_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 558static int559snd_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 566static 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 574static 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 582static int 583snd_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(h->card, "harmony", 0, 1, 1, &pcm);594 if (err < 0)595 return err;596 597 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 598 &snd_harmony_playback_ops);599 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,600 &snd_harmony_capture_ops);601 602 pcm->private_data = h;603 pcm->info_flags = 0;604 strcpy(pcm->name, "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(h->dma.type,614 h->dma.dev,615 BUF_SIZE*GRAVEYARD_BUFS,616 &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(h->dma.type,624 h->dma.dev,625 BUF_SIZE*SILENCE_BUFS,626 &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, h->dma.type, 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, 1);638 639 return 0;640}641 642static void 643snd_harmony_set_new_gain(struct snd_harmony *h)644{645 harmony_wait_for_control(h);646 harmony_write(h, HARMONY_GAINCTL, h->st.gain);647}648 649static int 650snd_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 666static int 667snd_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(&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(&h->mixer_lock);691 692 return 0;693} 694 695static int 696snd_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(&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(&h->mixer_lock);726 727 return h->st.gain != old_gain;728}729 730static int 731snd_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(uinfo, 1, 2, texts);737}738 739static int 740snd_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(&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(&h->mixer_lock);752 753 return 0;754} 755 756static int 757snd_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(&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(&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 786static 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_put799 },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 808static void809snd_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 817static int818snd_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(card->mixername, "Harmony Gain control interface");827 828 for (idx = 0; idx < HARMONY_CONTROLS; idx++) {829 err = snd_ctl_add(card, 830 snd_ctl_new1(&snd_harmony_controls[idx], h));831 if (err < 0)832 return err;833 }834 835 snd_harmony_mixer_reset(h);836 837 return 0;838}839 840static int841snd_harmony_free(struct snd_harmony *h)842{843 if (h->gdma.addr)844 snd_dma_free_pages(&h->gdma);845 if (h->sdma.addr)846 snd_dma_free_pages(&h->sdma);847 848 if (h->irq >= 0)849 free_irq(h->irq, h);850 851 iounmap(h->iobase);852 kfree(h);853 return 0;854}855 856static int857snd_harmony_dev_free(struct snd_device *dev)858{859 struct snd_harmony *h = dev->device_data;860 return snd_harmony_free(h);861}862 863static int864snd_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(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(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(padev->irq, snd_harmony_interrupt, 0,893 "harmony", 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, SNDRV_DEV_LOWLEVEL, h, &ops);905 if (err < 0)906 goto free_and_ret;907 908 *rchip = h;909 910 return 0;911 912free_and_ret:913 snd_harmony_free(h);914 return err;915}916 917static int __init918snd_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(&padev->dev, index, id, THIS_MODULE, 0, &card);925 if (err < 0)926 return err;927 928 err = snd_harmony_create(card, padev, &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(card->driver, "harmony");941 strcpy(card->shortname, "Harmony");942 sprintf(card->longname, "%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 952free_and_ret:953 snd_card_free(card);954 return err;955}956 957static void __exit958snd_harmony_remove(struct parisc_device *padev)959{960 snd_card_free(parisc_get_drvdata(padev));961}962 963static 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 970static int __init 971alsa_harmony_init(void)972{973 return register_parisc_driver(&snd_harmony_driver);974}975 976static void __exit977alsa_harmony_fini(void)978{979 unregister_parisc_driver(&snd_harmony_driver);980}981 982MODULE_LICENSE("GPL");983MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");984MODULE_DESCRIPTION("Harmony sound driver");985 986module_init(alsa_harmony_init);987module_exit(alsa_harmony_fini);988