diff options
| -rw-r--r-- | Documentation/sound/alsa/ALSA-Configuration.txt | 2 | ||||
| -rw-r--r-- | sound/core/seq/seq_timer.c | 27 | ||||
| -rw-r--r-- | sound/pci/Kconfig | 1 | ||||
| -rw-r--r-- | sound/pci/oxygen/Makefile | 2 | ||||
| -rw-r--r-- | sound/pci/oxygen/virtuoso.c | 3 | ||||
| -rw-r--r-- | sound/pci/oxygen/wm8766.h | 73 | ||||
| -rw-r--r-- | sound/pci/oxygen/wm8776.h | 177 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar.h | 2 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_wm87x6.c | 1021 |
9 files changed, 1292 insertions, 16 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index c83fd7b64201..33df82e3a398 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt | |||
| @@ -1948,7 +1948,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. | |||
| 1948 | ------------------- | 1948 | ------------------- |
| 1949 | 1949 | ||
| 1950 | Module for sound cards based on the Asus AV100/AV200 chips, | 1950 | Module for sound cards based on the Asus AV100/AV200 chips, |
| 1951 | i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), Essence ST | 1951 | i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST |
| 1952 | (Deluxe) and Essence STX. | 1952 | (Deluxe) and Essence STX. |
| 1953 | 1953 | ||
| 1954 | This module supports autoprobe and multiple cards. | 1954 | This module supports autoprobe and multiple cards. |
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index f745c317d6af..160b1bd0cd62 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c | |||
| @@ -33,22 +33,21 @@ | |||
| 33 | 33 | ||
| 34 | #define SKEW_BASE 0x10000 /* 16bit shift */ | 34 | #define SKEW_BASE 0x10000 /* 16bit shift */ |
| 35 | 35 | ||
| 36 | static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer_tick *tick, | 36 | static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr) |
| 37 | int tempo, int ppq) | ||
| 38 | { | 37 | { |
| 39 | if (tempo < 1000000) | 38 | if (tmr->tempo < 1000000) |
| 40 | tick->resolution = (tempo * 1000) / ppq; | 39 | tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq; |
| 41 | else { | 40 | else { |
| 42 | /* might overflow.. */ | 41 | /* might overflow.. */ |
| 43 | unsigned int s; | 42 | unsigned int s; |
| 44 | s = tempo % ppq; | 43 | s = tmr->tempo % tmr->ppq; |
| 45 | s = (s * 1000) / ppq; | 44 | s = (s * 1000) / tmr->ppq; |
| 46 | tick->resolution = (tempo / ppq) * 1000; | 45 | tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000; |
| 47 | tick->resolution += s; | 46 | tmr->tick.resolution += s; |
| 48 | } | 47 | } |
| 49 | if (tick->resolution <= 0) | 48 | if (tmr->tick.resolution <= 0) |
| 50 | tick->resolution = 1; | 49 | tmr->tick.resolution = 1; |
| 51 | snd_seq_timer_update_tick(tick, 0); | 50 | snd_seq_timer_update_tick(&tmr->tick, 0); |
| 52 | } | 51 | } |
| 53 | 52 | ||
| 54 | /* create new timer (constructor) */ | 53 | /* create new timer (constructor) */ |
| @@ -96,7 +95,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) | |||
| 96 | /* setup defaults */ | 95 | /* setup defaults */ |
| 97 | tmr->ppq = 96; /* 96 PPQ */ | 96 | tmr->ppq = 96; /* 96 PPQ */ |
| 98 | tmr->tempo = 500000; /* 120 BPM */ | 97 | tmr->tempo = 500000; /* 120 BPM */ |
| 99 | snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); | 98 | snd_seq_timer_set_tick_resolution(tmr); |
| 100 | tmr->running = 0; | 99 | tmr->running = 0; |
| 101 | 100 | ||
| 102 | tmr->type = SNDRV_SEQ_TIMER_ALSA; | 101 | tmr->type = SNDRV_SEQ_TIMER_ALSA; |
| @@ -180,7 +179,7 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) | |||
| 180 | spin_lock_irqsave(&tmr->lock, flags); | 179 | spin_lock_irqsave(&tmr->lock, flags); |
| 181 | if ((unsigned int)tempo != tmr->tempo) { | 180 | if ((unsigned int)tempo != tmr->tempo) { |
| 182 | tmr->tempo = tempo; | 181 | tmr->tempo = tempo; |
| 183 | snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); | 182 | snd_seq_timer_set_tick_resolution(tmr); |
| 184 | } | 183 | } |
| 185 | spin_unlock_irqrestore(&tmr->lock, flags); | 184 | spin_unlock_irqrestore(&tmr->lock, flags); |
| 186 | return 0; | 185 | return 0; |
| @@ -205,7 +204,7 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq) | |||
| 205 | } | 204 | } |
| 206 | 205 | ||
| 207 | tmr->ppq = ppq; | 206 | tmr->ppq = ppq; |
| 208 | snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); | 207 | snd_seq_timer_set_tick_resolution(tmr); |
| 209 | spin_unlock_irqrestore(&tmr->lock, flags); | 208 | spin_unlock_irqrestore(&tmr->lock, flags); |
| 210 | return 0; | 209 | return 0; |
| 211 | } | 210 | } |
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 351654cf7b09..1298c68d6bf0 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig | |||
| @@ -789,6 +789,7 @@ config SND_VIRTUOSO | |||
| 789 | Say Y here to include support for sound cards based on the | 789 | Say Y here to include support for sound cards based on the |
| 790 | Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, | 790 | Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, |
| 791 | Essence ST (Deluxe), and Essence STX. | 791 | Essence ST (Deluxe), and Essence STX. |
| 792 | Support for the DS is experimental. | ||
| 792 | Support for the HDAV1.3 (Deluxe) is very experimental. | 793 | Support for the HDAV1.3 (Deluxe) is very experimental. |
| 793 | 794 | ||
| 794 | To compile this driver as a module, choose M here: the module | 795 | To compile this driver as a module, choose M here: the module |
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 389941cf6100..acd8f15f7bff 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile | |||
| @@ -2,7 +2,7 @@ snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o | |||
| 2 | snd-hifier-objs := hifier.o | 2 | snd-hifier-objs := hifier.o |
| 3 | snd-oxygen-objs := oxygen.o | 3 | snd-oxygen-objs := oxygen.o |
| 4 | snd-virtuoso-objs := virtuoso.o xonar_lib.o \ | 4 | snd-virtuoso-objs := virtuoso.o xonar_lib.o \ |
| 5 | xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o | 5 | xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o |
| 6 | 6 | ||
| 7 | obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o | 7 | obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o |
| 8 | obj-$(CONFIG_SND_HIFIER) += snd-hifier.o | 8 | obj-$(CONFIG_SND_HIFIER) += snd-hifier.o |
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 6accaf9580b2..563b6f50821f 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c | |||
| @@ -49,6 +49,7 @@ static struct pci_device_id xonar_ids[] __devinitdata = { | |||
| 49 | { OXYGEN_PCI_SUBID(0x1043, 0x834f) }, | 49 | { OXYGEN_PCI_SUBID(0x1043, 0x834f) }, |
| 50 | { OXYGEN_PCI_SUBID(0x1043, 0x835c) }, | 50 | { OXYGEN_PCI_SUBID(0x1043, 0x835c) }, |
| 51 | { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, | 51 | { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, |
| 52 | { OXYGEN_PCI_SUBID(0x1043, 0x838e) }, | ||
| 52 | { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, | 53 | { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, |
| 53 | { } | 54 | { } |
| 54 | }; | 55 | }; |
| @@ -61,6 +62,8 @@ static int __devinit get_xonar_model(struct oxygen *chip, | |||
| 61 | return 0; | 62 | return 0; |
| 62 | if (get_xonar_cs43xx_model(chip, id) >= 0) | 63 | if (get_xonar_cs43xx_model(chip, id) >= 0) |
| 63 | return 0; | 64 | return 0; |
| 65 | if (get_xonar_wm87x6_model(chip, id) >= 0) | ||
| 66 | return 0; | ||
| 64 | return -EINVAL; | 67 | return -EINVAL; |
| 65 | } | 68 | } |
| 66 | 69 | ||
diff --git a/sound/pci/oxygen/wm8766.h b/sound/pci/oxygen/wm8766.h new file mode 100644 index 000000000000..e0e849a7eaeb --- /dev/null +++ b/sound/pci/oxygen/wm8766.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | #ifndef WM8766_H_INCLUDED | ||
| 2 | #define WM8766_H_INCLUDED | ||
| 3 | |||
| 4 | #define WM8766_LDA1 0x00 | ||
| 5 | #define WM8766_RDA1 0x01 | ||
| 6 | #define WM8766_DAC_CTRL 0x02 | ||
| 7 | #define WM8766_INT_CTRL 0x03 | ||
| 8 | #define WM8766_LDA2 0x04 | ||
| 9 | #define WM8766_RDA2 0x05 | ||
| 10 | #define WM8766_LDA3 0x06 | ||
| 11 | #define WM8766_RDA3 0x07 | ||
| 12 | #define WM8766_MASTDA 0x08 | ||
| 13 | #define WM8766_DAC_CTRL2 0x09 | ||
| 14 | #define WM8766_DAC_CTRL3 0x0a | ||
| 15 | #define WM8766_MUTE1 0x0c | ||
| 16 | #define WM8766_MUTE2 0x0f | ||
| 17 | #define WM8766_RESET 0x1f | ||
| 18 | |||
| 19 | /* LDAx/RDAx/MASTDA */ | ||
| 20 | #define WM8766_ATT_MASK 0x0ff | ||
| 21 | #define WM8766_UPDATE 0x100 | ||
| 22 | /* DAC_CTRL */ | ||
| 23 | #define WM8766_MUTEALL 0x001 | ||
| 24 | #define WM8766_DEEMPALL 0x002 | ||
| 25 | #define WM8766_PWDN 0x004 | ||
| 26 | #define WM8766_ATC 0x008 | ||
| 27 | #define WM8766_IZD 0x010 | ||
| 28 | #define WM8766_PL_LEFT_MASK 0x060 | ||
| 29 | #define WM8766_PL_LEFT_MUTE 0x000 | ||
| 30 | #define WM8766_PL_LEFT_LEFT 0x020 | ||
| 31 | #define WM8766_PL_LEFT_RIGHT 0x040 | ||
| 32 | #define WM8766_PL_LEFT_LRMIX 0x060 | ||
| 33 | #define WM8766_PL_RIGHT_MASK 0x180 | ||
| 34 | #define WM8766_PL_RIGHT_MUTE 0x000 | ||
| 35 | #define WM8766_PL_RIGHT_LEFT 0x080 | ||
| 36 | #define WM8766_PL_RIGHT_RIGHT 0x100 | ||
| 37 | #define WM8766_PL_RIGHT_LRMIX 0x180 | ||
| 38 | /* INT_CTRL */ | ||
| 39 | #define WM8766_FMT_MASK 0x003 | ||
| 40 | #define WM8766_FMT_RJUST 0x000 | ||
| 41 | #define WM8766_FMT_LJUST 0x001 | ||
| 42 | #define WM8766_FMT_I2S 0x002 | ||
| 43 | #define WM8766_FMT_DSP 0x003 | ||
| 44 | #define WM8766_LRP 0x004 | ||
| 45 | #define WM8766_BCP 0x008 | ||
| 46 | #define WM8766_IWL_MASK 0x030 | ||
| 47 | #define WM8766_IWL_16 0x000 | ||
| 48 | #define WM8766_IWL_20 0x010 | ||
| 49 | #define WM8766_IWL_24 0x020 | ||
| 50 | #define WM8766_IWL_32 0x030 | ||
| 51 | #define WM8766_PHASE_MASK 0x1c0 | ||
| 52 | /* DAC_CTRL2 */ | ||
| 53 | #define WM8766_ZCD 0x001 | ||
| 54 | #define WM8766_DZFM_MASK 0x006 | ||
| 55 | #define WM8766_DMUTE_MASK 0x038 | ||
| 56 | #define WM8766_DEEMP_MASK 0x1c0 | ||
| 57 | /* DAC_CTRL3 */ | ||
| 58 | #define WM8766_DACPD_MASK 0x00e | ||
| 59 | #define WM8766_PWRDNALL 0x010 | ||
| 60 | #define WM8766_MS 0x020 | ||
| 61 | #define WM8766_RATE_MASK 0x1c0 | ||
| 62 | #define WM8766_RATE_128 0x000 | ||
| 63 | #define WM8766_RATE_192 0x040 | ||
| 64 | #define WM8766_RATE_256 0x080 | ||
| 65 | #define WM8766_RATE_384 0x0c0 | ||
| 66 | #define WM8766_RATE_512 0x100 | ||
| 67 | #define WM8766_RATE_768 0x140 | ||
| 68 | /* MUTE1 */ | ||
| 69 | #define WM8766_MPD1 0x040 | ||
| 70 | /* MUTE2 */ | ||
| 71 | #define WM8766_MPD2 0x020 | ||
| 72 | |||
| 73 | #endif | ||
diff --git a/sound/pci/oxygen/wm8776.h b/sound/pci/oxygen/wm8776.h new file mode 100644 index 000000000000..1a96f5615727 --- /dev/null +++ b/sound/pci/oxygen/wm8776.h | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | #ifndef WM8776_H_INCLUDED | ||
| 2 | #define WM8776_H_INCLUDED | ||
| 3 | |||
| 4 | /* | ||
| 5 | * the following register names are from: | ||
| 6 | * wm8776.h -- WM8776 ASoC driver | ||
| 7 | * | ||
| 8 | * Copyright 2009 Wolfson Microelectronics plc | ||
| 9 | * | ||
| 10 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
| 11 | * | ||
| 12 | * This program is free software; you can redistribute it and/or modify | ||
| 13 | * it under the terms of the GNU General Public License version 2 as | ||
| 14 | * published by the Free Software Foundation. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #define WM8776_HPLVOL 0x00 | ||
| 18 | #define WM8776_HPRVOL 0x01 | ||
| 19 | #define WM8776_HPMASTER 0x02 | ||
| 20 | #define WM8776_DACLVOL 0x03 | ||
| 21 | #define WM8776_DACRVOL 0x04 | ||
| 22 | #define WM8776_DACMASTER 0x05 | ||
| 23 | #define WM8776_PHASESWAP 0x06 | ||
| 24 | #define WM8776_DACCTRL1 0x07 | ||
| 25 | #define WM8776_DACMUTE 0x08 | ||
| 26 | #define WM8776_DACCTRL2 0x09 | ||
| 27 | #define WM8776_DACIFCTRL 0x0a | ||
| 28 | #define WM8776_ADCIFCTRL 0x0b | ||
| 29 | #define WM8776_MSTRCTRL 0x0c | ||
| 30 | #define WM8776_PWRDOWN 0x0d | ||
| 31 | #define WM8776_ADCLVOL 0x0e | ||
| 32 | #define WM8776_ADCRVOL 0x0f | ||
| 33 | #define WM8776_ALCCTRL1 0x10 | ||
| 34 | #define WM8776_ALCCTRL2 0x11 | ||
| 35 | #define WM8776_ALCCTRL3 0x12 | ||
| 36 | #define WM8776_NOISEGATE 0x13 | ||
| 37 | #define WM8776_LIMITER 0x14 | ||
| 38 | #define WM8776_ADCMUX 0x15 | ||
| 39 | #define WM8776_OUTMUX 0x16 | ||
| 40 | #define WM8776_RESET 0x17 | ||
| 41 | |||
| 42 | |||
| 43 | /* HPLVOL/HPRVOL/HPMASTER */ | ||
| 44 | #define WM8776_HPATT_MASK 0x07f | ||
| 45 | #define WM8776_HPZCEN 0x080 | ||
| 46 | #define WM8776_UPDATE 0x100 | ||
| 47 | |||
| 48 | /* DACLVOL/DACRVOL/DACMASTER */ | ||
| 49 | #define WM8776_DATT_MASK 0x0ff | ||
| 50 | /*#define WM8776_UPDATE 0x100*/ | ||
| 51 | |||
| 52 | /* PHASESWAP */ | ||
| 53 | #define WM8776_PH_MASK 0x003 | ||
| 54 | |||
| 55 | /* DACCTRL1 */ | ||
| 56 | #define WM8776_DZCEN 0x001 | ||
| 57 | #define WM8776_ATC 0x002 | ||
| 58 | #define WM8776_IZD 0x004 | ||
| 59 | #define WM8776_TOD 0x008 | ||
| 60 | #define WM8776_PL_LEFT_MASK 0x030 | ||
| 61 | #define WM8776_PL_LEFT_MUTE 0x000 | ||
| 62 | #define WM8776_PL_LEFT_LEFT 0x010 | ||
| 63 | #define WM8776_PL_LEFT_RIGHT 0x020 | ||
| 64 | #define WM8776_PL_LEFT_LRMIX 0x030 | ||
| 65 | #define WM8776_PL_RIGHT_MASK 0x0c0 | ||
| 66 | #define WM8776_PL_RIGHT_MUTE 0x000 | ||
| 67 | #define WM8776_PL_RIGHT_LEFT 0x040 | ||
| 68 | #define WM8776_PL_RIGHT_RIGHT 0x080 | ||
| 69 | #define WM8776_PL_RIGHT_LRMIX 0x0c0 | ||
| 70 | |||
| 71 | /* DACMUTE */ | ||
| 72 | #define WM8776_DMUTE 0x001 | ||
| 73 | |||
| 74 | /* DACCTRL2 */ | ||
| 75 | #define WM8776_DEEMPH 0x001 | ||
| 76 | #define WM8776_DZFM_MASK 0x006 | ||
| 77 | #define WM8776_DZFM_NONE 0x000 | ||
| 78 | #define WM8776_DZFM_LR 0x002 | ||
| 79 | #define WM8776_DZFM_BOTH 0x004 | ||
| 80 | #define WM8776_DZFM_EITHER 0x006 | ||
| 81 | |||
| 82 | /* DACIFCTRL */ | ||
| 83 | #define WM8776_DACFMT_MASK 0x003 | ||
| 84 | #define WM8776_DACFMT_RJUST 0x000 | ||
| 85 | #define WM8776_DACFMT_LJUST 0x001 | ||
| 86 | #define WM8776_DACFMT_I2S 0x002 | ||
| 87 | #define WM8776_DACFMT_DSP 0x003 | ||
| 88 | #define WM8776_DACLRP 0x004 | ||
| 89 | #define WM8776_DACBCP 0x008 | ||
| 90 | #define WM8776_DACWL_MASK 0x030 | ||
| 91 | #define WM8776_DACWL_16 0x000 | ||
| 92 | #define WM8776_DACWL_20 0x010 | ||
| 93 | #define WM8776_DACWL_24 0x020 | ||
| 94 | #define WM8776_DACWL_32 0x030 | ||
| 95 | |||
| 96 | /* ADCIFCTRL */ | ||
| 97 | #define WM8776_ADCFMT_MASK 0x003 | ||
| 98 | #define WM8776_ADCFMT_RJUST 0x000 | ||
| 99 | #define WM8776_ADCFMT_LJUST 0x001 | ||
| 100 | #define WM8776_ADCFMT_I2S 0x002 | ||
| 101 | #define WM8776_ADCFMT_DSP 0x003 | ||
| 102 | #define WM8776_ADCLRP 0x004 | ||
| 103 | #define WM8776_ADCBCP 0x008 | ||
| 104 | #define WM8776_ADCWL_MASK 0x030 | ||
| 105 | #define WM8776_ADCWL_16 0x000 | ||
| 106 | #define WM8776_ADCWL_20 0x010 | ||
| 107 | #define WM8776_ADCWL_24 0x020 | ||
| 108 | #define WM8776_ADCWL_32 0x030 | ||
| 109 | #define WM8776_ADCMCLK 0x040 | ||
| 110 | #define WM8776_ADCHPD 0x100 | ||
| 111 | |||
| 112 | /* MSTRCTRL */ | ||
| 113 | #define WM8776_ADCRATE_MASK 0x007 | ||
| 114 | #define WM8776_ADCRATE_256 0x002 | ||
| 115 | #define WM8776_ADCRATE_384 0x003 | ||
| 116 | #define WM8776_ADCRATE_512 0x004 | ||
| 117 | #define WM8776_ADCRATE_768 0x005 | ||
| 118 | #define WM8776_ADCOSR 0x008 | ||
| 119 | #define WM8776_DACRATE_MASK 0x070 | ||
| 120 | #define WM8776_DACRATE_128 0x000 | ||
| 121 | #define WM8776_DACRATE_192 0x010 | ||
| 122 | #define WM8776_DACRATE_256 0x020 | ||
| 123 | #define WM8776_DACRATE_384 0x030 | ||
| 124 | #define WM8776_DACRATE_512 0x040 | ||
| 125 | #define WM8776_DACRATE_768 0x050 | ||
| 126 | #define WM8776_DACMS 0x080 | ||
| 127 | #define WM8776_ADCMS 0x100 | ||
| 128 | |||
| 129 | /* PWRDOWN */ | ||
| 130 | #define WM8776_PDWN 0x001 | ||
| 131 | #define WM8776_ADCPD 0x002 | ||
| 132 | #define WM8776_DACPD 0x004 | ||
| 133 | #define WM8776_HPPD 0x008 | ||
| 134 | #define WM8776_AINPD 0x040 | ||
| 135 | |||
| 136 | /* ADCLVOL/ADCRVOL */ | ||
| 137 | #define WM8776_AGMASK 0x0ff | ||
| 138 | #define WM8776_ZCA 0x100 | ||
| 139 | |||
| 140 | /* ALCCTRL1 */ | ||
| 141 | #define WM8776_LCT_MASK 0x00f | ||
| 142 | #define WM8776_MAXGAIN_MASK 0x070 | ||
| 143 | #define WM8776_LCSEL_MASK 0x180 | ||
| 144 | #define WM8776_LCSEL_LIMITER 0x000 | ||
| 145 | #define WM8776_LCSEL_ALC_RIGHT 0x080 | ||
| 146 | #define WM8776_LCSEL_ALC_LEFT 0x100 | ||
| 147 | #define WM8776_LCSEL_ALC_STEREO 0x180 | ||
| 148 | |||
| 149 | /* ALCCTRL2 */ | ||
| 150 | #define WM8776_HLD_MASK 0x00f | ||
| 151 | #define WM8776_ALCZC 0x080 | ||
| 152 | #define WM8776_LCEN 0x100 | ||
| 153 | |||
| 154 | /* ALCCTRL3 */ | ||
| 155 | #define WM8776_ATK_MASK 0x00f | ||
| 156 | #define WM8776_DCY_MASK 0x0f0 | ||
| 157 | |||
| 158 | /* NOISEGATE */ | ||
| 159 | #define WM8776_NGAT 0x001 | ||
| 160 | #define WM8776_NGTH_MASK 0x01c | ||
| 161 | |||
| 162 | /* LIMITER */ | ||
| 163 | #define WM8776_MAXATTEN_MASK 0x00f | ||
| 164 | #define WM8776_TRANWIN_MASK 0x070 | ||
| 165 | |||
| 166 | /* ADCMUX */ | ||
| 167 | #define WM8776_AMX_MASK 0x01f | ||
| 168 | #define WM8776_MUTERA 0x040 | ||
| 169 | #define WM8776_MUTELA 0x080 | ||
| 170 | #define WM8776_LRBOTH 0x100 | ||
| 171 | |||
| 172 | /* OUTMUX */ | ||
| 173 | #define WM8776_MX_DAC 0x001 | ||
| 174 | #define WM8776_MX_AUX 0x002 | ||
| 175 | #define WM8776_MX_BYPASS 0x004 | ||
| 176 | |||
| 177 | #endif | ||
diff --git a/sound/pci/oxygen/xonar.h b/sound/pci/oxygen/xonar.h index 89b3ed814d64..b35343b0a9a5 100644 --- a/sound/pci/oxygen/xonar.h +++ b/sound/pci/oxygen/xonar.h | |||
| @@ -35,6 +35,8 @@ int get_xonar_pcm179x_model(struct oxygen *chip, | |||
| 35 | const struct pci_device_id *id); | 35 | const struct pci_device_id *id); |
| 36 | int get_xonar_cs43xx_model(struct oxygen *chip, | 36 | int get_xonar_cs43xx_model(struct oxygen *chip, |
| 37 | const struct pci_device_id *id); | 37 | const struct pci_device_id *id); |
| 38 | int get_xonar_wm87x6_model(struct oxygen *chip, | ||
| 39 | const struct pci_device_id *id); | ||
| 38 | 40 | ||
| 39 | /* HDMI helper functions */ | 41 | /* HDMI helper functions */ |
| 40 | 42 | ||
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c new file mode 100644 index 000000000000..7754db166d9e --- /dev/null +++ b/sound/pci/oxygen/xonar_wm87x6.c | |||
| @@ -0,0 +1,1021 @@ | |||
| 1 | /* | ||
| 2 | * card driver for models with WM8776/WM8766 DACs (Xonar DS) | ||
| 3 | * | ||
| 4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
| 5 | * | ||
| 6 | * | ||
| 7 | * This driver is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License, version 2. | ||
| 9 | * | ||
| 10 | * This driver is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this driver; if not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | /* | ||
| 20 | * Xonar DS | ||
| 21 | * -------- | ||
| 22 | * | ||
| 23 | * CMI8788: | ||
| 24 | * | ||
| 25 | * SPI 0 -> WM8766 (surround, center/LFE, back) | ||
| 26 | * SPI 1 -> WM8776 (front, input) | ||
| 27 | * | ||
| 28 | * GPIO 4 <- headphone detect | ||
| 29 | * GPIO 6 -> route input jack to input 1/2 (1/0) | ||
| 30 | * GPIO 7 -> enable output to speakers | ||
| 31 | * GPIO 8 -> enable output to speakers | ||
| 32 | */ | ||
| 33 | |||
| 34 | #include <linux/pci.h> | ||
| 35 | #include <linux/delay.h> | ||
| 36 | #include <sound/control.h> | ||
| 37 | #include <sound/core.h> | ||
| 38 | #include <sound/pcm.h> | ||
| 39 | #include <sound/pcm_params.h> | ||
| 40 | #include <sound/tlv.h> | ||
| 41 | #include "xonar.h" | ||
| 42 | #include "wm8776.h" | ||
| 43 | #include "wm8766.h" | ||
| 44 | |||
| 45 | #define GPIO_DS_HP_DETECT 0x0010 | ||
| 46 | #define GPIO_DS_INPUT_ROUTE 0x0040 | ||
| 47 | #define GPIO_DS_OUTPUT_ENABLE 0x0180 | ||
| 48 | |||
| 49 | #define LC_CONTROL_LIMITER 0x40000000 | ||
| 50 | #define LC_CONTROL_ALC 0x20000000 | ||
| 51 | |||
| 52 | struct xonar_wm87x6 { | ||
| 53 | struct xonar_generic generic; | ||
| 54 | u16 wm8776_regs[0x17]; | ||
| 55 | u16 wm8766_regs[0x10]; | ||
| 56 | struct snd_kcontrol *lc_controls[13]; | ||
| 57 | }; | ||
| 58 | |||
| 59 | static void wm8776_write(struct oxygen *chip, | ||
| 60 | unsigned int reg, unsigned int value) | ||
| 61 | { | ||
| 62 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 63 | |||
| 64 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | | ||
| 65 | OXYGEN_SPI_DATA_LENGTH_2 | | ||
| 66 | OXYGEN_SPI_CLOCK_160 | | ||
| 67 | (1 << OXYGEN_SPI_CODEC_SHIFT) | | ||
| 68 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, | ||
| 69 | (reg << 9) | value); | ||
| 70 | if (reg < ARRAY_SIZE(data->wm8776_regs)) { | ||
| 71 | if (reg >= WM8776_HPLVOL || reg <= WM8776_DACMASTER) | ||
| 72 | value &= ~WM8776_UPDATE; | ||
| 73 | data->wm8776_regs[reg] = value; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | static void wm8776_write_cached(struct oxygen *chip, | ||
| 78 | unsigned int reg, unsigned int value) | ||
| 79 | { | ||
| 80 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 81 | |||
| 82 | if (reg >= ARRAY_SIZE(data->wm8776_regs) || | ||
| 83 | value != data->wm8776_regs[reg]) | ||
| 84 | wm8776_write(chip, reg, value); | ||
| 85 | } | ||
| 86 | |||
| 87 | static void wm8766_write(struct oxygen *chip, | ||
| 88 | unsigned int reg, unsigned int value) | ||
| 89 | { | ||
| 90 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 91 | |||
| 92 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | | ||
| 93 | OXYGEN_SPI_DATA_LENGTH_2 | | ||
| 94 | OXYGEN_SPI_CLOCK_160 | | ||
| 95 | (0 << OXYGEN_SPI_CODEC_SHIFT) | | ||
| 96 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, | ||
| 97 | (reg << 9) | value); | ||
| 98 | if (reg < ARRAY_SIZE(data->wm8766_regs)) | ||
| 99 | data->wm8766_regs[reg] = value; | ||
| 100 | } | ||
| 101 | |||
| 102 | static void wm8766_write_cached(struct oxygen *chip, | ||
| 103 | unsigned int reg, unsigned int value) | ||
| 104 | { | ||
| 105 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 106 | |||
| 107 | if (reg >= ARRAY_SIZE(data->wm8766_regs) || | ||
| 108 | value != data->wm8766_regs[reg]) { | ||
| 109 | if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) || | ||
| 110 | (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA)) | ||
| 111 | value &= ~WM8766_UPDATE; | ||
| 112 | wm8766_write(chip, reg, value); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | static void wm8776_registers_init(struct oxygen *chip) | ||
| 117 | { | ||
| 118 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 119 | |||
| 120 | wm8776_write(chip, WM8776_RESET, 0); | ||
| 121 | wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN | | ||
| 122 | WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT); | ||
| 123 | wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0); | ||
| 124 | wm8776_write(chip, WM8776_DACIFCTRL, | ||
| 125 | WM8776_DACFMT_LJUST | WM8776_DACWL_24); | ||
| 126 | wm8776_write(chip, WM8776_ADCIFCTRL, | ||
| 127 | data->wm8776_regs[WM8776_ADCIFCTRL]); | ||
| 128 | wm8776_write(chip, WM8776_MSTRCTRL, data->wm8776_regs[WM8776_MSTRCTRL]); | ||
| 129 | wm8776_write(chip, WM8776_PWRDOWN, data->wm8776_regs[WM8776_PWRDOWN]); | ||
| 130 | wm8776_write(chip, WM8776_HPLVOL, data->wm8776_regs[WM8776_HPLVOL]); | ||
| 131 | wm8776_write(chip, WM8776_HPRVOL, data->wm8776_regs[WM8776_HPRVOL] | | ||
| 132 | WM8776_UPDATE); | ||
| 133 | wm8776_write(chip, WM8776_ADCLVOL, data->wm8776_regs[WM8776_ADCLVOL]); | ||
| 134 | wm8776_write(chip, WM8776_ADCRVOL, data->wm8776_regs[WM8776_ADCRVOL]); | ||
| 135 | wm8776_write(chip, WM8776_ADCMUX, data->wm8776_regs[WM8776_ADCMUX]); | ||
| 136 | wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0]); | ||
| 137 | wm8776_write(chip, WM8776_DACRVOL, chip->dac_volume[1] | WM8776_UPDATE); | ||
| 138 | } | ||
| 139 | |||
| 140 | static void wm8766_registers_init(struct oxygen *chip) | ||
| 141 | { | ||
| 142 | wm8766_write(chip, WM8766_RESET, 0); | ||
| 143 | wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24); | ||
| 144 | wm8766_write(chip, WM8766_DAC_CTRL2, | ||
| 145 | WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); | ||
| 146 | wm8766_write(chip, WM8766_LDA1, chip->dac_volume[2]); | ||
| 147 | wm8766_write(chip, WM8766_RDA1, chip->dac_volume[3]); | ||
| 148 | wm8766_write(chip, WM8766_LDA2, chip->dac_volume[4]); | ||
| 149 | wm8766_write(chip, WM8766_RDA2, chip->dac_volume[5]); | ||
| 150 | wm8766_write(chip, WM8766_LDA3, chip->dac_volume[6]); | ||
| 151 | wm8766_write(chip, WM8766_RDA3, chip->dac_volume[7] | WM8766_UPDATE); | ||
| 152 | } | ||
| 153 | |||
| 154 | static void wm8776_init(struct oxygen *chip) | ||
| 155 | { | ||
| 156 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 157 | |||
| 158 | data->wm8776_regs[WM8776_HPLVOL] = (0x79 - 60) | WM8776_HPZCEN; | ||
| 159 | data->wm8776_regs[WM8776_HPRVOL] = (0x79 - 60) | WM8776_HPZCEN; | ||
| 160 | data->wm8776_regs[WM8776_ADCIFCTRL] = | ||
| 161 | WM8776_ADCFMT_LJUST | WM8776_ADCWL_24 | WM8776_ADCMCLK; | ||
| 162 | data->wm8776_regs[WM8776_MSTRCTRL] = | ||
| 163 | WM8776_ADCRATE_256 | WM8776_DACRATE_256; | ||
| 164 | data->wm8776_regs[WM8776_PWRDOWN] = WM8776_HPPD; | ||
| 165 | data->wm8776_regs[WM8776_ADCLVOL] = 0xa5 | WM8776_ZCA; | ||
| 166 | data->wm8776_regs[WM8776_ADCRVOL] = 0xa5 | WM8776_ZCA; | ||
| 167 | data->wm8776_regs[WM8776_ADCMUX] = 0x001; | ||
| 168 | wm8776_registers_init(chip); | ||
| 169 | } | ||
| 170 | |||
| 171 | static void xonar_ds_init(struct oxygen *chip) | ||
| 172 | { | ||
| 173 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 174 | |||
| 175 | data->generic.anti_pop_delay = 300; | ||
| 176 | data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE; | ||
| 177 | |||
| 178 | wm8776_init(chip); | ||
| 179 | wm8766_registers_init(chip); | ||
| 180 | |||
| 181 | oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE, | ||
| 182 | GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE); | ||
| 183 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE); | ||
| 184 | oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT); | ||
| 185 | chip->interrupt_mask |= OXYGEN_INT_GPIO; | ||
| 186 | |||
| 187 | xonar_enable_output(chip); | ||
| 188 | |||
| 189 | snd_component_add(chip->card, "WM8776"); | ||
| 190 | snd_component_add(chip->card, "WM8766"); | ||
| 191 | } | ||
| 192 | |||
| 193 | static void xonar_ds_cleanup(struct oxygen *chip) | ||
| 194 | { | ||
| 195 | xonar_disable_output(chip); | ||
| 196 | } | ||
| 197 | |||
| 198 | static void xonar_ds_suspend(struct oxygen *chip) | ||
| 199 | { | ||
| 200 | xonar_ds_cleanup(chip); | ||
| 201 | } | ||
| 202 | |||
| 203 | static void xonar_ds_resume(struct oxygen *chip) | ||
| 204 | { | ||
| 205 | wm8776_registers_init(chip); | ||
| 206 | wm8766_registers_init(chip); | ||
| 207 | xonar_enable_output(chip); | ||
| 208 | } | ||
| 209 | |||
| 210 | static void wm8776_adc_hardware_filter(unsigned int channel, | ||
| 211 | struct snd_pcm_hardware *hardware) | ||
| 212 | { | ||
| 213 | if (channel == PCM_A) { | ||
| 214 | hardware->rates = SNDRV_PCM_RATE_32000 | | ||
| 215 | SNDRV_PCM_RATE_44100 | | ||
| 216 | SNDRV_PCM_RATE_48000 | | ||
| 217 | SNDRV_PCM_RATE_64000 | | ||
| 218 | SNDRV_PCM_RATE_88200 | | ||
| 219 | SNDRV_PCM_RATE_96000; | ||
| 220 | hardware->rate_max = 96000; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | static void set_wm87x6_dac_params(struct oxygen *chip, | ||
| 225 | struct snd_pcm_hw_params *params) | ||
| 226 | { | ||
| 227 | } | ||
| 228 | |||
| 229 | static void set_wm8776_adc_params(struct oxygen *chip, | ||
| 230 | struct snd_pcm_hw_params *params) | ||
| 231 | { | ||
| 232 | u16 reg; | ||
| 233 | |||
| 234 | reg = WM8776_ADCRATE_256 | WM8776_DACRATE_256; | ||
| 235 | if (params_rate(params) > 48000) | ||
| 236 | reg |= WM8776_ADCOSR; | ||
| 237 | wm8776_write_cached(chip, WM8776_MSTRCTRL, reg); | ||
| 238 | } | ||
| 239 | |||
| 240 | static void update_wm8776_volume(struct oxygen *chip) | ||
| 241 | { | ||
| 242 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 243 | u8 to_change; | ||
| 244 | |||
| 245 | if (chip->dac_volume[0] == chip->dac_volume[1]) { | ||
| 246 | if (chip->dac_volume[0] != data->wm8776_regs[WM8776_DACLVOL] || | ||
| 247 | chip->dac_volume[1] != data->wm8776_regs[WM8776_DACRVOL]) { | ||
| 248 | wm8776_write(chip, WM8776_DACMASTER, | ||
| 249 | chip->dac_volume[0] | WM8776_UPDATE); | ||
| 250 | data->wm8776_regs[WM8776_DACLVOL] = chip->dac_volume[0]; | ||
| 251 | data->wm8776_regs[WM8776_DACRVOL] = chip->dac_volume[0]; | ||
| 252 | } | ||
| 253 | } else { | ||
| 254 | to_change = (chip->dac_volume[0] != | ||
| 255 | data->wm8776_regs[WM8776_DACLVOL]) << 0; | ||
| 256 | to_change |= (chip->dac_volume[1] != | ||
| 257 | data->wm8776_regs[WM8776_DACLVOL]) << 1; | ||
| 258 | if (to_change & 1) | ||
| 259 | wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0] | | ||
| 260 | ((to_change & 2) ? 0 : WM8776_UPDATE)); | ||
| 261 | if (to_change & 2) | ||
| 262 | wm8776_write(chip, WM8776_DACRVOL, | ||
| 263 | chip->dac_volume[1] | WM8776_UPDATE); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | static void update_wm87x6_volume(struct oxygen *chip) | ||
| 268 | { | ||
| 269 | static const u8 wm8766_regs[6] = { | ||
| 270 | WM8766_LDA1, WM8766_RDA1, | ||
| 271 | WM8766_LDA2, WM8766_RDA2, | ||
| 272 | WM8766_LDA3, WM8766_RDA3, | ||
| 273 | }; | ||
| 274 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 275 | unsigned int i; | ||
| 276 | u8 to_change; | ||
| 277 | |||
| 278 | update_wm8776_volume(chip); | ||
| 279 | if (chip->dac_volume[2] == chip->dac_volume[3] && | ||
| 280 | chip->dac_volume[2] == chip->dac_volume[4] && | ||
| 281 | chip->dac_volume[2] == chip->dac_volume[5] && | ||
| 282 | chip->dac_volume[2] == chip->dac_volume[6] && | ||
| 283 | chip->dac_volume[2] == chip->dac_volume[7]) { | ||
| 284 | to_change = 0; | ||
| 285 | for (i = 0; i < 6; ++i) | ||
| 286 | if (chip->dac_volume[2] != | ||
| 287 | data->wm8766_regs[wm8766_regs[i]]) | ||
| 288 | to_change = 1; | ||
| 289 | if (to_change) { | ||
| 290 | wm8766_write(chip, WM8766_MASTDA, | ||
| 291 | chip->dac_volume[2] | WM8766_UPDATE); | ||
| 292 | for (i = 0; i < 6; ++i) | ||
| 293 | data->wm8766_regs[wm8766_regs[i]] = | ||
| 294 | chip->dac_volume[2]; | ||
| 295 | } | ||
| 296 | } else { | ||
| 297 | to_change = 0; | ||
| 298 | for (i = 0; i < 6; ++i) | ||
| 299 | to_change |= (chip->dac_volume[2 + i] != | ||
| 300 | data->wm8766_regs[wm8766_regs[i]]) << i; | ||
| 301 | for (i = 0; i < 6; ++i) | ||
| 302 | if (to_change & (1 << i)) | ||
| 303 | wm8766_write(chip, wm8766_regs[i], | ||
| 304 | chip->dac_volume[2 + i] | | ||
| 305 | ((to_change & (0x3e << i)) | ||
| 306 | ? 0 : WM8766_UPDATE)); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | static void update_wm8776_mute(struct oxygen *chip) | ||
| 311 | { | ||
| 312 | wm8776_write_cached(chip, WM8776_DACMUTE, | ||
| 313 | chip->dac_mute ? WM8776_DMUTE : 0); | ||
| 314 | } | ||
| 315 | |||
| 316 | static void update_wm87x6_mute(struct oxygen *chip) | ||
| 317 | { | ||
| 318 | update_wm8776_mute(chip); | ||
| 319 | wm8766_write_cached(chip, WM8766_DAC_CTRL2, WM8766_ZCD | | ||
| 320 | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); | ||
| 321 | } | ||
| 322 | |||
| 323 | static void xonar_ds_gpio_changed(struct oxygen *chip) | ||
| 324 | { | ||
| 325 | u16 bits; | ||
| 326 | |||
| 327 | bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); | ||
| 328 | snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT)); | ||
| 329 | } | ||
| 330 | |||
| 331 | static int wm8776_bit_switch_get(struct snd_kcontrol *ctl, | ||
| 332 | struct snd_ctl_elem_value *value) | ||
| 333 | { | ||
| 334 | struct oxygen *chip = ctl->private_data; | ||
| 335 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 336 | u16 bit = ctl->private_value & 0xffff; | ||
| 337 | unsigned int reg_index = (ctl->private_value >> 16) & 0xff; | ||
| 338 | bool invert = (ctl->private_value >> 24) & 1; | ||
| 339 | |||
| 340 | value->value.integer.value[0] = | ||
| 341 | ((data->wm8776_regs[reg_index] & bit) != 0) ^ invert; | ||
| 342 | return 0; | ||
| 343 | } | ||
| 344 | |||
| 345 | static int wm8776_bit_switch_put(struct snd_kcontrol *ctl, | ||
| 346 | struct snd_ctl_elem_value *value) | ||
| 347 | { | ||
| 348 | struct oxygen *chip = ctl->private_data; | ||
| 349 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 350 | u16 bit = ctl->private_value & 0xffff; | ||
| 351 | u16 reg_value; | ||
| 352 | unsigned int reg_index = (ctl->private_value >> 16) & 0xff; | ||
| 353 | bool invert = (ctl->private_value >> 24) & 1; | ||
| 354 | int changed; | ||
| 355 | |||
| 356 | mutex_lock(&chip->mutex); | ||
| 357 | reg_value = data->wm8776_regs[reg_index] & ~bit; | ||
| 358 | if (value->value.integer.value[0] ^ invert) | ||
| 359 | reg_value |= bit; | ||
| 360 | changed = reg_value != data->wm8776_regs[reg_index]; | ||
| 361 | if (changed) | ||
| 362 | wm8776_write(chip, reg_index, reg_value); | ||
| 363 | mutex_unlock(&chip->mutex); | ||
| 364 | return changed; | ||
| 365 | } | ||
| 366 | |||
| 367 | static int wm8776_field_enum_info(struct snd_kcontrol *ctl, | ||
| 368 | struct snd_ctl_elem_info *info) | ||
| 369 | { | ||
| 370 | static const char *const hld[16] = { | ||
| 371 | "0 ms", "2.67 ms", "5.33 ms", "10.6 ms", | ||
| 372 | "21.3 ms", "42.7 ms", "85.3 ms", "171 ms", | ||
| 373 | "341 ms", "683 ms", "1.37 s", "2.73 s", | ||
| 374 | "5.46 s", "10.9 s", "21.8 s", "43.7 s", | ||
| 375 | }; | ||
| 376 | static const char *const atk_lim[11] = { | ||
| 377 | "0.25 ms", "0.5 ms", "1 ms", "2 ms", | ||
| 378 | "4 ms", "8 ms", "16 ms", "32 ms", | ||
| 379 | "64 ms", "128 ms", "256 ms", | ||
| 380 | }; | ||
| 381 | static const char *const atk_alc[11] = { | ||
| 382 | "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms", | ||
| 383 | "134 ms", "269 ms", "538 ms", "1.08 s", | ||
| 384 | "2.15 s", "4.3 s", "8.6 s", | ||
| 385 | }; | ||
| 386 | static const char *const dcy_lim[11] = { | ||
| 387 | "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms", | ||
| 388 | "19.2 ms", "38.4 ms", "76.8 ms", "154 ms", | ||
| 389 | "307 ms", "614 ms", "1.23 s", | ||
| 390 | }; | ||
| 391 | static const char *const dcy_alc[11] = { | ||
| 392 | "33.5 ms", "67.0 ms", "134 ms", "268 ms", | ||
| 393 | "536 ms", "1.07 s", "2.14 s", "4.29 s", | ||
| 394 | "8.58 s", "17.2 s", "34.3 s", | ||
| 395 | }; | ||
| 396 | static const char *const tranwin[8] = { | ||
| 397 | "0 us", "62.5 us", "125 us", "250 us", | ||
| 398 | "500 us", "1 ms", "2 ms", "4 ms", | ||
| 399 | }; | ||
| 400 | u8 max; | ||
| 401 | const char *const *names; | ||
| 402 | |||
| 403 | max = (ctl->private_value >> 12) & 0xf; | ||
| 404 | info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 405 | info->count = 1; | ||
| 406 | info->value.enumerated.items = max + 1; | ||
| 407 | if (info->value.enumerated.item > max) | ||
| 408 | info->value.enumerated.item = max; | ||
| 409 | switch ((ctl->private_value >> 24) & 0x1f) { | ||
| 410 | case WM8776_ALCCTRL2: | ||
| 411 | names = hld; | ||
| 412 | break; | ||
| 413 | case WM8776_ALCCTRL3: | ||
| 414 | if (((ctl->private_value >> 20) & 0xf) == 0) { | ||
| 415 | if (ctl->private_value & LC_CONTROL_LIMITER) | ||
| 416 | names = atk_lim; | ||
| 417 | else | ||
| 418 | names = atk_alc; | ||
| 419 | } else { | ||
| 420 | if (ctl->private_value & LC_CONTROL_LIMITER) | ||
| 421 | names = dcy_lim; | ||
| 422 | else | ||
| 423 | names = dcy_alc; | ||
| 424 | } | ||
| 425 | break; | ||
| 426 | case WM8776_LIMITER: | ||
| 427 | names = tranwin; | ||
| 428 | break; | ||
| 429 | default: | ||
| 430 | return -ENXIO; | ||
| 431 | } | ||
| 432 | strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); | ||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 436 | static int wm8776_field_volume_info(struct snd_kcontrol *ctl, | ||
| 437 | struct snd_ctl_elem_info *info) | ||
| 438 | { | ||
| 439 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 440 | info->count = 1; | ||
| 441 | info->value.integer.min = (ctl->private_value >> 8) & 0xf; | ||
| 442 | info->value.integer.max = (ctl->private_value >> 12) & 0xf; | ||
| 443 | return 0; | ||
| 444 | } | ||
| 445 | |||
| 446 | static void wm8776_field_set_from_ctl(struct snd_kcontrol *ctl) | ||
| 447 | { | ||
| 448 | struct oxygen *chip = ctl->private_data; | ||
| 449 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 450 | unsigned int value, reg_index, mode; | ||
| 451 | u8 min, max, shift; | ||
| 452 | u16 mask, reg_value; | ||
| 453 | bool invert; | ||
| 454 | |||
| 455 | if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) == | ||
| 456 | WM8776_LCSEL_LIMITER) | ||
| 457 | mode = LC_CONTROL_LIMITER; | ||
| 458 | else | ||
| 459 | mode = LC_CONTROL_ALC; | ||
| 460 | if (!(ctl->private_value & mode)) | ||
| 461 | return; | ||
| 462 | |||
| 463 | value = ctl->private_value & 0xf; | ||
| 464 | min = (ctl->private_value >> 8) & 0xf; | ||
| 465 | max = (ctl->private_value >> 12) & 0xf; | ||
| 466 | mask = (ctl->private_value >> 16) & 0xf; | ||
| 467 | shift = (ctl->private_value >> 20) & 0xf; | ||
| 468 | reg_index = (ctl->private_value >> 24) & 0x1f; | ||
| 469 | invert = (ctl->private_value >> 29) & 0x1; | ||
| 470 | |||
| 471 | if (invert) | ||
| 472 | value = max - (value - min); | ||
| 473 | reg_value = data->wm8776_regs[reg_index]; | ||
| 474 | reg_value &= ~(mask << shift); | ||
| 475 | reg_value |= value << shift; | ||
| 476 | wm8776_write_cached(chip, reg_index, reg_value); | ||
| 477 | } | ||
| 478 | |||
| 479 | static int wm8776_field_set(struct snd_kcontrol *ctl, unsigned int value) | ||
| 480 | { | ||
| 481 | struct oxygen *chip = ctl->private_data; | ||
| 482 | u8 min, max; | ||
| 483 | int changed; | ||
| 484 | |||
| 485 | min = (ctl->private_value >> 8) & 0xf; | ||
| 486 | max = (ctl->private_value >> 12) & 0xf; | ||
| 487 | if (value < min || value > max) | ||
| 488 | return -EINVAL; | ||
| 489 | mutex_lock(&chip->mutex); | ||
| 490 | changed = value != (ctl->private_value & 0xf); | ||
| 491 | if (changed) { | ||
| 492 | ctl->private_value = (ctl->private_value & ~0xf) | value; | ||
| 493 | wm8776_field_set_from_ctl(ctl); | ||
| 494 | } | ||
| 495 | mutex_unlock(&chip->mutex); | ||
| 496 | return changed; | ||
| 497 | } | ||
| 498 | |||
| 499 | static int wm8776_field_enum_get(struct snd_kcontrol *ctl, | ||
| 500 | struct snd_ctl_elem_value *value) | ||
| 501 | { | ||
| 502 | value->value.enumerated.item[0] = ctl->private_value & 0xf; | ||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 506 | static int wm8776_field_volume_get(struct snd_kcontrol *ctl, | ||
| 507 | struct snd_ctl_elem_value *value) | ||
| 508 | { | ||
| 509 | value->value.integer.value[0] = ctl->private_value & 0xf; | ||
| 510 | return 0; | ||
| 511 | } | ||
| 512 | |||
| 513 | static int wm8776_field_enum_put(struct snd_kcontrol *ctl, | ||
| 514 | struct snd_ctl_elem_value *value) | ||
| 515 | { | ||
| 516 | return wm8776_field_set(ctl, value->value.enumerated.item[0]); | ||
| 517 | } | ||
| 518 | |||
| 519 | static int wm8776_field_volume_put(struct snd_kcontrol *ctl, | ||
| 520 | struct snd_ctl_elem_value *value) | ||
| 521 | { | ||
| 522 | return wm8776_field_set(ctl, value->value.integer.value[0]); | ||
| 523 | } | ||
| 524 | |||
| 525 | static int wm8776_hp_vol_info(struct snd_kcontrol *ctl, | ||
| 526 | struct snd_ctl_elem_info *info) | ||
| 527 | { | ||
| 528 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 529 | info->count = 2; | ||
| 530 | info->value.integer.min = 0x79 - 60; | ||
| 531 | info->value.integer.max = 0x7f; | ||
| 532 | return 0; | ||
| 533 | } | ||
| 534 | |||
| 535 | static int wm8776_hp_vol_get(struct snd_kcontrol *ctl, | ||
| 536 | struct snd_ctl_elem_value *value) | ||
| 537 | { | ||
| 538 | struct oxygen *chip = ctl->private_data; | ||
| 539 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 540 | |||
| 541 | mutex_lock(&chip->mutex); | ||
| 542 | value->value.integer.value[0] = | ||
| 543 | data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK; | ||
| 544 | value->value.integer.value[1] = | ||
| 545 | data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK; | ||
| 546 | mutex_unlock(&chip->mutex); | ||
| 547 | return 0; | ||
| 548 | } | ||
| 549 | |||
| 550 | static int wm8776_hp_vol_put(struct snd_kcontrol *ctl, | ||
| 551 | struct snd_ctl_elem_value *value) | ||
| 552 | { | ||
| 553 | struct oxygen *chip = ctl->private_data; | ||
| 554 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 555 | u8 to_update; | ||
| 556 | |||
| 557 | mutex_lock(&chip->mutex); | ||
| 558 | to_update = (value->value.integer.value[0] != | ||
| 559 | (data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK)) | ||
| 560 | << 0; | ||
| 561 | to_update |= (value->value.integer.value[1] != | ||
| 562 | (data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK)) | ||
| 563 | << 1; | ||
| 564 | if (value->value.integer.value[0] == value->value.integer.value[1]) { | ||
| 565 | if (to_update) { | ||
| 566 | wm8776_write(chip, WM8776_HPMASTER, | ||
| 567 | value->value.integer.value[0] | | ||
| 568 | WM8776_HPZCEN | WM8776_UPDATE); | ||
| 569 | data->wm8776_regs[WM8776_HPLVOL] = | ||
| 570 | value->value.integer.value[0] | WM8776_HPZCEN; | ||
| 571 | data->wm8776_regs[WM8776_HPRVOL] = | ||
| 572 | value->value.integer.value[0] | WM8776_HPZCEN; | ||
| 573 | } | ||
| 574 | } else { | ||
| 575 | if (to_update & 1) | ||
| 576 | wm8776_write(chip, WM8776_HPLVOL, | ||
| 577 | value->value.integer.value[0] | | ||
| 578 | WM8776_HPZCEN | | ||
| 579 | ((to_update & 2) ? 0 : WM8776_UPDATE)); | ||
| 580 | if (to_update & 2) | ||
| 581 | wm8776_write(chip, WM8776_HPRVOL, | ||
| 582 | value->value.integer.value[1] | | ||
| 583 | WM8776_HPZCEN | WM8776_UPDATE); | ||
| 584 | } | ||
| 585 | mutex_unlock(&chip->mutex); | ||
| 586 | return to_update != 0; | ||
| 587 | } | ||
| 588 | |||
| 589 | static int wm8776_input_mux_get(struct snd_kcontrol *ctl, | ||
| 590 | struct snd_ctl_elem_value *value) | ||
| 591 | { | ||
| 592 | struct oxygen *chip = ctl->private_data; | ||
| 593 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 594 | unsigned int mux_bit = ctl->private_value; | ||
| 595 | |||
| 596 | value->value.integer.value[0] = | ||
| 597 | !!(data->wm8776_regs[WM8776_ADCMUX] & mux_bit); | ||
| 598 | return 0; | ||
| 599 | } | ||
| 600 | |||
| 601 | static int wm8776_input_mux_put(struct snd_kcontrol *ctl, | ||
| 602 | struct snd_ctl_elem_value *value) | ||
| 603 | { | ||
| 604 | struct oxygen *chip = ctl->private_data; | ||
| 605 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 606 | unsigned int mux_bit = ctl->private_value; | ||
| 607 | u16 reg; | ||
| 608 | int changed; | ||
| 609 | |||
| 610 | mutex_lock(&chip->mutex); | ||
| 611 | reg = data->wm8776_regs[WM8776_ADCMUX]; | ||
| 612 | if (value->value.integer.value[0]) { | ||
| 613 | reg &= ~0x003; | ||
| 614 | reg |= mux_bit; | ||
| 615 | } else | ||
| 616 | reg &= ~mux_bit; | ||
| 617 | changed = reg != data->wm8776_regs[WM8776_ADCMUX]; | ||
| 618 | if (changed) { | ||
| 619 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | ||
| 620 | reg & 1 ? GPIO_DS_INPUT_ROUTE : 0, | ||
| 621 | GPIO_DS_INPUT_ROUTE); | ||
| 622 | wm8776_write(chip, WM8776_ADCMUX, reg); | ||
| 623 | } | ||
| 624 | mutex_unlock(&chip->mutex); | ||
| 625 | return changed; | ||
| 626 | } | ||
| 627 | |||
| 628 | static int wm8776_input_vol_info(struct snd_kcontrol *ctl, | ||
| 629 | struct snd_ctl_elem_info *info) | ||
| 630 | { | ||
| 631 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 632 | info->count = 2; | ||
| 633 | info->value.integer.min = 0xa5; | ||
| 634 | info->value.integer.max = 0xff; | ||
| 635 | return 0; | ||
| 636 | } | ||
| 637 | |||
| 638 | static int wm8776_input_vol_get(struct snd_kcontrol *ctl, | ||
| 639 | struct snd_ctl_elem_value *value) | ||
| 640 | { | ||
| 641 | struct oxygen *chip = ctl->private_data; | ||
| 642 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 643 | |||
| 644 | mutex_lock(&chip->mutex); | ||
| 645 | value->value.integer.value[0] = | ||
| 646 | data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK; | ||
| 647 | value->value.integer.value[1] = | ||
| 648 | data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK; | ||
| 649 | mutex_unlock(&chip->mutex); | ||
| 650 | return 0; | ||
| 651 | } | ||
| 652 | |||
| 653 | static int wm8776_input_vol_put(struct snd_kcontrol *ctl, | ||
| 654 | struct snd_ctl_elem_value *value) | ||
| 655 | { | ||
| 656 | struct oxygen *chip = ctl->private_data; | ||
| 657 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 658 | int changed = 0; | ||
| 659 | |||
| 660 | mutex_lock(&chip->mutex); | ||
| 661 | changed = (value->value.integer.value[0] != | ||
| 662 | (data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK)) || | ||
| 663 | (value->value.integer.value[1] != | ||
| 664 | (data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK)); | ||
| 665 | wm8776_write_cached(chip, WM8776_ADCLVOL, | ||
| 666 | value->value.integer.value[0] | WM8776_ZCA); | ||
| 667 | wm8776_write_cached(chip, WM8776_ADCRVOL, | ||
| 668 | value->value.integer.value[1] | WM8776_ZCA); | ||
| 669 | mutex_unlock(&chip->mutex); | ||
| 670 | return changed; | ||
| 671 | } | ||
| 672 | |||
| 673 | static int wm8776_level_control_info(struct snd_kcontrol *ctl, | ||
| 674 | struct snd_ctl_elem_info *info) | ||
| 675 | { | ||
| 676 | static const char *const names[3] = { | ||
| 677 | "None", "Peak Limiter", "Automatic Level Control" | ||
| 678 | }; | ||
| 679 | info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 680 | info->count = 1; | ||
| 681 | info->value.enumerated.items = 3; | ||
| 682 | if (info->value.enumerated.item >= 3) | ||
| 683 | info->value.enumerated.item = 2; | ||
| 684 | strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); | ||
| 685 | return 0; | ||
| 686 | } | ||
| 687 | |||
| 688 | static int wm8776_level_control_get(struct snd_kcontrol *ctl, | ||
| 689 | struct snd_ctl_elem_value *value) | ||
| 690 | { | ||
| 691 | struct oxygen *chip = ctl->private_data; | ||
| 692 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 693 | |||
| 694 | if (!(data->wm8776_regs[WM8776_ALCCTRL2] & WM8776_LCEN)) | ||
| 695 | value->value.enumerated.item[0] = 0; | ||
| 696 | else if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) == | ||
| 697 | WM8776_LCSEL_LIMITER) | ||
| 698 | value->value.enumerated.item[0] = 1; | ||
| 699 | else | ||
| 700 | value->value.enumerated.item[0] = 2; | ||
| 701 | return 0; | ||
| 702 | } | ||
| 703 | |||
| 704 | static void activate_control(struct oxygen *chip, | ||
| 705 | struct snd_kcontrol *ctl, unsigned int mode) | ||
| 706 | { | ||
| 707 | unsigned int access; | ||
| 708 | |||
| 709 | if (ctl->private_value & mode) | ||
| 710 | access = 0; | ||
| 711 | else | ||
| 712 | access = SNDRV_CTL_ELEM_ACCESS_INACTIVE; | ||
| 713 | if ((ctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) != access) { | ||
| 714 | ctl->vd[0].access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE; | ||
| 715 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); | ||
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 719 | static int wm8776_level_control_put(struct snd_kcontrol *ctl, | ||
| 720 | struct snd_ctl_elem_value *value) | ||
| 721 | { | ||
| 722 | struct oxygen *chip = ctl->private_data; | ||
| 723 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 724 | unsigned int mode = 0, i; | ||
| 725 | u16 ctrl1, ctrl2; | ||
| 726 | int changed; | ||
| 727 | |||
| 728 | if (value->value.enumerated.item[0] >= 3) | ||
| 729 | return -EINVAL; | ||
| 730 | mutex_lock(&chip->mutex); | ||
| 731 | changed = value->value.enumerated.item[0] != ctl->private_value; | ||
| 732 | if (changed) { | ||
| 733 | ctl->private_value = value->value.enumerated.item[0]; | ||
| 734 | ctrl1 = data->wm8776_regs[WM8776_ALCCTRL1]; | ||
| 735 | ctrl2 = data->wm8776_regs[WM8776_ALCCTRL2]; | ||
| 736 | switch (value->value.enumerated.item[0]) { | ||
| 737 | default: | ||
| 738 | wm8776_write_cached(chip, WM8776_ALCCTRL2, | ||
| 739 | ctrl2 & ~WM8776_LCEN); | ||
| 740 | break; | ||
| 741 | case 1: | ||
| 742 | wm8776_write_cached(chip, WM8776_ALCCTRL1, | ||
| 743 | (ctrl1 & ~WM8776_LCSEL_MASK) | | ||
| 744 | WM8776_LCSEL_LIMITER); | ||
| 745 | wm8776_write_cached(chip, WM8776_ALCCTRL2, | ||
| 746 | ctrl2 | WM8776_LCEN); | ||
| 747 | mode = LC_CONTROL_LIMITER; | ||
| 748 | break; | ||
| 749 | case 2: | ||
| 750 | wm8776_write_cached(chip, WM8776_ALCCTRL1, | ||
| 751 | (ctrl1 & ~WM8776_LCSEL_MASK) | | ||
| 752 | WM8776_LCSEL_ALC_STEREO); | ||
| 753 | wm8776_write_cached(chip, WM8776_ALCCTRL2, | ||
| 754 | ctrl2 | WM8776_LCEN); | ||
| 755 | mode = LC_CONTROL_ALC; | ||
| 756 | break; | ||
| 757 | } | ||
| 758 | for (i = 0; i < ARRAY_SIZE(data->lc_controls); ++i) | ||
| 759 | activate_control(chip, data->lc_controls[i], mode); | ||
| 760 | } | ||
| 761 | mutex_unlock(&chip->mutex); | ||
| 762 | return changed; | ||
| 763 | } | ||
| 764 | |||
| 765 | static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) | ||
| 766 | { | ||
| 767 | static const char *const names[2] = { | ||
| 768 | "None", "High-pass Filter" | ||
| 769 | }; | ||
| 770 | |||
| 771 | info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 772 | info->count = 1; | ||
| 773 | info->value.enumerated.items = 2; | ||
| 774 | if (info->value.enumerated.item >= 2) | ||
| 775 | info->value.enumerated.item = 1; | ||
| 776 | strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); | ||
| 777 | return 0; | ||
| 778 | } | ||
| 779 | |||
| 780 | static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) | ||
| 781 | { | ||
| 782 | struct oxygen *chip = ctl->private_data; | ||
| 783 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 784 | |||
| 785 | value->value.enumerated.item[0] = | ||
| 786 | !(data->wm8776_regs[WM8776_ADCIFCTRL] & WM8776_ADCHPD); | ||
| 787 | return 0; | ||
| 788 | } | ||
| 789 | |||
| 790 | static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) | ||
| 791 | { | ||
| 792 | struct oxygen *chip = ctl->private_data; | ||
| 793 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 794 | unsigned int reg; | ||
| 795 | int changed; | ||
| 796 | |||
| 797 | mutex_lock(&chip->mutex); | ||
| 798 | reg = data->wm8776_regs[WM8776_ADCIFCTRL] & ~WM8776_ADCHPD; | ||
| 799 | if (!value->value.enumerated.item[0]) | ||
| 800 | reg |= WM8776_ADCHPD; | ||
| 801 | changed = reg != data->wm8776_regs[WM8776_ADCIFCTRL]; | ||
| 802 | if (changed) | ||
| 803 | wm8776_write(chip, WM8776_ADCIFCTRL, reg); | ||
| 804 | mutex_unlock(&chip->mutex); | ||
| 805 | return changed; | ||
| 806 | } | ||
| 807 | |||
| 808 | #define WM8776_BIT_SWITCH(xname, reg, bit, invert, flags) { \ | ||
| 809 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
| 810 | .name = xname, \ | ||
| 811 | .info = snd_ctl_boolean_mono_info, \ | ||
| 812 | .get = wm8776_bit_switch_get, \ | ||
| 813 | .put = wm8776_bit_switch_put, \ | ||
| 814 | .private_value = ((reg) << 16) | (bit) | ((invert) << 24) | (flags), \ | ||
| 815 | } | ||
| 816 | #define _WM8776_FIELD_CTL(xname, reg, shift, initval, min, max, mask, flags) \ | ||
| 817 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
| 818 | .name = xname, \ | ||
| 819 | .private_value = (initval) | ((min) << 8) | ((max) << 12) | \ | ||
| 820 | ((mask) << 16) | ((shift) << 20) | ((reg) << 24) | (flags) | ||
| 821 | #define WM8776_FIELD_CTL_ENUM(xname, reg, shift, init, min, max, mask, flags) {\ | ||
| 822 | _WM8776_FIELD_CTL(xname " Capture Enum", \ | ||
| 823 | reg, shift, init, min, max, mask, flags), \ | ||
| 824 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ | ||
| 825 | SNDRV_CTL_ELEM_ACCESS_INACTIVE, \ | ||
| 826 | .info = wm8776_field_enum_info, \ | ||
| 827 | .get = wm8776_field_enum_get, \ | ||
| 828 | .put = wm8776_field_enum_put, \ | ||
| 829 | } | ||
| 830 | #define WM8776_FIELD_CTL_VOLUME(a, b, c, d, e, f, g, h, tlv_p) { \ | ||
| 831 | _WM8776_FIELD_CTL(a " Capture Volume", b, c, d, e, f, g, h), \ | ||
| 832 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ | ||
| 833 | SNDRV_CTL_ELEM_ACCESS_INACTIVE | \ | ||
| 834 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ | ||
| 835 | .info = wm8776_field_volume_info, \ | ||
| 836 | .get = wm8776_field_volume_get, \ | ||
| 837 | .put = wm8776_field_volume_put, \ | ||
| 838 | .tlv = { .p = tlv_p }, \ | ||
| 839 | } | ||
| 840 | |||
| 841 | static const DECLARE_TLV_DB_SCALE(wm87x6_dac_db_scale, -6000, 50, 0); | ||
| 842 | static const DECLARE_TLV_DB_SCALE(wm8776_adc_db_scale, -2100, 50, 0); | ||
| 843 | static const DECLARE_TLV_DB_SCALE(wm8776_hp_db_scale, -6000, 100, 0); | ||
| 844 | static const DECLARE_TLV_DB_SCALE(wm8776_lct_db_scale, -1600, 100, 0); | ||
| 845 | static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_db_scale, 0, 400, 0); | ||
| 846 | static const DECLARE_TLV_DB_SCALE(wm8776_ngth_db_scale, -7800, 600, 0); | ||
| 847 | static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_db_scale, -1200, 100, 0); | ||
| 848 | static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_db_scale, -2100, 400, 0); | ||
| 849 | |||
| 850 | static const struct snd_kcontrol_new ds_controls[] = { | ||
| 851 | { | ||
| 852 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 853 | .name = "Headphone Playback Volume", | ||
| 854 | .info = wm8776_hp_vol_info, | ||
| 855 | .get = wm8776_hp_vol_get, | ||
| 856 | .put = wm8776_hp_vol_put, | ||
| 857 | .tlv = { .p = wm8776_hp_db_scale }, | ||
| 858 | }, | ||
| 859 | WM8776_BIT_SWITCH("Headphone Playback Switch", | ||
| 860 | WM8776_PWRDOWN, WM8776_HPPD, 1, 0), | ||
| 861 | { | ||
| 862 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 863 | .name = "Input Capture Volume", | ||
| 864 | .info = wm8776_input_vol_info, | ||
| 865 | .get = wm8776_input_vol_get, | ||
| 866 | .put = wm8776_input_vol_put, | ||
| 867 | .tlv = { .p = wm8776_adc_db_scale }, | ||
| 868 | }, | ||
| 869 | { | ||
| 870 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 871 | .name = "Line Capture Switch", | ||
| 872 | .info = snd_ctl_boolean_mono_info, | ||
| 873 | .get = wm8776_input_mux_get, | ||
| 874 | .put = wm8776_input_mux_put, | ||
| 875 | .private_value = 1 << 0, | ||
| 876 | }, | ||
| 877 | { | ||
| 878 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 879 | .name = "Mic Capture Switch", | ||
| 880 | .info = snd_ctl_boolean_mono_info, | ||
| 881 | .get = wm8776_input_mux_get, | ||
| 882 | .put = wm8776_input_mux_put, | ||
| 883 | .private_value = 1 << 1, | ||
| 884 | }, | ||
| 885 | WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0), | ||
| 886 | { | ||
| 887 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 888 | .name = "ADC Filter Capture Enum", | ||
| 889 | .info = hpf_info, | ||
| 890 | .get = hpf_get, | ||
| 891 | .put = hpf_put, | ||
| 892 | }, | ||
| 893 | { | ||
| 894 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 895 | .name = "Level Control Capture Enum", | ||
| 896 | .info = wm8776_level_control_info, | ||
| 897 | .get = wm8776_level_control_get, | ||
| 898 | .put = wm8776_level_control_put, | ||
| 899 | .private_value = 0, | ||
| 900 | }, | ||
| 901 | }; | ||
| 902 | static const struct snd_kcontrol_new lc_controls[] = { | ||
| 903 | WM8776_FIELD_CTL_VOLUME("Limiter Threshold", | ||
| 904 | WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, | ||
| 905 | LC_CONTROL_LIMITER, wm8776_lct_db_scale), | ||
| 906 | WM8776_FIELD_CTL_ENUM("Limiter Attack Time", | ||
| 907 | WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf, | ||
| 908 | LC_CONTROL_LIMITER), | ||
| 909 | WM8776_FIELD_CTL_ENUM("Limiter Decay Time", | ||
| 910 | WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf, | ||
| 911 | LC_CONTROL_LIMITER), | ||
| 912 | WM8776_FIELD_CTL_ENUM("Limiter Transient Window", | ||
| 913 | WM8776_LIMITER, 4, 2, 0, 7, 0x7, | ||
| 914 | LC_CONTROL_LIMITER), | ||
| 915 | WM8776_FIELD_CTL_VOLUME("Limiter Maximum Attenuation", | ||
| 916 | WM8776_LIMITER, 0, 6, 3, 12, 0xf, | ||
| 917 | LC_CONTROL_LIMITER, | ||
| 918 | wm8776_maxatten_lim_db_scale), | ||
| 919 | WM8776_FIELD_CTL_VOLUME("ALC Target Level", | ||
| 920 | WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, | ||
| 921 | LC_CONTROL_ALC, wm8776_lct_db_scale), | ||
| 922 | WM8776_FIELD_CTL_ENUM("ALC Attack Time", | ||
| 923 | WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf, | ||
| 924 | LC_CONTROL_ALC), | ||
| 925 | WM8776_FIELD_CTL_ENUM("ALC Decay Time", | ||
| 926 | WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf, | ||
| 927 | LC_CONTROL_ALC), | ||
| 928 | WM8776_FIELD_CTL_VOLUME("ALC Maximum Gain", | ||
| 929 | WM8776_ALCCTRL1, 4, 7, 1, 7, 0x7, | ||
| 930 | LC_CONTROL_ALC, wm8776_maxgain_db_scale), | ||
| 931 | WM8776_FIELD_CTL_VOLUME("ALC Maximum Attenuation", | ||
| 932 | WM8776_LIMITER, 0, 10, 10, 15, 0xf, | ||
| 933 | LC_CONTROL_ALC, wm8776_maxatten_alc_db_scale), | ||
| 934 | WM8776_FIELD_CTL_ENUM("ALC Hold Time", | ||
| 935 | WM8776_ALCCTRL2, 0, 0, 0, 15, 0xf, | ||
| 936 | LC_CONTROL_ALC), | ||
| 937 | WM8776_BIT_SWITCH("Noise Gate Capture Switch", | ||
| 938 | WM8776_NOISEGATE, WM8776_NGAT, 0, | ||
| 939 | LC_CONTROL_ALC), | ||
| 940 | WM8776_FIELD_CTL_VOLUME("Noise Gate Threshold", | ||
| 941 | WM8776_NOISEGATE, 2, 0, 0, 7, 0x7, | ||
| 942 | LC_CONTROL_ALC, wm8776_ngth_db_scale), | ||
| 943 | }; | ||
| 944 | |||
| 945 | static int xonar_ds_control_filter(struct snd_kcontrol_new *template) | ||
| 946 | { | ||
| 947 | if (!strncmp(template->name, "CD Capture ", 11)) | ||
| 948 | return 1; /* no CD input */ | ||
| 949 | return 0; | ||
| 950 | } | ||
| 951 | |||
| 952 | static int xonar_ds_mixer_init(struct oxygen *chip) | ||
| 953 | { | ||
| 954 | struct xonar_wm87x6 *data = chip->model_data; | ||
| 955 | unsigned int i; | ||
| 956 | struct snd_kcontrol *ctl; | ||
| 957 | int err; | ||
| 958 | |||
| 959 | for (i = 0; i < ARRAY_SIZE(ds_controls); ++i) { | ||
| 960 | ctl = snd_ctl_new1(&ds_controls[i], chip); | ||
| 961 | if (!ctl) | ||
| 962 | return -ENOMEM; | ||
| 963 | err = snd_ctl_add(chip->card, ctl); | ||
| 964 | if (err < 0) | ||
| 965 | return err; | ||
| 966 | } | ||
| 967 | BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); | ||
| 968 | for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { | ||
| 969 | ctl = snd_ctl_new1(&lc_controls[i], chip); | ||
| 970 | if (!ctl) | ||
| 971 | return -ENOMEM; | ||
| 972 | err = snd_ctl_add(chip->card, ctl); | ||
| 973 | if (err < 0) | ||
| 974 | return err; | ||
| 975 | data->lc_controls[i] = ctl; | ||
| 976 | } | ||
| 977 | return 0; | ||
| 978 | } | ||
| 979 | |||
| 980 | static const struct oxygen_model model_xonar_ds = { | ||
| 981 | .shortname = "Xonar DS", | ||
| 982 | .longname = "Asus Virtuoso 200", | ||
| 983 | .chip = "AV200", | ||
| 984 | .init = xonar_ds_init, | ||
| 985 | .control_filter = xonar_ds_control_filter, | ||
| 986 | .mixer_init = xonar_ds_mixer_init, | ||
| 987 | .cleanup = xonar_ds_cleanup, | ||
| 988 | .suspend = xonar_ds_suspend, | ||
| 989 | .resume = xonar_ds_resume, | ||
| 990 | .pcm_hardware_filter = wm8776_adc_hardware_filter, | ||
| 991 | .get_i2s_mclk = oxygen_default_i2s_mclk, | ||
| 992 | .set_dac_params = set_wm87x6_dac_params, | ||
| 993 | .set_adc_params = set_wm8776_adc_params, | ||
| 994 | .update_dac_volume = update_wm87x6_volume, | ||
| 995 | .update_dac_mute = update_wm87x6_mute, | ||
| 996 | .gpio_changed = xonar_ds_gpio_changed, | ||
| 997 | .dac_tlv = wm87x6_dac_db_scale, | ||
| 998 | .model_data_size = sizeof(struct xonar_wm87x6), | ||
| 999 | .device_config = PLAYBACK_0_TO_I2S | | ||
| 1000 | PLAYBACK_1_TO_SPDIF | | ||
| 1001 | CAPTURE_0_FROM_I2S_1, | ||
| 1002 | .dac_channels = 8, | ||
| 1003 | .dac_volume_min = 255 - 2*60, | ||
| 1004 | .dac_volume_max = 255, | ||
| 1005 | .function_flags = OXYGEN_FUNCTION_SPI, | ||
| 1006 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
| 1007 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
| 1008 | }; | ||
| 1009 | |||
| 1010 | int __devinit get_xonar_wm87x6_model(struct oxygen *chip, | ||
| 1011 | const struct pci_device_id *id) | ||
| 1012 | { | ||
| 1013 | switch (id->subdevice) { | ||
| 1014 | case 0x838e: | ||
| 1015 | chip->model = model_xonar_ds; | ||
| 1016 | break; | ||
| 1017 | default: | ||
| 1018 | return -EINVAL; | ||
| 1019 | } | ||
| 1020 | return 0; | ||
| 1021 | } | ||
