Commit 66410bfdf14f7c2ad3b2d4a8adeab41d368b6f05

Authored by Clemens Ladisch
Committed by Takashi Iwai
1 parent 8443d2eb81

ALSA: oxygen: add Xonar DG support

Add experimental support for the Asus Xonar DG sound card.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

Showing 8 changed files with 735 additions and 6 deletions Side-by-side Diff

Documentation/sound/alsa/ALSA-Configuration.txt
... ... @@ -1524,8 +1524,9 @@
1524 1524 Module snd-oxygen
1525 1525 -----------------
1526 1526  
1527   - Module for sound cards based on the C-Media CMI8787/8788 chip:
  1527 + Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
1528 1528 * Asound A-8788
  1529 + * Asus Xonar DG
1529 1530 * AuzenTech X-Meridian
1530 1531 * AuzenTech X-Meridian 2G
1531 1532 * Bgears b-Enspirer
... ... @@ -209,7 +209,7 @@
209 209 tristate
210 210  
211 211 config SND_OXYGEN
212   - tristate "C-Media 8787, 8788 (Oxygen)"
  212 + tristate "C-Media 8786, 8787, 8788 (Oxygen)"
213 213 select SND_OXYGEN_LIB
214 214 select SND_PCM
215 215 select SND_MPU401_UART
... ... @@ -217,6 +217,7 @@
217 217 Say Y here to include support for sound cards based on the
218 218 C-Media CMI8788 (Oxygen HD Audio) chip:
219 219 * Asound A-8788
  220 + * Asus Xonar DG
220 221 * AuzenTech X-Meridian
221 222 * AuzenTech X-Meridian 2G
222 223 * Bgears b-Enspirer
sound/pci/oxygen/Makefile
1 1 snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
2   -snd-oxygen-objs := oxygen.o
  2 +snd-oxygen-objs := oxygen.o xonar_dg.o
3 3 snd-virtuoso-objs := virtuoso.o xonar_lib.o \
4 4 xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
5 5  
sound/pci/oxygen/cs4245.h
  1 +#define CS4245_CHIP_ID 0x01
  2 +#define CS4245_POWER_CTRL 0x02
  3 +#define CS4245_DAC_CTRL_1 0x03
  4 +#define CS4245_ADC_CTRL 0x04
  5 +#define CS4245_MCLK_FREQ 0x05
  6 +#define CS4245_SIGNAL_SEL 0x06
  7 +#define CS4245_PGA_B_CTRL 0x07
  8 +#define CS4245_PGA_A_CTRL 0x08
  9 +#define CS4245_ANALOG_IN 0x09
  10 +#define CS4245_DAC_A_CTRL 0x0a
  11 +#define CS4245_DAC_B_CTRL 0x0b
  12 +#define CS4245_DAC_CTRL_2 0x0c
  13 +#define CS4245_INT_STATUS 0x0d
  14 +#define CS4245_INT_MASK 0x0e
  15 +#define CS4245_INT_MODE_MSB 0x0f
  16 +#define CS4245_INT_MODE_LSB 0x10
  17 +
  18 +/* Chip ID */
  19 +#define CS4245_CHIP_PART_MASK 0xf0
  20 +#define CS4245_CHIP_REV_MASK 0x0f
  21 +
  22 +/* Power Control */
  23 +#define CS4245_FREEZE 0x80
  24 +#define CS4245_PDN_MIC 0x08
  25 +#define CS4245_PDN_ADC 0x04
  26 +#define CS4245_PDN_DAC 0x02
  27 +#define CS4245_PDN 0x01
  28 +
  29 +/* DAC Control */
  30 +#define CS4245_DAC_FM_MASK 0xc0
  31 +#define CS4245_DAC_FM_SINGLE 0x00
  32 +#define CS4245_DAC_FM_DOUBLE 0x40
  33 +#define CS4245_DAC_FM_QUAD 0x80
  34 +#define CS4245_DAC_DIF_MASK 0x30
  35 +#define CS4245_DAC_DIF_LJUST 0x00
  36 +#define CS4245_DAC_DIF_I2S 0x10
  37 +#define CS4245_DAC_DIF_RJUST_16 0x20
  38 +#define CS4245_DAC_DIF_RJUST_24 0x30
  39 +#define CS4245_RESERVED_1 0x08
  40 +#define CS4245_MUTE_DAC 0x04
  41 +#define CS4245_DEEMPH 0x02
  42 +#define CS4245_DAC_MASTER 0x01
  43 +
  44 +/* ADC Control */
  45 +#define CS4245_ADC_FM_MASK 0xc0
  46 +#define CS4245_ADC_FM_SINGLE 0x00
  47 +#define CS4245_ADC_FM_DOUBLE 0x40
  48 +#define CS4245_ADC_FM_QUAD 0x80
  49 +#define CS4245_ADC_DIF_MASK 0x10
  50 +#define CS4245_ADC_DIF_LJUST 0x00
  51 +#define CS4245_ADC_DIF_I2S 0x10
  52 +#define CS4245_MUTE_ADC 0x04
  53 +#define CS4245_HPF_FREEZE 0x02
  54 +#define CS4245_ADC_MASTER 0x01
  55 +
  56 +/* MCLK Frequency */
  57 +#define CS4245_MCLK1_MASK 0x70
  58 +#define CS4245_MCLK1_SHIFT 4
  59 +#define CS4245_MCLK2_MASK 0x07
  60 +#define CS4245_MCLK2_SHIFT 0
  61 +#define CS4245_MCLK_1 0
  62 +#define CS4245_MCLK_1_5 1
  63 +#define CS4245_MCLK_2 2
  64 +#define CS4245_MCLK_3 3
  65 +#define CS4245_MCLK_4 4
  66 +
  67 +/* Signal Selection */
  68 +#define CS4245_A_OUT_SEL_MASK 0x60
  69 +#define CS4245_A_OUT_SEL_HIZ 0x00
  70 +#define CS4245_A_OUT_SEL_DAC 0x20
  71 +#define CS4245_A_OUT_SEL_PGA 0x40
  72 +#define CS4245_LOOP 0x02
  73 +#define CS4245_ASYNCH 0x01
  74 +
  75 +/* Channel B/A PGA Control */
  76 +#define CS4245_PGA_GAIN_MASK 0x3f
  77 +
  78 +/* ADC Input Control */
  79 +#define CS4245_PGA_SOFT 0x10
  80 +#define CS4245_PGA_ZERO 0x08
  81 +#define CS4245_SEL_MASK 0x07
  82 +#define CS4245_SEL_MIC 0x00
  83 +#define CS4245_SEL_INPUT_1 0x01
  84 +#define CS4245_SEL_INPUT_2 0x02
  85 +#define CS4245_SEL_INPUT_3 0x03
  86 +#define CS4245_SEL_INPUT_4 0x04
  87 +#define CS4245_SEL_INPUT_5 0x05
  88 +#define CS4245_SEL_INPUT_6 0x06
  89 +
  90 +/* DAC Channel A/B Volume Control */
  91 +#define CS4245_VOL_MASK 0xff
  92 +
  93 +/* DAC Control 2 */
  94 +#define CS4245_DAC_SOFT 0x80
  95 +#define CS4245_DAC_ZERO 0x40
  96 +#define CS4245_INVERT_DAC 0x20
  97 +#define CS4245_INT_ACTIVE_HIGH 0x01
  98 +
  99 +/* Interrupt Status/Mask/Mode */
  100 +#define CS4245_ADC_CLK_ERR 0x08
  101 +#define CS4245_DAC_CLK_ERR 0x04
  102 +#define CS4245_ADC_OVFL 0x02
  103 +#define CS4245_ADC_UNDRFL 0x01
  104 +
  105 +
  106 +#define CS4245_SPI_ADDRESS (0x9e << 16)
  107 +#define CS4245_SPI_WRITE (0 << 16)
sound/pci/oxygen/oxygen.c
... ... @@ -53,13 +53,16 @@
53 53 #include <sound/pcm_params.h>
54 54 #include <sound/tlv.h>
55 55 #include "oxygen.h"
  56 +#include "xonar_dg.h"
56 57 #include "ak4396.h"
57 58 #include "wm8785.h"
58 59  
59 60 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
60 61 MODULE_DESCRIPTION("C-Media CMI8788 driver");
61 62 MODULE_LICENSE("GPL v2");
62   -MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
  63 +MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8786}"
  64 + ",{C-Media,CMI8787}"
  65 + ",{C-Media,CMI8788}}");
63 66  
64 67 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
65 68 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
... ... @@ -79,6 +82,7 @@
79 82 MODEL_CLARO_HALO,
80 83 MODEL_FANTASIA,
81 84 MODEL_2CH_OUTPUT,
  85 + MODEL_XONAR_DG,
82 86 };
83 87  
84 88 static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
... ... @@ -92,6 +96,8 @@
92 96 { OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
93 97 { OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
94 98 { OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
  99 + /* Asus Xonar DG */
  100 + { OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
95 101 /* PCI 2.0 HD Audio */
96 102 { OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
97 103 /* Kuroutoshikou CMI8787-HG2PCI */
... ... @@ -654,6 +660,9 @@
654 660 }
655 661 chip->model.dac_channels_pcm = 2;
656 662 chip->model.dac_channels_mixer = 2;
  663 + break;
  664 + case MODEL_XONAR_DG:
  665 + chip->model = model_xonar_dg;
657 666 break;
658 667 }
659 668 if (id->driver_data == MODEL_MERIDIAN ||
sound/pci/oxygen/oxygen_mixer.c
... ... @@ -97,6 +97,16 @@
97 97 return changed;
98 98 }
99 99  
  100 +static unsigned int upmix_item_count(struct oxygen *chip)
  101 +{
  102 + if (chip->model.dac_channels_pcm < 8)
  103 + return 2;
  104 + else if (chip->model.update_center_lfe_mix)
  105 + return 5;
  106 + else
  107 + return 3;
  108 +}
  109 +
100 110 static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
101 111 {
102 112 static const char *const names[5] = {
... ... @@ -107,7 +117,7 @@
107 117 "Front+Surround+Center/LFE+Back",
108 118 };
109 119 struct oxygen *chip = ctl->private_data;
110   - unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
  120 + unsigned int count = upmix_item_count(chip);
111 121  
112 122 info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
113 123 info->count = 1;
... ... @@ -188,7 +198,7 @@
188 198 static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
189 199 {
190 200 struct oxygen *chip = ctl->private_data;
191   - unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
  201 + unsigned int count = upmix_item_count(chip);
192 202 int changed;
193 203  
194 204 if (value->value.enumerated.item[0] >= count)
sound/pci/oxygen/xonar_dg.c
  1 +/*
  2 + * card driver for the Xonar DG
  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 DG
  21 + * --------
  22 + *
  23 + * CMI8788:
  24 + *
  25 + * SPI 0 -> CS4245
  26 + *
  27 + * GPIO 3 <- ?
  28 + * GPIO 4 <- headphone detect
  29 + * GPIO 5 -> route input jack to line-in (0) or mic-in (1)
  30 + * GPIO 6 -> route input jack to line-in (0) or mic-in (1)
  31 + * GPIO 7 -> enable rear headphone amp
  32 + * GPIO 8 -> enable output to speakers
  33 + *
  34 + * CS4245:
  35 + *
  36 + * input 1 <- aux
  37 + * input 2 <- front mic
  38 + * input 4 <- line/mic
  39 + * aux out -> front panel headphones
  40 + */
  41 +
  42 +#include <linux/pci.h>
  43 +#include <sound/control.h>
  44 +#include <sound/core.h>
  45 +#include <sound/info.h>
  46 +#include <sound/pcm.h>
  47 +#include <sound/tlv.h>
  48 +#include "oxygen.h"
  49 +#include "xonar_dg.h"
  50 +#include "cs4245.h"
  51 +
  52 +#define GPIO_MAGIC 0x0008
  53 +#define GPIO_HP_DETECT 0x0010
  54 +#define GPIO_INPUT_ROUTE 0x0060
  55 +#define GPIO_HP_REAR 0x0080
  56 +#define GPIO_OUTPUT_ENABLE 0x0100
  57 +
  58 +struct dg {
  59 + unsigned int output_sel;
  60 + s8 input_vol[4][2];
  61 + unsigned int input_sel;
  62 + u8 hp_vol_att;
  63 + u8 cs4245_regs[0x11];
  64 +};
  65 +
  66 +static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
  67 +{
  68 + struct dg *data = chip->model_data;
  69 +
  70 + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
  71 + OXYGEN_SPI_DATA_LENGTH_3 |
  72 + OXYGEN_SPI_CLOCK_1280 |
  73 + (0 << OXYGEN_SPI_CODEC_SHIFT) |
  74 + OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
  75 + CS4245_SPI_ADDRESS |
  76 + CS4245_SPI_WRITE |
  77 + (value << 8) | reg);
  78 + data->cs4245_regs[reg] = value;
  79 +}
  80 +
  81 +static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
  82 +{
  83 + struct dg *data = chip->model_data;
  84 +
  85 + if (value != data->cs4245_regs[reg])
  86 + cs4245_write(chip, reg, value);
  87 +}
  88 +
  89 +static void cs4245_registers_init(struct oxygen *chip)
  90 +{
  91 + struct dg *data = chip->model_data;
  92 +
  93 + cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN);
  94 + cs4245_write(chip, CS4245_DAC_CTRL_1,
  95 + data->cs4245_regs[CS4245_DAC_CTRL_1]);
  96 + cs4245_write(chip, CS4245_ADC_CTRL,
  97 + data->cs4245_regs[CS4245_ADC_CTRL]);
  98 + cs4245_write(chip, CS4245_SIGNAL_SEL,
  99 + data->cs4245_regs[CS4245_SIGNAL_SEL]);
  100 + cs4245_write(chip, CS4245_PGA_B_CTRL,
  101 + data->cs4245_regs[CS4245_PGA_B_CTRL]);
  102 + cs4245_write(chip, CS4245_PGA_A_CTRL,
  103 + data->cs4245_regs[CS4245_PGA_A_CTRL]);
  104 + cs4245_write(chip, CS4245_ANALOG_IN,
  105 + data->cs4245_regs[CS4245_ANALOG_IN]);
  106 + cs4245_write(chip, CS4245_DAC_A_CTRL,
  107 + data->cs4245_regs[CS4245_DAC_A_CTRL]);
  108 + cs4245_write(chip, CS4245_DAC_B_CTRL,
  109 + data->cs4245_regs[CS4245_DAC_B_CTRL]);
  110 + cs4245_write(chip, CS4245_DAC_CTRL_2,
  111 + CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC);
  112 + cs4245_write(chip, CS4245_INT_MASK, 0);
  113 + cs4245_write(chip, CS4245_POWER_CTRL, 0);
  114 +}
  115 +
  116 +static void cs4245_init(struct oxygen *chip)
  117 +{
  118 + struct dg *data = chip->model_data;
  119 +
  120 + data->cs4245_regs[CS4245_DAC_CTRL_1] =
  121 + CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
  122 + data->cs4245_regs[CS4245_ADC_CTRL] =
  123 + CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
  124 + data->cs4245_regs[CS4245_SIGNAL_SEL] =
  125 + CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH;
  126 + data->cs4245_regs[CS4245_PGA_B_CTRL] = 0;
  127 + data->cs4245_regs[CS4245_PGA_A_CTRL] = 0;
  128 + data->cs4245_regs[CS4245_ANALOG_IN] =
  129 + CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4;
  130 + data->cs4245_regs[CS4245_DAC_A_CTRL] = 0;
  131 + data->cs4245_regs[CS4245_DAC_B_CTRL] = 0;
  132 + cs4245_registers_init(chip);
  133 + snd_component_add(chip->card, "CS4245");
  134 +}
  135 +
  136 +static void dg_output_enable(struct oxygen *chip)
  137 +{
  138 + msleep(2500);
  139 + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
  140 +}
  141 +
  142 +static void dg_init(struct oxygen *chip)
  143 +{
  144 + struct dg *data = chip->model_data;
  145 +
  146 + data->output_sel = 0;
  147 + data->input_sel = 3;
  148 + data->hp_vol_att = 2 * 16;
  149 +
  150 + cs4245_init(chip);
  151 +
  152 + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
  153 + GPIO_MAGIC | GPIO_HP_DETECT);
  154 + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
  155 + GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE);
  156 + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
  157 + GPIO_INPUT_ROUTE | GPIO_HP_REAR);
  158 + dg_output_enable(chip);
  159 +}
  160 +
  161 +static void dg_cleanup(struct oxygen *chip)
  162 +{
  163 + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
  164 +}
  165 +
  166 +static void dg_suspend(struct oxygen *chip)
  167 +{
  168 + dg_cleanup(chip);
  169 +}
  170 +
  171 +static void dg_resume(struct oxygen *chip)
  172 +{
  173 + cs4245_registers_init(chip);
  174 + dg_output_enable(chip);
  175 +}
  176 +
  177 +static void set_cs4245_dac_params(struct oxygen *chip,
  178 + struct snd_pcm_hw_params *params)
  179 +{
  180 + struct dg *data = chip->model_data;
  181 + u8 value;
  182 +
  183 + value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
  184 + if (params_rate(params) <= 50000)
  185 + value |= CS4245_DAC_FM_SINGLE;
  186 + else if (params_rate(params) <= 100000)
  187 + value |= CS4245_DAC_FM_DOUBLE;
  188 + else
  189 + value |= CS4245_DAC_FM_QUAD;
  190 + cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value);
  191 +}
  192 +
  193 +static void set_cs4245_adc_params(struct oxygen *chip,
  194 + struct snd_pcm_hw_params *params)
  195 +{
  196 + struct dg *data = chip->model_data;
  197 + u8 value;
  198 +
  199 + value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
  200 + if (params_rate(params) <= 50000)
  201 + value |= CS4245_ADC_FM_SINGLE;
  202 + else if (params_rate(params) <= 100000)
  203 + value |= CS4245_ADC_FM_DOUBLE;
  204 + else
  205 + value |= CS4245_ADC_FM_QUAD;
  206 + cs4245_write_cached(chip, CS4245_ADC_CTRL, value);
  207 +}
  208 +
  209 +static int output_switch_info(struct snd_kcontrol *ctl,
  210 + struct snd_ctl_elem_info *info)
  211 +{
  212 + static const char *const names[3] = {
  213 + "Speakers", "Headphones", "FP Headphones"
  214 + };
  215 +
  216 + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
  217 + info->count = 1;
  218 + info->value.enumerated.items = 3;
  219 + if (info->value.enumerated.item >= 3)
  220 + info->value.enumerated.item = 2;
  221 + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
  222 + return 0;
  223 +}
  224 +
  225 +static int output_switch_get(struct snd_kcontrol *ctl,
  226 + struct snd_ctl_elem_value *value)
  227 +{
  228 + struct oxygen *chip = ctl->private_data;
  229 + struct dg *data = chip->model_data;
  230 +
  231 + mutex_lock(&chip->mutex);
  232 + value->value.enumerated.item[0] = data->output_sel;
  233 + mutex_unlock(&chip->mutex);
  234 + return 0;
  235 +}
  236 +
  237 +static int output_switch_put(struct snd_kcontrol *ctl,
  238 + struct snd_ctl_elem_value *value)
  239 +{
  240 + struct oxygen *chip = ctl->private_data;
  241 + struct dg *data = chip->model_data;
  242 + u8 reg;
  243 + int changed;
  244 +
  245 + if (value->value.enumerated.item[0] > 2)
  246 + return -EINVAL;
  247 +
  248 + mutex_lock(&chip->mutex);
  249 + changed = value->value.enumerated.item[0] != data->output_sel;
  250 + if (changed) {
  251 + data->output_sel = value->value.enumerated.item[0];
  252 +
  253 + reg = data->cs4245_regs[CS4245_SIGNAL_SEL] &
  254 + ~CS4245_A_OUT_SEL_MASK;
  255 + reg |= data->output_sel == 2 ?
  256 + CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
  257 + cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
  258 +
  259 + cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
  260 + data->output_sel ? data->hp_vol_att : 0);
  261 + cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
  262 + data->output_sel ? data->hp_vol_att : 0);
  263 +
  264 + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
  265 + data->output_sel == 1 ? GPIO_HP_REAR : 0,
  266 + GPIO_HP_REAR);
  267 + }
  268 + mutex_unlock(&chip->mutex);
  269 + return changed;
  270 +}
  271 +
  272 +static int hp_volume_offset_info(struct snd_kcontrol *ctl,
  273 + struct snd_ctl_elem_info *info)
  274 +{
  275 + static const char *const names[3] = {
  276 + "< 64 ohms", "64-150 ohms", "150-300 ohms"
  277 + };
  278 +
  279 + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
  280 + info->count = 1;
  281 + info->value.enumerated.items = 3;
  282 + if (info->value.enumerated.item >= 3)
  283 + info->value.enumerated.item = 2;
  284 + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
  285 + return 0;
  286 +}
  287 +
  288 +static int hp_volume_offset_get(struct snd_kcontrol *ctl,
  289 + struct snd_ctl_elem_value *value)
  290 +{
  291 + struct oxygen *chip = ctl->private_data;
  292 + struct dg *data = chip->model_data;
  293 +
  294 + mutex_lock(&chip->mutex);
  295 + if (data->hp_vol_att > 2 * 7)
  296 + value->value.enumerated.item[0] = 0;
  297 + else if (data->hp_vol_att > 0)
  298 + value->value.enumerated.item[0] = 1;
  299 + else
  300 + value->value.enumerated.item[0] = 2;
  301 + mutex_unlock(&chip->mutex);
  302 + return 0;
  303 +}
  304 +
  305 +static int hp_volume_offset_put(struct snd_kcontrol *ctl,
  306 + struct snd_ctl_elem_value *value)
  307 +{
  308 + static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
  309 + struct oxygen *chip = ctl->private_data;
  310 + struct dg *data = chip->model_data;
  311 + s8 att;
  312 + int changed;
  313 +
  314 + if (value->value.enumerated.item[0] > 2)
  315 + return -EINVAL;
  316 + att = atts[value->value.enumerated.item[0]];
  317 + mutex_lock(&chip->mutex);
  318 + changed = att != data->hp_vol_att;
  319 + if (changed) {
  320 + data->hp_vol_att = att;
  321 + if (data->output_sel) {
  322 + cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
  323 + cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
  324 + }
  325 + }
  326 + mutex_unlock(&chip->mutex);
  327 + return changed;
  328 +}
  329 +
  330 +static int input_vol_info(struct snd_kcontrol *ctl,
  331 + struct snd_ctl_elem_info *info)
  332 +{
  333 + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  334 + info->count = 2;
  335 + info->value.integer.min = 2 * -12;
  336 + info->value.integer.max = 2 * 12;
  337 + return 0;
  338 +}
  339 +
  340 +static int input_vol_get(struct snd_kcontrol *ctl,
  341 + struct snd_ctl_elem_value *value)
  342 +{
  343 + struct oxygen *chip = ctl->private_data;
  344 + struct dg *data = chip->model_data;
  345 + unsigned int idx = ctl->private_value;
  346 +
  347 + mutex_lock(&chip->mutex);
  348 + value->value.integer.value[0] = data->input_vol[idx][0];
  349 + value->value.integer.value[1] = data->input_vol[idx][1];
  350 + mutex_unlock(&chip->mutex);
  351 + return 0;
  352 +}
  353 +
  354 +static int input_vol_put(struct snd_kcontrol *ctl,
  355 + struct snd_ctl_elem_value *value)
  356 +{
  357 + struct oxygen *chip = ctl->private_data;
  358 + struct dg *data = chip->model_data;
  359 + unsigned int idx = ctl->private_value;
  360 + int changed = 0;
  361 +
  362 + if (value->value.integer.value[0] < 2 * -12 ||
  363 + value->value.integer.value[0] > 2 * 12 ||
  364 + value->value.integer.value[1] < 2 * -12 ||
  365 + value->value.integer.value[1] > 2 * 12)
  366 + return -EINVAL;
  367 + mutex_lock(&chip->mutex);
  368 + changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
  369 + data->input_vol[idx][1] != value->value.integer.value[1];
  370 + if (changed) {
  371 + data->input_vol[idx][0] = value->value.integer.value[0];
  372 + data->input_vol[idx][1] = value->value.integer.value[1];
  373 + if (idx == data->input_sel) {
  374 + cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
  375 + data->input_vol[idx][0]);
  376 + cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
  377 + data->input_vol[idx][1]);
  378 + }
  379 + }
  380 + mutex_unlock(&chip->mutex);
  381 + return changed;
  382 +}
  383 +
  384 +static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
  385 +
  386 +static int input_sel_info(struct snd_kcontrol *ctl,
  387 + struct snd_ctl_elem_info *info)
  388 +{
  389 + static const char *const names[4] = {
  390 + "Mic", "Aux", "Front Mic", "Line"
  391 + };
  392 +
  393 + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
  394 + info->count = 1;
  395 + info->value.enumerated.items = 4;
  396 + info->value.enumerated.item &= 3;
  397 + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
  398 + return 0;
  399 +}
  400 +
  401 +static int input_sel_get(struct snd_kcontrol *ctl,
  402 + struct snd_ctl_elem_value *value)
  403 +{
  404 + struct oxygen *chip = ctl->private_data;
  405 + struct dg *data = chip->model_data;
  406 +
  407 + mutex_lock(&chip->mutex);
  408 + value->value.enumerated.item[0] = data->input_sel;
  409 + mutex_unlock(&chip->mutex);
  410 + return 0;
  411 +}
  412 +
  413 +static int input_sel_put(struct snd_kcontrol *ctl,
  414 + struct snd_ctl_elem_value *value)
  415 +{
  416 + static const u8 sel_values[4] = {
  417 + CS4245_SEL_MIC,
  418 + CS4245_SEL_INPUT_1,
  419 + CS4245_SEL_INPUT_2,
  420 + CS4245_SEL_INPUT_4
  421 + };
  422 + struct oxygen *chip = ctl->private_data;
  423 + struct dg *data = chip->model_data;
  424 + int changed;
  425 +
  426 + if (value->value.enumerated.item[0] > 3)
  427 + return -EINVAL;
  428 +
  429 + mutex_lock(&chip->mutex);
  430 + changed = value->value.enumerated.item[0] != data->input_sel;
  431 + if (changed) {
  432 + data->input_sel = value->value.enumerated.item[0];
  433 +
  434 + cs4245_write(chip, CS4245_ANALOG_IN,
  435 + (data->cs4245_regs[CS4245_ANALOG_IN] &
  436 + ~CS4245_SEL_MASK) |
  437 + sel_values[data->input_sel]);
  438 +
  439 + cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
  440 + data->input_vol[data->input_sel][0]);
  441 + cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
  442 + data->input_vol[data->input_sel][1]);
  443 +
  444 + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
  445 + data->input_sel ? 0 : GPIO_INPUT_ROUTE,
  446 + GPIO_INPUT_ROUTE);
  447 + }
  448 + mutex_unlock(&chip->mutex);
  449 + return changed;
  450 +}
  451 +
  452 +static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
  453 +{
  454 + static const char *const names[2] = { "Active", "Frozen" };
  455 +
  456 + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
  457 + info->count = 1;
  458 + info->value.enumerated.items = 2;
  459 + info->value.enumerated.item &= 1;
  460 + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
  461 + return 0;
  462 +}
  463 +
  464 +static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
  465 +{
  466 + struct oxygen *chip = ctl->private_data;
  467 + struct dg *data = chip->model_data;
  468 +
  469 + value->value.enumerated.item[0] =
  470 + !!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
  471 + return 0;
  472 +}
  473 +
  474 +static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
  475 +{
  476 + struct oxygen *chip = ctl->private_data;
  477 + struct dg *data = chip->model_data;
  478 + u8 reg;
  479 + int changed;
  480 +
  481 + mutex_lock(&chip->mutex);
  482 + reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
  483 + if (value->value.enumerated.item[0])
  484 + reg |= CS4245_HPF_FREEZE;
  485 + changed = reg != data->cs4245_regs[CS4245_ADC_CTRL];
  486 + if (changed)
  487 + cs4245_write(chip, CS4245_ADC_CTRL, reg);
  488 + mutex_unlock(&chip->mutex);
  489 + return changed;
  490 +}
  491 +
  492 +#define INPUT_VOLUME(xname, index) { \
  493 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
  494 + .name = xname, \
  495 + .info = input_vol_info, \
  496 + .get = input_vol_get, \
  497 + .put = input_vol_put, \
  498 + .tlv = { .p = cs4245_pga_db_scale }, \
  499 + .private_value = index, \
  500 +}
  501 +static const struct snd_kcontrol_new dg_controls[] = {
  502 + {
  503 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  504 + .name = "Analog Output Playback Enum",
  505 + .info = output_switch_info,
  506 + .get = output_switch_get,
  507 + .put = output_switch_put,
  508 + },
  509 + {
  510 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  511 + .name = "Headphones Impedance Playback Enum",
  512 + .info = hp_volume_offset_info,
  513 + .get = hp_volume_offset_get,
  514 + .put = hp_volume_offset_put,
  515 + },
  516 + INPUT_VOLUME("Mic Capture Volume", 0),
  517 + INPUT_VOLUME("Aux Capture Volume", 1),
  518 + INPUT_VOLUME("Front Mic Capture Volume", 2),
  519 + INPUT_VOLUME("Line Capture Volume", 3),
  520 + {
  521 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  522 + .name = "Capture Source",
  523 + .info = input_sel_info,
  524 + .get = input_sel_get,
  525 + .put = input_sel_put,
  526 + },
  527 + {
  528 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  529 + .name = "ADC High-pass Filter Capture Enum",
  530 + .info = hpf_info,
  531 + .get = hpf_get,
  532 + .put = hpf_put,
  533 + },
  534 +};
  535 +
  536 +static int dg_control_filter(struct snd_kcontrol_new *template)
  537 +{
  538 + if (!strncmp(template->name, "Master Playback ", 16))
  539 + return 1;
  540 + return 0;
  541 +}
  542 +
  543 +static int dg_mixer_init(struct oxygen *chip)
  544 +{
  545 + unsigned int i;
  546 + int err;
  547 +
  548 + for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
  549 + err = snd_ctl_add(chip->card,
  550 + snd_ctl_new1(&dg_controls[i], chip));
  551 + if (err < 0)
  552 + return err;
  553 + }
  554 + return 0;
  555 +}
  556 +
  557 +static void dump_cs4245_registers(struct oxygen *chip,
  558 + struct snd_info_buffer *buffer)
  559 +{
  560 + struct dg *data = chip->model_data;
  561 + unsigned int i;
  562 +
  563 + snd_iprintf(buffer, "\nCS4245:");
  564 + for (i = 1; i <= 0x10; ++i)
  565 + snd_iprintf(buffer, " %02x", data->cs4245_regs[i]);
  566 + snd_iprintf(buffer, "\n");
  567 +}
  568 +
  569 +struct oxygen_model model_xonar_dg = {
  570 + .shortname = "Xonar DG",
  571 + .longname = "C-Media Oxygen HD Audio",
  572 + .chip = "CMI8786",
  573 + .init = dg_init,
  574 + .control_filter = dg_control_filter,
  575 + .mixer_init = dg_mixer_init,
  576 + .cleanup = dg_cleanup,
  577 + .suspend = dg_suspend,
  578 + .resume = dg_resume,
  579 + .set_dac_params = set_cs4245_dac_params,
  580 + .set_adc_params = set_cs4245_adc_params,
  581 + .dump_registers = dump_cs4245_registers,
  582 + .model_data_size = sizeof(struct dg),
  583 + .device_config = PLAYBACK_0_TO_I2S |
  584 + PLAYBACK_1_TO_SPDIF |
  585 + CAPTURE_0_FROM_I2S_1,
  586 + .dac_channels_pcm = 6,
  587 + .dac_channels_mixer = 0,
  588 + .function_flags = OXYGEN_FUNCTION_SPI,
  589 + .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
  590 + .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
  591 + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
  592 + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
  593 +};
sound/pci/oxygen/xonar_dg.h
  1 +#ifndef XONAR_DG_H_INCLUDED
  2 +#define XONAR_DG_H_INCLUDED
  3 +
  4 +#include "oxygen.h"
  5 +
  6 +extern struct oxygen_model model_xonar_dg;
  7 +
  8 +#endif